mirror of
				git://sourceware.org/git/lvm2.git
				synced 2025-10-30 20:23:49 +03:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
			dev-mcsont
			...
			dev-dct-cm
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | bfcc1f0ccc | ||
|  | 27e2902672 | ||
|  | a8c3979252 | ||
|  | 94fab0ec87 | ||
|  | de81dcabd0 | ||
|  | 6f37cba3e7 | ||
|  | 3b7a6d1c40 | ||
|  | 75732b65a4 | ||
|  | a4d1ceb6d4 | ||
|  | b5e373866d | ||
|  | d81e37acf5 | ||
|  | 516068b8cc | ||
|  | 95e00d2043 | 
| @@ -59,8 +59,6 @@ liblvm: lib | ||||
| daemons: lib libdaemon tools | ||||
| tools: lib libdaemon device-mapper | ||||
| po: tools daemons | ||||
| man: tools | ||||
| all_man: tools | ||||
| scripts: liblvm libdm | ||||
|  | ||||
| lib.device-mapper: include.device-mapper | ||||
| @@ -100,7 +98,7 @@ 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: all | ||||
| 	$(MAKE) -C test $(@) | ||||
|  | ||||
| conf.generate man.generate: tools | ||||
| conf.generate: tools | ||||
|  | ||||
| # how to use parenthesis in makefiles | ||||
| leftparen:=( | ||||
| @@ -130,9 +128,8 @@ rpm: dist | ||||
| 	    $(top_srcdir)/spec/source.inc >$(rpmbuilddir)/SOURCES/source.inc | ||||
| 	rpmbuild -v --define "_topdir $(rpmbuilddir)" -ba $(top_srcdir)/spec/lvm2.spec | ||||
|  | ||||
| generate: conf.generate man.generate | ||||
| generate: conf.generate | ||||
| 	$(MAKE) -C conf generate | ||||
| 	$(MAKE) -C man generate | ||||
|  | ||||
| all_man: | ||||
| 	$(MAKE) -C man all_man | ||||
|   | ||||
							
								
								
									
										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.146-git (2017-11-03) | ||||
| 1.02.138-git (2016-11-30) | ||||
|   | ||||
							
								
								
									
										239
									
								
								WHATS_NEW
									
									
									
									
									
								
							
							
						
						
									
										239
									
								
								WHATS_NEW
									
									
									
									
									
								
							| @@ -1,240 +1,5 @@ | ||||
| Version 2.02.177 - | ||||
| ==================================== | ||||
|   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. | ||||
| Version 2.02.169 -  | ||||
| ===================================== | ||||
|   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. | ||||
|   | ||||
							
								
								
									
										83
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							
							
						
						
									
										83
									
								
								WHATS_NEW_DM
									
									
									
									
									
								
							| @@ -1,82 +1,13 @@ | ||||
| Version 1.02.146 - | ||||
| ==================================== | ||||
|   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. | ||||
| Version 1.02.138 -  | ||||
| ===================================== | ||||
|   Add dm_stats_bind_from_fd() to bind a stats handle from a file descriptor. | ||||
|   Do not try call callback when reverting activation on error path. | ||||
|   Fix file mapping for extents with physically adjacent extents in dmstats. | ||||
|   Fix file mapping for extents with physically adjacent extents. | ||||
|   Validation vsnprintf result in runtime translate of dm_log (1.02.136). | ||||
|   Separate filemap extent allocation from region table in dmstats. | ||||
|   Fix segmentation fault when filemap region creation fails in dmstats. | ||||
|   Fix performance of region cleanup for failed filemap creation in dmstats. | ||||
|   Fix very slow region deletion with many regions in dmstats. | ||||
|   Separate filemap extent allocation from region table. | ||||
|   Fix segmentation fault when filemap region creation fails. | ||||
|   Fix performance of region cleanup for failed filemap creation. | ||||
|   Fix very slow region deletion with many regions. | ||||
|  | ||||
| Version 1.02.137 - 30th November 2016 | ||||
| ===================================== | ||||
|   | ||||
| @@ -32,8 +32,8 @@ 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 +48,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 | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										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. | ||||
|  | ||||
| @@ -379,9 +379,8 @@ allocation { | ||||
|  | ||||
| 	# Configuration option allocation/raid_stripe_all_devices. | ||||
| 	# Stripe across all PVs when RAID stripes are not specified. | ||||
| 	# If enabled, all PVs in the VG or on the command line are used for | ||||
| 	# raid0/4/5/6/10 when the command does not specify the number of | ||||
| 	# stripes to use. | ||||
| 	# If enabled, all PVs in the VG or on the command line are used for raid0/4/5/6/10 | ||||
| 	# when the command does not specify the number of stripes to use. | ||||
| 	# This was the default behaviour until release 2.02.162. | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# raid_stripe_all_devices = 0 | ||||
| @@ -390,17 +389,6 @@ allocation { | ||||
| 	# Cache pool metadata and data will always use different PVs. | ||||
| 	cache_pool_metadata_require_separate_pvs = 0 | ||||
|  | ||||
| 	# Configuration option allocation/cache_metadata_format. | ||||
| 	# Sets default metadata format for new cache. | ||||
| 	#  | ||||
| 	# Accepted values: | ||||
| 	#   0  Automatically detected best available format | ||||
| 	#   1  Original format | ||||
| 	#   2  Improved 2nd. generation format | ||||
| 	#  | ||||
| 	# This configuration option has an automatic default value. | ||||
| 	# cache_metadata_format = 0 | ||||
|  | ||||
| 	# Configuration option allocation/cache_mode. | ||||
| 	# The default cache mode used for new cache. | ||||
| 	#  | ||||
| @@ -417,7 +405,7 @@ allocation { | ||||
|  | ||||
| 	# Configuration option allocation/cache_policy. | ||||
| 	# The default cache policy used for new cache volume. | ||||
| 	# Since kernel 4.2 the default policy is smq (Stochastic multiqueue), | ||||
| 	# Since kernel 4.2 the default policy is smq (Stochastic multique), | ||||
| 	# otherwise the older mq (Multiqueue) policy is selected. | ||||
| 	# This configuration option does not have a default value defined. | ||||
|  | ||||
| @@ -611,9 +599,9 @@ log { | ||||
| 	# Select log messages by class. | ||||
| 	# Some debugging messages are assigned to a class and only appear in | ||||
| 	# debug output if the class is listed here. Classes currently | ||||
| 	# available: memory, devices, io, activation, allocation, 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", "dbus" ] | ||||
| } | ||||
|  | ||||
| # Configuration section backup. | ||||
| @@ -677,7 +665,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. | ||||
| @@ -940,7 +928,7 @@ global { | ||||
| 	use_lvmetad = @DEFAULT_USE_LVMETAD@ | ||||
|  | ||||
| 	# Configuration option global/lvmetad_update_wait_time. | ||||
| 	# Number of seconds a command will wait for lvmetad update to finish. | ||||
| 	# The number of seconds a command will wait for lvmetad update to finish. | ||||
| 	# After waiting for this period, a command will not use lvmetad, and | ||||
| 	# will revert to disk scanning. | ||||
| 	# This configuration option has an automatic default value. | ||||
| @@ -1025,7 +1013,7 @@ global { | ||||
| 	# Configuration option global/cache_disabled_features. | ||||
| 	# Features to not use in the cache driver. | ||||
| 	# This can be helpful for testing, or to avoid using a feature that is | ||||
| 	# causing problems. Features include: policy_mq, policy_smq, metadata2. | ||||
| 	# causing problems. Features include: policy_mq, policy_smq. | ||||
| 	#  | ||||
| 	# Example | ||||
| 	# cache_disabled_features = [ "policy_smq" ] | ||||
| @@ -1070,12 +1058,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, | ||||
| @@ -1174,8 +1156,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. | ||||
| @@ -1295,10 +1276,9 @@ activation { | ||||
|  | ||||
| 	# Configuration option activation/raid_region_size. | ||||
| 	# Size in KiB of each raid or mirror synchronization region. | ||||
| 	# The clean/dirty state of data is tracked for each region. | ||||
| 	# The value is rounded down to a power of two if necessary, and | ||||
| 	# is ignored if it is not a multiple of the machine memory page size. | ||||
| 	raid_region_size = 2048 | ||||
| 	# For raid or mirror segment types, this is the amount of data that is | ||||
| 	# copied at once when initializing, or moved at once by pvmove. | ||||
| 	raid_region_size = 512 | ||||
|  | ||||
| 	# Configuration option activation/error_when_full. | ||||
| 	# Return errors if a thin pool runs out of space. | ||||
| @@ -2068,15 +2048,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. | ||||
|   | ||||
							
								
								
									
										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. | ||||
|  | ||||
							
								
								
									
										201
									
								
								configure.in
									
									
									
									
									
								
							
							
						
						
									
										201
									
								
								configure.in
									
									
									
									
									
								
							| @@ -15,7 +15,6 @@ AC_PREREQ(2.69) | ||||
| ################################################################################ | ||||
| dnl -- Process this file with autoconf to produce a configure script. | ||||
| AC_INIT | ||||
| CONFIGURE_LINE="$0 $@" | ||||
| AC_CONFIG_SRCDIR([lib/device/dev-cache.h]) | ||||
| AC_CONFIG_HEADERS([include/configure.h]) | ||||
|  | ||||
| @@ -31,7 +30,6 @@ AS_IF([test -z "$CFLAGS"], [COPTIMISE_FLAG="-O2"]) | ||||
| case "$host_os" in | ||||
| 	linux*) | ||||
| 		CLDFLAGS="$CLDFLAGS -Wl,--version-script,.export.sym" | ||||
| 		# equivalent to -rdynamic | ||||
| 		ELDFLAGS="-Wl,--export-dynamic" | ||||
| 		# FIXME Generate list and use --dynamic-list=.dlopen.sym | ||||
| 		CLDWHOLEARCHIVE="-Wl,-whole-archive" | ||||
| @@ -85,12 +83,9 @@ AC_PROG_LN_S | ||||
| AC_PROG_MAKE_SET | ||||
| AC_PROG_MKDIR_P | ||||
| AC_PROG_RANLIB | ||||
| AC_CHECK_TOOL(AR, ar) | ||||
| AC_PATH_TOOL(CFLOW_CMD, cflow) | ||||
| AC_PATH_TOOL(CSCOPE_CMD, cscope) | ||||
| AC_PATH_TOOL(CHMOD, chmod) | ||||
| AC_PATH_TOOL(WC, wc) | ||||
| AC_PATH_TOOL(SORT, sort) | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Check for header files. | ||||
| @@ -108,7 +103,7 @@ AC_CHECK_HEADERS([assert.h ctype.h dirent.h errno.h fcntl.h float.h \ | ||||
|   sys/time.h sys/types.h sys/utsname.h sys/wait.h time.h \ | ||||
|   unistd.h], , [AC_MSG_ERROR(bailing out)]) | ||||
|  | ||||
| AC_CHECK_HEADERS(termios.h sys/statvfs.h sys/timerfd.h sys/vfs.h linux/magic.h linux/fiemap.h) | ||||
| AC_CHECK_HEADERS(termios.h sys/statvfs.h sys/timerfd.h linux/magic.h linux/fiemap.h) | ||||
|  | ||||
| case "$host_os" in | ||||
| 	linux*) | ||||
| @@ -123,7 +118,6 @@ AC_C_CONST | ||||
| AC_C_INLINE | ||||
| AC_CHECK_MEMBERS([struct stat.st_rdev]) | ||||
| AC_CHECK_TYPES([ptrdiff_t]) | ||||
| AC_STRUCT_ST_BLOCKS | ||||
| AC_STRUCT_TM | ||||
| AC_TYPE_OFF_T | ||||
| AC_TYPE_PID_T | ||||
| @@ -191,15 +185,9 @@ AC_SUBST(HAVE_FULL_RELRO) | ||||
| ################################################################################ | ||||
| dnl -- Prefix is /usr by default, the exec_prefix default is setup later | ||||
| AC_PREFIX_DEFAULT(/usr) | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Clear default exec_prefix - install into /sbin rather than /usr/sbin | ||||
| test "$exec_prefix" = NONE -a "$prefix" = NONE && exec_prefix="" | ||||
|  | ||||
| test "x$prefix" = xNONE && prefix=$ac_default_prefix | ||||
| # Let make expand exec_prefix. | ||||
| test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' | ||||
|  | ||||
| if test "$prefix" = NONE; then | ||||
|   datarootdir=${ac_default_prefix}/share | ||||
| fi | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Setup the ownership of the files | ||||
| @@ -414,6 +402,22 @@ AC_DEFINE_UNQUOTED([DEFAULT_RAID10_SEGTYPE], ["$DEFAULT_RAID10_SEGTYPE"], | ||||
| 		   [Default segtype used for raid10 volumes.]) | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- asynchronous volume replicator inclusion type | ||||
| AC_MSG_CHECKING(whether to include replicators) | ||||
| AC_ARG_WITH(replicators, | ||||
| 	    AC_HELP_STRING([--with-replicators=TYPE], | ||||
| 			   [replicator support: internal/shared/none [none]]), | ||||
| 	    REPLICATORS=$withval, REPLICATORS=none) | ||||
| AC_MSG_RESULT($REPLICATORS) | ||||
|  | ||||
| case "$REPLICATORS" in | ||||
|   none|shared) ;; | ||||
|   internal) AC_DEFINE([REPLICATOR_INTERNAL], 1, | ||||
| 		[Define to 1 to include built-in support for replicators.]) ;; | ||||
|   *) AC_MSG_ERROR([--with-replicators parameter invalid ($REPLICATORS)]) ;; | ||||
| esac | ||||
|  | ||||
|  | ||||
| AC_ARG_WITH(default-sparse-segtype, | ||||
| 	    AC_HELP_STRING([--with-default-sparse-segtype=TYPE], | ||||
| 			   [default sparse segtype: thin/snapshot [thin]]), | ||||
| @@ -609,10 +613,6 @@ case "$CACHE" in | ||||
| 				CACHE_CHECK_VERSION_WARN=y | ||||
| 				CACHE_CHECK_NEEDS_CHECK=no | ||||
| 			fi | ||||
| 			if test "$CACHE_CHECK_VSN_MINOR" -lt 7 ; then | ||||
| 				AC_MSG_WARN([$CACHE_CHECK_CMD: Old version "$CACHE_CHECK_VSN" does not support new cache format V2]) | ||||
| 				CACHE_CHECK_VERSION_WARN=y | ||||
| 			fi | ||||
| 		fi | ||||
| 	fi | ||||
| 	# Empty means a config way to ignore cache dumping | ||||
| @@ -666,9 +666,11 @@ AC_DEFINE_UNQUOTED([CACHE_RESTORE_CMD], ["$CACHE_RESTORE_CMD"], | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Disable readline | ||||
| AC_MSG_CHECKING(whether to enable readline) | ||||
| AC_ARG_ENABLE([readline], | ||||
| 	      AC_HELP_STRING([--disable-readline], [disable readline support]), | ||||
| 	      READLINE=$enableval, READLINE=maybe) | ||||
| AC_MSG_RESULT($READLINE) | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Disable realtime clock support | ||||
| @@ -1144,10 +1146,10 @@ AC_MSG_RESULT($BUILD_LVMPOLLD) | ||||
| ################################################################################ | ||||
| BUILD_LVMLOCKD=no | ||||
|  | ||||
| dnl -- Build lvmlockdsanlock | ||||
| AC_MSG_CHECKING(whether to build lvmlockdsanlock) | ||||
| AC_ARG_ENABLE(lvmlockd-sanlock, | ||||
| 	      AC_HELP_STRING([--enable-lvmlockd-sanlock], | ||||
| dnl -- Build lockdsanlock | ||||
| AC_MSG_CHECKING(whether to build lockdsanlock) | ||||
| AC_ARG_ENABLE(lockd-sanlock, | ||||
| 	      AC_HELP_STRING([--enable-lockd-sanlock], | ||||
| 			     [enable the LVM lock daemon using sanlock]), | ||||
| 	      LOCKDSANLOCK=$enableval) | ||||
| AC_MSG_RESULT($LOCKDSANLOCK) | ||||
| @@ -1162,10 +1164,10 @@ if test "$BUILD_LOCKDSANLOCK" = yes; then | ||||
| fi | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Build lvmlockddlm | ||||
| AC_MSG_CHECKING(whether to build lvmlockddlm) | ||||
| AC_ARG_ENABLE(lvmlockd-dlm, | ||||
| 	      AC_HELP_STRING([--enable-lvmlockd-dlm], | ||||
| dnl -- Build lockddlm | ||||
| AC_MSG_CHECKING(whether to build lockddlm) | ||||
| AC_ARG_ENABLE(lockd-dlm, | ||||
| 	      AC_HELP_STRING([--enable-lockd-dlm], | ||||
| 			     [enable the LVM lock daemon using dlm]), | ||||
| 	      LOCKDDLM=$enableval) | ||||
| AC_MSG_RESULT($LOCKDDLM) | ||||
| @@ -1267,80 +1269,75 @@ fi | ||||
| AC_DEFINE_UNQUOTED(DEFAULT_USE_LVMPOLLD, [$DEFAULT_USE_LVMPOLLD], | ||||
| 		   [Use lvmpolld by default.]) | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Check dmfilemapd | ||||
| AC_MSG_CHECKING(whether to build dmfilemapd) | ||||
| AC_ARG_ENABLE(dmfilemapd, AC_HELP_STRING([--enable-dmfilemapd], | ||||
| 					 [enable the dmstats filemap daemon]), | ||||
| 	      BUILD_DMFILEMAPD=$enableval, BUILD_DMFILEMAPD=no) | ||||
| AC_MSG_RESULT($BUILD_DMFILEMAPD) | ||||
| AC_DEFINE([DMFILEMAPD], $BUILD_DMFILEMAPD, [Define to 1 to enable the device-mapper filemap daemon.]) | ||||
|  | ||||
| dnl -- dmfilemapd requires FIEMAP | ||||
| if test "$BUILD_DMFILEMAPD" = yes; then | ||||
|    AC_CHECK_HEADER([linux/fiemap.h], , [AC_MSG_ERROR(--enable-dmfilemapd requires fiemap.h)]) | ||||
| fi | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Build notifydbus | ||||
| AC_MSG_CHECKING(whether to build notifydbus) | ||||
| AC_ARG_ENABLE(notify-dbus, | ||||
| 	      AC_HELP_STRING([--enable-notify-dbus], | ||||
| 			     [enable LVM notification using dbus]), | ||||
| 	      NOTIFYDBUS_SUPPORT=$enableval, NOTIFYDBUS_SUPPORT=no) | ||||
| AC_MSG_RESULT($NOTIFYDBUS_SUPPORT) | ||||
| 	      NOTIFYDBUS=$enableval) | ||||
| AC_MSG_RESULT($NOTIFYDBUS) | ||||
|  | ||||
| if test "$NOTIFYDBUS_SUPPORT" = yes; then | ||||
| BUILD_NOTIFYDBUS=$NOTIFYDBUS | ||||
|  | ||||
| if test "$BUILD_NOTIFYDBUS" = yes; then | ||||
| 	AC_DEFINE([NOTIFYDBUS_SUPPORT], 1, [Define to 1 to include code that uses dbus notification.]) | ||||
| 	SYSTEMD_LIBS="-lsystemd" | ||||
| 	LIBS="-lsystemd $LIBS" | ||||
| fi | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Look for dbus libraries | ||||
| if test "$NOTIFYDBUS_SUPPORT" = yes; then | ||||
| if test "$BUILD_NOTIFYDBUS" = yes; then | ||||
| 	PKG_CHECK_MODULES(NOTIFY_DBUS, systemd >= 221, [HAVE_NOTIFY_DBUS=yes], $bailout) | ||||
| fi | ||||
|  | ||||
| ################################################################################ | ||||
|  | ||||
| dnl -- Enable blkid wiping functionality | ||||
| AC_MSG_CHECKING(whether to enable libblkid detection of signatures when wiping) | ||||
| AC_ARG_ENABLE(blkid_wiping, | ||||
| 	      AC_HELP_STRING([--disable-blkid_wiping], | ||||
| 			     [disable libblkid detection of signatures when wiping and use native code instead]), | ||||
| 	      BLKID_WIPING=$enableval, BLKID_WIPING=maybe) | ||||
| AC_MSG_RESULT($BLKID_WIPING) | ||||
|  | ||||
| DEFAULT_USE_BLKID_WIPING=0 | ||||
| if test "$BLKID_WIPING" != no; then | ||||
| 	pkg_config_init | ||||
| 	PKG_CHECK_MODULES(BLKID, blkid >= 2.24, | ||||
| 			  [ BLKID_WIPING=yes | ||||
| 			    BLKID_PC="blkid" | ||||
| 			    DEFAULT_USE_BLKID_WIPING=1 | ||||
| 			    AC_DEFINE([BLKID_WIPING_SUPPORT], 1, [Define to 1 to use libblkid detection of signatures when wiping.]) | ||||
| 			  ], [if test "$BLKID_WIPING" = maybe; then | ||||
| 			  [test "$BLKID_WIPING" = maybe && BLKID_WIPING=yes], | ||||
| 			  [if test "$BLKID_WIPING" = maybe; then | ||||
| 				BLKID_WIPING=no | ||||
| 			   else | ||||
| 			        AC_MSG_ERROR([bailing out... blkid library >= 2.24 is required]) | ||||
| 			   fi]) | ||||
| 	if test "$BLKID_WIPING" = yes; then | ||||
| 		BLKID_PC="blkid" | ||||
| 		DEFAULT_USE_BLKID_WIPING=1 | ||||
| 		AC_DEFINE([BLKID_WIPING_SUPPORT], 1, [Define to 1 to use libblkid detection of signatures when wiping.]) | ||||
| 	else | ||||
| 		DEFAULT_USE_BLKID_WIPING=0 | ||||
| 	fi | ||||
| else | ||||
| 	DEFAULT_USE_BLKID_WIPING=0 | ||||
| fi | ||||
| AC_MSG_CHECKING([whether to enable libblkid detection of signatures when wiping]) | ||||
| AC_MSG_RESULT($BLKID_WIPING) | ||||
| AC_DEFINE_UNQUOTED(DEFAULT_USE_BLKID_WIPING, [$DEFAULT_USE_BLKID_WIPING], | ||||
| 		   [Use blkid wiping by default.]) | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Enable udev-systemd protocol to instantiate a service for background jobs | ||||
| dnl -- Requires systemd version 205 at least (including support for systemd-run) | ||||
| AC_MSG_CHECKING(whether to use udev-systemd protocol for jobs in background) | ||||
| AC_ARG_ENABLE(udev-systemd-background-jobs, | ||||
| 	      AC_HELP_STRING([--disable-udev-systemd-background-jobs], | ||||
| 			     [disable udev-systemd protocol to instantiate a service for background job]), | ||||
| 	      UDEV_SYSTEMD_BACKGROUND_JOBS=$enableval, | ||||
| 	      UDEV_SYSTEMD_BACKGROUND_JOBS=maybe) | ||||
| AC_MSG_RESULT($UDEV_SYSTEMD_BACKGROUND_JOBS) | ||||
|  | ||||
| if test "$UDEV_SYSTEMD_BACKGROUND_JOBS" != no; then | ||||
| 	pkg_config_init | ||||
| 	PKG_CHECK_MODULES(SYSTEMD, systemd >= 205, | ||||
| 			  [UDEV_SYSTEMD_BACKGROUND_JOBS=yes], | ||||
| 			  [test "$UDEV_SYSTEMD_BACKGROUND_JOBS" = maybe && UDEV_SYSTEMD_BACKGROUND_JOBS=yes], | ||||
| 			  [if test "$UDEV_SYSTEMD_BACKGROUND_JOBS" = maybe; then | ||||
| 				UDEV_SYSTEMD_BACKGROUND_JOBS=no | ||||
| 			   else | ||||
| @@ -1348,9 +1345,6 @@ if test "$UDEV_SYSTEMD_BACKGROUND_JOBS" != no; then | ||||
| 			   fi]) | ||||
| fi | ||||
|  | ||||
| AC_MSG_CHECKING(whether to use udev-systemd protocol for jobs in background) | ||||
| AC_MSG_RESULT($UDEV_SYSTEMD_BACKGROUND_JOBS) | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Enable udev synchronisation | ||||
| AC_MSG_CHECKING(whether to enable synchronisation with udev processing) | ||||
| @@ -1454,8 +1448,6 @@ AC_SUBST([LVM2APP_LIB]) | ||||
| test "$APPLIB" = yes \ | ||||
|   && LVM2APP_LIB=-llvm2app \ | ||||
|   || LVM2APP_LIB= | ||||
| AS_IF([test "$APPLIB"], | ||||
|       [AC_MSG_WARN([liblvm2app is deprecated. Use D-Bus API])]) | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Enable cmdlib | ||||
| @@ -1476,8 +1468,6 @@ AC_ARG_ENABLE(dbus-service, | ||||
| 	      AC_HELP_STRING([--enable-dbus-service], [install D-Bus support]), | ||||
| 	      BUILD_LVMDBUSD=$enableval, BUILD_LVMDBUSD=no) | ||||
| AC_MSG_RESULT($BUILD_LVMDBUSD) | ||||
| AS_IF([test "$NOTIFYDBUS_SUPPORT" = yes && test "BUILD_LVMDBUSD" = yes], | ||||
|       [AC_MSG_WARN([Building D-Bus support without D-Bus notifications.])]) | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Enable Python liblvm2app bindings | ||||
| @@ -1530,7 +1520,7 @@ if test "$PYTHON3_BINDINGS" = yes -o "$BUILD_LVMDBUSD" = yes; then | ||||
| 	PYTHON3_INCDIRS=`"$PYTHON3_CONFIG" --includes` | ||||
| 	PYTHON3_LIBDIRS=`"$PYTHON3_CONFIG" --libs` | ||||
| 	PYTHON3DIR=$pythondir | ||||
| 	test "$PYTHON3_BINDINGS" = yes && PYTHON_BINDINGS=yes | ||||
| 	PYTHON_BINDINGS=yes | ||||
| fi | ||||
|  | ||||
| if test "$BUILD_LVMDBUSD" = yes; then | ||||
| @@ -1540,7 +1530,6 @@ if test "$BUILD_LVMDBUSD" = yes; then | ||||
| fi | ||||
|  | ||||
| if test "$PYTHON_BINDINGS" = yes -o "$PYTHON2_BINDINGS" = yes -o "$PYTHON3_BINDINGS" = yes; then | ||||
| 	AC_MSG_WARN([Python bindings are deprecated. Use D-Bus API]) | ||||
| 	test "$APPLIB" != yes && AC_MSG_ERROR([Python_bindings require --enable-applib]) | ||||
| fi | ||||
|  | ||||
| @@ -1576,11 +1565,13 @@ dnl -- enable dmeventd handling | ||||
| AC_MSG_CHECKING(whether to use dmeventd) | ||||
| AC_ARG_ENABLE(dmeventd, AC_HELP_STRING([--enable-dmeventd], | ||||
| 				       [enable the device-mapper event daemon]), | ||||
| 	      BUILD_DMEVENTD=$enableval, BUILD_DMEVENTD=no) | ||||
| AC_MSG_RESULT($BUILD_DMEVENTD) | ||||
| 	      DMEVENTD=$enableval) | ||||
| AC_MSG_RESULT($DMEVENTD) | ||||
|  | ||||
| BUILD_DMEVENTD=$DMEVENTD | ||||
|  | ||||
| dnl -- dmeventd currently requires internal mirror support | ||||
| if test "$BUILD_DMEVENTD" = yes; then | ||||
| if test "$DMEVENTD" = yes; then | ||||
|    if test "$MIRRORS" != internal; then | ||||
|       AC_MSG_ERROR([--enable-dmeventd currently requires --with-mirrors=internal]) | ||||
|    fi | ||||
| @@ -1604,6 +1595,10 @@ AC_CHECK_LIB(c, canonicalize_file_name, | ||||
|   AC_DEFINE([HAVE_CANONICALIZE_FILE_NAME], 1, | ||||
|     [Define to 1 if canonicalize_file_name is available.])) | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Clear default exec_prefix - install into /sbin rather than /usr/sbin | ||||
| test "$exec_prefix" = NONE -a "$prefix" = NONE && exec_prefix="" | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Check for dlopen | ||||
| AC_CHECK_LIB(dl, dlopen, | ||||
| @@ -1660,16 +1655,13 @@ fi | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- Check for realtime clock support | ||||
| RT_LIBS= | ||||
| HAVE_REALTIME=no | ||||
| if test "$REALTIME" = yes; then | ||||
| 	AC_CHECK_FUNCS([clock_gettime], HAVE_REALTIME=yes) | ||||
|  | ||||
| 	AS_IF([test "$HAVE_REALTIME" != yes], [ # try again with -lrt | ||||
| 	      AC_CHECK_LIB([rt], [clock_gettime], RT_LIBS="-lrt"; HAVE_REALTIME=yes)]) | ||||
| 	AC_CHECK_LIB(rt, clock_gettime, HAVE_REALTIME=yes, HAVE_REALTIME=no) | ||||
|  | ||||
| 	if test "$HAVE_REALTIME" = yes; then | ||||
| 		AC_DEFINE([HAVE_REALTIME], 1, [Define to 1 to include support for realtime clock.]) | ||||
| 		LIBS="-lrt $LIBS" | ||||
| 		RT_LIB="-lrt" | ||||
| 	else | ||||
| 		AC_MSG_WARN(Disabling realtime clock) | ||||
| 	fi | ||||
| @@ -1713,7 +1705,6 @@ Note: (n)curses also seems to work as a substitute for termcap.  This was | ||||
| 		AC_DEFINE([READLINE_SUPPORT], 1, | ||||
| 			[Define to 1 to include the LVM readline shell.]) | ||||
| 		dnl -- Try only with -lreadline and check for different symbol | ||||
| 		READLINE=yes | ||||
| 		LIBS=$lvm_saved_libs | ||||
| 		AC_CHECK_LIB([readline], [rl_line_buffer], | ||||
| 			[ READLINE_LIBS="-lreadline" ], [ | ||||
| @@ -1820,16 +1811,13 @@ dnl -- Ensure additional headers required | ||||
| if test "$READLINE" = yes; then | ||||
| 	AC_CHECK_HEADERS(readline/readline.h readline/history.h,,hard_bailout) | ||||
| fi | ||||
| AC_MSG_CHECKING(whether to enable readline) | ||||
| AC_MSG_RESULT($READLINE) | ||||
|  | ||||
| if test "$BUILD_CMIRRORD" = yes; then | ||||
| 	AC_CHECK_FUNCS(atexit,,hard_bailout) | ||||
| fi | ||||
|  | ||||
| if test "$BUILD_LVMLOCKD" = yes; then | ||||
| 	AS_IF([test "$HAVE_REALTIME" != yes], [AC_MSG_ERROR([Realtime clock support is mandatory for lvmlockd.])]) | ||||
| 	AC_CHECK_FUNCS(strtoull,,hard_bailout) | ||||
| 	AC_CHECK_FUNCS(clock_gettime strtoull,,hard_bailout) | ||||
| fi | ||||
|  | ||||
| if test "$BUILD_LVMPOLLD" = yes; then | ||||
| @@ -1849,7 +1837,7 @@ if test "$CLUSTER" != none; then | ||||
| 	AC_CHECK_FUNCS(socket,,hard_bailout) | ||||
| fi | ||||
|  | ||||
| if test "$BUILD_DMEVENTD" = yes; then | ||||
| if test "$DMEVENTD" = yes; then | ||||
| 	AC_CHECK_HEADERS(arpa/inet.h,,hard_bailout) | ||||
| fi | ||||
|  | ||||
| @@ -1865,10 +1853,6 @@ if test "$UDEV_SYNC" = yes; then | ||||
| 	AC_CHECK_HEADERS(sys/ipc.h sys/sem.h,,hard_bailout) | ||||
| fi | ||||
|  | ||||
| if test "$BUILD_DMFILEMAPD" = yes; then | ||||
| 	AC_CHECK_HEADERS([sys/inotify.h],,hard_bailout) | ||||
| fi | ||||
|  | ||||
| ################################################################################ | ||||
| AC_PATH_TOOL(MODPROBE_CMD, modprobe) | ||||
|  | ||||
| @@ -1876,19 +1860,18 @@ if test -n "$MODPROBE_CMD"; then | ||||
| 	AC_DEFINE_UNQUOTED([MODPROBE_CMD], ["$MODPROBE_CMD"], [The path to 'modprobe', if available.]) | ||||
| fi | ||||
|  | ||||
| SYSCONFDIR="$(eval echo $(eval echo $sysconfdir))" | ||||
|  | ||||
| SBINDIR="$(eval echo $(eval echo $sbindir))" | ||||
| LVM_PATH="$SBINDIR/lvm" | ||||
| lvm_exec_prefix=$exec_prefix | ||||
| test "$lvm_exec_prefix" = NONE && lvm_exec_prefix=$prefix | ||||
| test "$lvm_exec_prefix" = NONE && lvm_exec_prefix=$ac_default_prefix | ||||
| LVM_PATH="$lvm_exec_prefix/sbin/lvm" | ||||
| AC_DEFINE_UNQUOTED(LVM_PATH, ["$LVM_PATH"], [Path to lvm binary.]) | ||||
|  | ||||
| USRSBINDIR="$(eval echo $(eval echo $usrsbindir))" | ||||
| CLVMD_PATH="$USRSBINDIR/clvmd" | ||||
| clvmd_prefix=$ac_default_prefix | ||||
| test "$prefix" != NONE && clvmd_prefix=$prefix | ||||
| CLVMD_PATH="$clvmd_prefix/sbin/clvmd" | ||||
| AC_DEFINE_UNQUOTED(CLVMD_PATH, ["$CLVMD_PATH"], [Path to clvmd binary.]) | ||||
|  | ||||
| FSADM_PATH="$SBINDIR/fsadm" | ||||
| AC_DEFINE_UNQUOTED(FSADM_PATH, ["$FSADM_PATH"], [Path to fsadm binary.]) | ||||
|  | ||||
| ################################################################################ | ||||
| dnl -- dmeventd pidfile and executable path | ||||
| if test "$BUILD_DMEVENTD" = yes; then | ||||
| @@ -1906,7 +1889,7 @@ if test "$BUILD_DMEVENTD" = yes; then | ||||
| 		    AC_HELP_STRING([--with-dmeventd-path=PATH], | ||||
| 				   [dmeventd path [EPREFIX/sbin/dmeventd]]), | ||||
| 		    DMEVENTD_PATH=$withval, | ||||
| 		    DMEVENTD_PATH="$SBINDIR/dmeventd") | ||||
| 		    DMEVENTD_PATH="$lvm_exec_prefix/sbin/dmeventd") | ||||
| 	AC_DEFINE_UNQUOTED(DMEVENTD_PATH, ["$DMEVENTD_PATH"], | ||||
| 			   [Path to dmeventd binary.]) | ||||
| fi | ||||
| @@ -1949,17 +1932,13 @@ AC_ARG_WITH(default-cache-subdir, | ||||
| AC_DEFINE_UNQUOTED(DEFAULT_CACHE_SUBDIR, ["$DEFAULT_CACHE_SUBDIR"], | ||||
| 		   [Name of default metadata cache subdirectory.]) | ||||
|  | ||||
| # Select default system locking dir, prefer /run/lock over /var/lock | ||||
| DEFAULT_SYS_LOCK_DIR="$RUN_DIR/lock" | ||||
| test -d "$DEFAULT_SYS_LOCK_DIR" || DEFAULT_SYS_LOCK_DIR="/var/lock" | ||||
|  | ||||
| # Support configurable locking subdir for lvm | ||||
| AC_ARG_WITH(default-locking-dir, | ||||
| 	    AC_HELP_STRING([--with-default-locking-dir=DIR], | ||||
| 			   [default locking directory [autodetect_lock_dir/lvm]]), | ||||
| 	    DEFAULT_LOCK_DIR=$withval, | ||||
| 	    [AC_MSG_CHECKING(for default lock directory) | ||||
| 	     DEFAULT_LOCK_DIR="$DEFAULT_SYS_LOCK_DIR/lvm" | ||||
| 	     DEFAULT_LOCK_DIR="$RUN_DIR/lock/lvm" | ||||
| 	     test -d "$RUN_DIR/lock" || DEFAULT_LOCK_DIR="/var/lock/lvm" | ||||
| 	     AC_MSG_RESULT($DEFAULT_LOCK_DIR)]) | ||||
| AC_DEFINE_UNQUOTED(DEFAULT_LOCK_DIR, ["$DEFAULT_LOCK_DIR"], | ||||
| 		   [Name of default locking directory.]) | ||||
| @@ -2001,8 +1980,6 @@ LVM_MINOR=`echo "$VER" | $AWK -F '.' '{print $2}'` | ||||
| LVM_PATCHLEVEL=`echo "$VER" | $AWK -F '[[(.]]' '{print $3}'` | ||||
| LVM_LIBAPI=`echo "$VER" | $AWK -F '[[()]]' '{print $2}'` | ||||
|  | ||||
| AC_DEFINE_UNQUOTED(LVM_CONFIGURE_LINE, "$CONFIGURE_LINE", [configure command line used]) | ||||
|  | ||||
| ################################################################################ | ||||
| AC_SUBST(APPLIB) | ||||
| AC_SUBST(AWK) | ||||
| @@ -2015,7 +1992,7 @@ AC_SUBST(BUILD_LVMPOLLD) | ||||
| AC_SUBST(BUILD_LVMLOCKD) | ||||
| AC_SUBST(BUILD_LOCKDSANLOCK) | ||||
| AC_SUBST(BUILD_LOCKDDLM) | ||||
| AC_SUBST(BUILD_DMFILEMAPD) | ||||
| AC_SUBST(BUILD_NOTIFYDBUS) | ||||
| AC_SUBST(CACHE) | ||||
| AC_SUBST(CFLAGS) | ||||
| AC_SUBST(CFLOW_CMD) | ||||
| @@ -2054,7 +2031,6 @@ AC_SUBST(DEFAULT_RAID10_SEGTYPE) | ||||
| AC_SUBST(DEFAULT_RUN_DIR) | ||||
| AC_SUBST(DEFAULT_SPARSE_SEGTYPE) | ||||
| AC_SUBST(DEFAULT_SYS_DIR) | ||||
| AC_SUBST(DEFAULT_SYS_LOCK_DIR) | ||||
| AC_SUBST(DEFAULT_USE_BLKID_WIPING) | ||||
| AC_SUBST(DEFAULT_USE_LVMETAD) | ||||
| AC_SUBST(DEFAULT_USE_LVMPOLLD) | ||||
| @@ -2063,11 +2039,11 @@ AC_SUBST(DEVMAPPER) | ||||
| AC_SUBST(DLM_CFLAGS) | ||||
| AC_SUBST(DLM_LIBS) | ||||
| AC_SUBST(DL_LIBS) | ||||
| AC_SUBST(DMEVENTD) | ||||
| AC_SUBST(DMEVENTD_PATH) | ||||
| AC_SUBST(DM_LIB_PATCHLEVEL) | ||||
| AC_SUBST(ELDFLAGS) | ||||
| AC_SUBST(FSADM) | ||||
| AC_SUBST(FSADM_PATH) | ||||
| AC_SUBST(BLKDEACTIVATE) | ||||
| AC_SUBST(HAVE_LIBDL) | ||||
| AC_SUBST(HAVE_REALTIME) | ||||
| @@ -2112,18 +2088,15 @@ AC_SUBST(PYTHON3DIR) | ||||
| AC_SUBST(QUORUM_CFLAGS) | ||||
| AC_SUBST(QUORUM_LIBS) | ||||
| AC_SUBST(RAID) | ||||
| AC_SUBST(RT_LIBS) | ||||
| AC_SUBST(RT_LIB) | ||||
| AC_SUBST(READLINE_LIBS) | ||||
| AC_SUBST(REPLICATORS) | ||||
| AC_SUBST(SACKPT_CFLAGS) | ||||
| AC_SUBST(SACKPT_LIBS) | ||||
| AC_SUBST(SALCK_CFLAGS) | ||||
| AC_SUBST(SALCK_LIBS) | ||||
| AC_SUBST(SBINDIR) | ||||
| AC_SUBST(SELINUX_LIBS) | ||||
| AC_SUBST(SELINUX_PC) | ||||
| AC_SUBST(SYSCONFDIR) | ||||
| AC_SUBST(SYSTEMD_LIBS) | ||||
| AC_SUBST(SNAPSHOTS) | ||||
| AC_SUBST(STATICDIR) | ||||
| AC_SUBST(STATIC_LINK) | ||||
| @@ -2145,7 +2118,6 @@ AC_SUBST(UDEV_SYSTEMD_BACKGROUND_JOBS) | ||||
| AC_SUBST(UDEV_RULE_EXEC_DETECTION) | ||||
| AC_SUBST(UDEV_HAS_BUILTIN_BLKID) | ||||
| AC_SUBST(USE_TRACKING) | ||||
| AC_SUBST(USRSBINDIR) | ||||
| AC_SUBST(VALGRIND_POOL) | ||||
| AC_SUBST(WRITE_INSTALL) | ||||
| AC_SUBST(DMEVENTD_PIDFILE) | ||||
| @@ -2184,11 +2156,7 @@ daemons/dmeventd/plugins/raid/Makefile | ||||
| daemons/dmeventd/plugins/mirror/Makefile | ||||
| daemons/dmeventd/plugins/snapshot/Makefile | ||||
| daemons/dmeventd/plugins/thin/Makefile | ||||
| daemons/dmfilemapd/Makefile | ||||
| daemons/lvmdbusd/Makefile | ||||
| daemons/lvmdbusd/lvmdbusd | ||||
| daemons/lvmdbusd/lvmdb.py | ||||
| daemons/lvmdbusd/lvm_shell_proxy.py | ||||
| daemons/lvmdbusd/path.py | ||||
| daemons/lvmetad/Makefile | ||||
| daemons/lvmpolld/Makefile | ||||
| @@ -2205,6 +2173,7 @@ lib/format1/Makefile | ||||
| lib/format_pool/Makefile | ||||
| lib/locking/Makefile | ||||
| lib/mirror/Makefile | ||||
| lib/replicator/Makefile | ||||
| include/lvm-version.h | ||||
| lib/raid/Makefile | ||||
| lib/snapshot/Makefile | ||||
| @@ -2263,14 +2232,10 @@ AS_IF([test -n "$THIN_CONFIGURE_WARN"], | ||||
|       [AC_MSG_WARN([Support for thin provisioning is limited since some thin provisioning tools are missing!])]) | ||||
|  | ||||
| AS_IF([test -n "$THIN_CHECK_VERSION_WARN"], | ||||
|       [AC_MSG_WARN([You should also install latest thin_check vsn 0.7.0 (or later) for lvm2 thin provisioning])]) | ||||
|       [AC_MSG_WARN([You should also install thin_check vsn 0.3.2 (or later) to use lvm2 thin provisioning])]) | ||||
|  | ||||
| AS_IF([test -n "$CACHE_CONFIGURE_WARN"], | ||||
|       [AC_MSG_WARN([Support for cache is limited since some cache tools are missing!])]) | ||||
|  | ||||
| AS_IF([test -n "$CACHE_CHECK_VERSION_WARN"], | ||||
|       [AC_MSG_WARN([You should install latest cache_check vsn 0.7.0 to use lvm2 cache metadata format 2])]) | ||||
|  | ||||
|  | ||||
| AS_IF([test "$ODIRECT" != yes], | ||||
|       [AC_MSG_WARN([O_DIRECT disabled: low-memory pvmove may lock up])]) | ||||
|   | ||||
| @@ -41,19 +41,6 @@ struct lv_segment *last_seg(const struct logical_volume *lv) | ||||
| 	return ((struct lv_segment **)lv)[0]; | ||||
| } | ||||
|  | ||||
| const char *find_config_tree_str(struct cmd_context *cmd, int id, struct profile *profile) | ||||
| { | ||||
| 	return "STRING"; | ||||
| } | ||||
|  | ||||
| struct logical_volume *origin_from_cow(const struct logical_volume *lv) | ||||
| { | ||||
| 	if (lv) | ||||
| 		return lv; | ||||
|  | ||||
| 	__coverity_panic__(); | ||||
| } | ||||
|  | ||||
| /* simple_memccpy() from glibc */ | ||||
| void *memccpy(void *dest, const void *src, int c, size_t n) | ||||
| { | ||||
| @@ -84,17 +71,6 @@ void model_FD_ZERO(void *fdset) | ||||
| 		((long*)fdset)[i] = 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Resent Coverity reports quite weird errors... */ | ||||
| int *__errno_location(void) | ||||
| { | ||||
| } | ||||
| const unsigned short **__ctype_b_loc (void) | ||||
| { | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Added extra pointer check to not need these models, | ||||
|  * for now just keep then in file | ||||
|   | ||||
| @@ -48,12 +48,8 @@ ifeq ("@BUILD_LVMDBUSD@", "yes") | ||||
|   SUBDIRS += lvmdbusd | ||||
| endif | ||||
|  | ||||
| ifeq ("@BUILD_DMFILEMAPD@", "yes") | ||||
|   SUBDIRS += dmfilemapd | ||||
| endif | ||||
|  | ||||
| ifeq ($(MAKECMDGOALS),distclean) | ||||
|   SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd dmfilemapd | ||||
|   SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd | ||||
| endif | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|   | ||||
| @@ -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) | ||||
| 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 | ||||
|  | ||||
|   | ||||
| @@ -171,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; | ||||
| @@ -208,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))) | ||||
| @@ -325,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; | ||||
|  | ||||
| @@ -334,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); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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 */ | ||||
|   | ||||
| @@ -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; | ||||
| @@ -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; | ||||
| @@ -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,14 +594,14 @@ 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)) { | ||||
| @@ -675,7 +637,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 +698,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 +726,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 +750,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 +794,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 +804,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 +819,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 +853,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 +875,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 +939,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 +966,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 +1068,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 +1179,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 +1202,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 +1213,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 +1263,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 +1337,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 +1373,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 +1399,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 +1419,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 +1430,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 +1444,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 +1455,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 +1472,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 +1495,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 +1514,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 +1529,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 +1655,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 +1682,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 +1698,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 +1718,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 +1736,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 +1756,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 +1782,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 +1798,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 +1841,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 +1858,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; | ||||
| 	} | ||||
|  | ||||
| @@ -2013,7 +1987,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; | ||||
| 		} | ||||
| @@ -2027,7 +2000,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); | ||||
| @@ -2036,11 +2009,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); | ||||
| 	} | ||||
| @@ -2134,8 +2107,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); | ||||
| @@ -2151,14 +2124,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; | ||||
| @@ -2178,11 +2143,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; | ||||
| 	} | ||||
|  | ||||
| @@ -2283,8 +2244,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); | ||||
| } | ||||
|  | ||||
| @@ -2323,11 +2283,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) | ||||
|   | ||||
| @@ -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) \ | ||||
|   | ||||
| @@ -182,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; | ||||
|   | ||||
| @@ -377,7 +377,7 @@ static int _clog_ctr(char *uuid, uint64_t luid, | ||||
| 	uint32_t block_on_error = 0; | ||||
|  | ||||
| 	int disk_log; | ||||
| 	char disk_path[PATH_MAX]; | ||||
| 	char disk_path[128]; | ||||
| 	int unlink_path = 0; | ||||
| 	long page_size; | ||||
| 	int pages; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -62,8 +62,6 @@ | ||||
|  | ||||
| #include <syslog.h> | ||||
|  | ||||
| #define DM_SIGNALED_EXIT  1 | ||||
| #define DM_SCHEDULED_EXIT 2 | ||||
| static volatile sig_atomic_t _exit_now = 0;	/* set to '1' when signal is given to exit */ | ||||
|  | ||||
| /* List (un)link macros. */ | ||||
| @@ -470,7 +468,7 @@ static int _pthread_create_smallstack(pthread_t *t, void *(*fun)(void *), void * | ||||
| 	/* | ||||
| 	 * We use a smaller stack since it gets preallocated in its entirety | ||||
| 	 */ | ||||
| 	pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE + getpagesize()); | ||||
| 	pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE); | ||||
|  | ||||
| 	/* | ||||
| 	 * If no-one will be waiting, we need to detach. | ||||
| @@ -865,7 +863,6 @@ static int _event_wait(struct thread_status *thread) | ||||
| 	 * This is so that you can break out of waiting on an event, | ||||
| 	 * either for a timeout event, or to cancel the thread. | ||||
| 	 */ | ||||
| 	sigemptyset(&old); | ||||
| 	sigemptyset(&set); | ||||
| 	sigaddset(&set, SIGALRM); | ||||
| 	if (pthread_sigmask(SIG_UNBLOCK, &set, &old) != 0) { | ||||
| @@ -1753,7 +1750,7 @@ static void _init_thread_signals(void) | ||||
|  */ | ||||
| static void _exit_handler(int sig __attribute__((unused))) | ||||
| { | ||||
| 	_exit_now = DM_SIGNALED_EXIT; | ||||
| 	_exit_now = 1; | ||||
| } | ||||
|  | ||||
| #ifdef __linux__ | ||||
| @@ -2251,14 +2248,11 @@ int main(int argc, char *argv[]) | ||||
| 	for (;;) { | ||||
| 		if (_idle_since) { | ||||
| 			if (_exit_now) { | ||||
| 				if (_exit_now == DM_SCHEDULED_EXIT) | ||||
| 					break; /* Only prints shutdown message */ | ||||
| 				log_info("dmeventd detected break while being idle " | ||||
| 					 "for %ld second(s), exiting.", | ||||
| 					 (long) (time(NULL) - _idle_since)); | ||||
| 				break; | ||||
| 			} | ||||
| 			if (idle_exit_timeout) { | ||||
| 			} else if (idle_exit_timeout) { | ||||
| 				now = time(NULL); | ||||
| 				if (now < _idle_since) | ||||
| 					_idle_since = now; /* clock change? */ | ||||
| @@ -2269,14 +2263,15 @@ int main(int argc, char *argv[]) | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} else if (_exit_now == DM_SIGNALED_EXIT) { | ||||
| 			_exit_now = DM_SCHEDULED_EXIT; | ||||
| 		} else if (_exit_now) { | ||||
| 			_exit_now = 0; | ||||
| 			/* | ||||
| 			 * When '_exit_now' is set, signal has been received, | ||||
| 			 * but can not simply exit unless all | ||||
| 			 * threads are done processing. | ||||
| 			 */ | ||||
| 			log_info("dmeventd received break, scheduling exit."); | ||||
| 			log_warn("WARNING: There are still devices being monitored."); | ||||
| 			log_warn("WARNING: Refusing to exit."); | ||||
| 		} | ||||
| 		_process_request(&fifos); | ||||
| 		_cleanup_unused_threads(); | ||||
|   | ||||
| @@ -250,9 +250,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; | ||||
| @@ -328,9 +329,10 @@ static int _daemon_write(struct dm_event_fifos *fifos, | ||||
| 		if (ret < 0) { | ||||
| 			if ((errno == EINTR) || (errno == EAGAIN)) | ||||
| 				continue; | ||||
|  | ||||
| 			log_error("Unable to talk to event daemon."); | ||||
| 			return 0; | ||||
| 			else { | ||||
| 				log_error("Unable to talk to event daemon."); | ||||
| 				return 0; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		bytes += ret; | ||||
| @@ -452,8 +454,7 @@ static int _start_daemon(char *dmeventd_path, struct dm_event_fifos *fifos) | ||||
| 		if (close(fifos->client)) | ||||
| 			log_sys_debug("close", fifos->client_path); | ||||
| 		return 1; | ||||
| 	} | ||||
| 	if (errno != ENXIO && errno != ENOENT)  { | ||||
| 	} else if (errno != ENXIO && errno != ENOENT)  { | ||||
| 		/* problem */ | ||||
| 		log_sys_error("open", fifos->client_path); | ||||
| 		return 0; | ||||
|   | ||||
| @@ -121,7 +121,6 @@ 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; | ||||
|  | ||||
| @@ -136,21 +135,6 @@ int dmeventd_lvm2_command(struct dm_pool *mem, char *buffer, size_t size, | ||||
| 	    (layer = strstr(lv, "_mlog"))) | ||||
| 		*layer = '\0'; | ||||
|  | ||||
| 	if (!strncmp(cmd, _internal_prefix, sizeof(_internal_prefix) - 1)) { | ||||
| 		dmeventd_lvm2_lock(); | ||||
| 		/* output of internal command passed via env var */ | ||||
| 		if (!dmeventd_lvm2_run(cmd)) | ||||
| 			cmd = NULL; | ||||
| 		else if ((cmd = getenv(cmd))) | ||||
| 			cmd = dm_pool_strdup(mem, cmd); /* copy with lock */ | ||||
| 		dmeventd_lvm2_unlock(); | ||||
|  | ||||
| 		if (!cmd) { | ||||
| 			log_error("Unable to find configured command."); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	r = dm_snprintf(buffer, size, "%s %s/%s", cmd, vg, lv); | ||||
|  | ||||
| 	dm_pool_free(mem, vg); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -25,6 +25,7 @@ | ||||
|  | ||||
| struct dso_state { | ||||
| 	struct dm_pool *mem; | ||||
| 	char cmd_lvscan[512]; | ||||
| 	char cmd_lvconvert[512]; | ||||
| }; | ||||
|  | ||||
| @@ -98,8 +99,12 @@ static int _get_mirror_event(struct dso_state *state, char *params) | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _remove_failed_devices(const char *cmd_lvconvert, const char *device) | ||||
| static int _remove_failed_devices(const char *cmd_lvscan, const char *cmd_lvconvert, | ||||
| 				  const char *device) | ||||
| { | ||||
| 	if (!dmeventd_lvm2_run_with_lock(cmd_lvscan)) | ||||
| 		log_warn("WARNING: Re-scan of mirrored device %s failed.", device); | ||||
|  | ||||
| 	/* if repair goes OK, report success even if lvscan has failed */ | ||||
| 	if (!dmeventd_lvm2_run_with_lock(cmd_lvconvert)) { | ||||
| 		log_error("Repair of mirrored device %s failed.", device); | ||||
| @@ -146,7 +151,9 @@ void process_event(struct dm_task *dmt, | ||||
| 			break; | ||||
| 		case ME_FAILURE: | ||||
| 			log_error("Device failure in %s.", device); | ||||
| 			if (!_remove_failed_devices(state->cmd_lvconvert, device)) | ||||
| 			if (!_remove_failed_devices(state->cmd_lvscan, | ||||
| 						    state->cmd_lvconvert, | ||||
| 						    device)) | ||||
| 				/* FIXME Why are all the error return codes unused? Get rid of them? */ | ||||
| 				log_error("Failed to remove faulty devices in %s.", | ||||
| 					  device); | ||||
| @@ -176,10 +183,17 @@ int register_device(const char *device, | ||||
| 	if (!dmeventd_lvm2_init_with_pool("mirror_state", state)) | ||||
| 		goto_bad; | ||||
|  | ||||
|         /* CANNOT use --config as this disables cached content */ | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert), | ||||
| 				   "lvconvert --repair --use-policies", device)) | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvscan, sizeof(state->cmd_lvscan), | ||||
| 				   "lvscan --cache", device)) { | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
| 		goto_bad; | ||||
| 	} | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert), | ||||
| 				   "lvconvert --repair --use-policies", device)) { | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
| 		goto_bad; | ||||
| 	} | ||||
|  | ||||
| 	*user = state; | ||||
|  | ||||
| @@ -189,9 +203,6 @@ int register_device(const char *device, | ||||
| bad: | ||||
| 	log_error("Failed to monitor mirror %s.", device); | ||||
|  | ||||
| 	if (state) | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2005-2017 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2005-2016 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -22,6 +22,7 @@ | ||||
|  | ||||
| struct dso_state { | ||||
| 	struct dm_pool *mem; | ||||
| 	char cmd_lvscan[512]; | ||||
| 	char cmd_lvconvert[512]; | ||||
| 	uint64_t raid_devs[RAID_DEVS_ELEMS]; | ||||
| 	int failed; | ||||
| @@ -58,23 +59,6 @@ static int _process_raid_event(struct dso_state *state, char *params, const char | ||||
| 		dead = 1; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * if we are converting from non-RAID to RAID (e.g. linear -> raid1) | ||||
| 	 * and too many original devices die, such that we cannot continue | ||||
| 	 * the "recover" operation, the sync action will go to "idle", the | ||||
| 	 * unsynced devs will remain at 'a', and the original devices will | ||||
| 	 * NOT SWITCH TO 'D', but will remain at 'A' - hoping to be revived. | ||||
| 	 * | ||||
| 	 * This is simply the way the kernel works... | ||||
| 	 */ | ||||
| 	if (!strcmp(status->sync_action, "idle") && | ||||
| 	    (status->dev_health[0] == 'a') && | ||||
| 	    (status->insync_regions < status->total_regions)) { | ||||
| 		log_error("Primary sources for new RAID, %s, have failed.", | ||||
| 			  device); | ||||
| 		dead = 1; /* run it through LVM repair */ | ||||
| 	} | ||||
|  | ||||
| 	if (dead) { | ||||
| 		if (status->insync_regions < status->total_regions) { | ||||
| 			if (!state->warned) { | ||||
| @@ -90,6 +74,8 @@ static int _process_raid_event(struct dso_state *state, char *params, const char | ||||
| 			goto out; /* already reported */ | ||||
|  | ||||
| 		state->failed = 1; | ||||
| 		if (!dmeventd_lvm2_run_with_lock(state->cmd_lvscan)) | ||||
| 			log_warn("WARNING: Re-scan of RAID device %s failed.", device); | ||||
|  | ||||
| 		/* if repair goes OK, report success even if lvscan has failed */ | ||||
| 		if (!dmeventd_lvm2_run_with_lock(state->cmd_lvconvert)) { | ||||
| @@ -98,8 +84,6 @@ static int _process_raid_event(struct dso_state *state, char *params, const char | ||||
| 		} | ||||
| 	} else { | ||||
| 		state->failed = 0; | ||||
| 		if (status->insync_regions == status->total_regions) | ||||
| 			memset(&state->raid_devs, 0, sizeof(state->raid_devs)); | ||||
| 		log_info("%s array, %s, is %s in-sync.", | ||||
| 			 status->raid_type, device, | ||||
| 			 (status->insync_regions == status->total_regions) ? "now" : "not"); | ||||
| @@ -152,9 +136,14 @@ int register_device(const char *device, | ||||
| 	if (!dmeventd_lvm2_init_with_pool("raid_state", state)) | ||||
| 		goto_bad; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert), | ||||
| 				   "lvconvert --repair --use-policies", device)) | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvscan, sizeof(state->cmd_lvscan), | ||||
| 				   "lvscan --cache", device) || | ||||
| 	    !dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert), | ||||
| 				   "lvconvert --config devices{ignore_suspended_devices=1} " | ||||
| 				   "--repair --use-policies", device)) { | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
| 		goto_bad; | ||||
| 	} | ||||
|  | ||||
| 	*user = state; | ||||
|  | ||||
| @@ -164,9 +153,6 @@ int register_device(const char *device, | ||||
| bad: | ||||
| 	log_error("Failed to monitor RAID %s.", device); | ||||
|  | ||||
| 	if (state) | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -231,7 +231,7 @@ void process_event(struct dm_task *dmt, | ||||
|  | ||||
| 		if (percent >= WARNING_THRESH) /* Print a warning to syslog. */ | ||||
| 			log_warn("WARNING: Snapshot %s is now %.2f%% full.", | ||||
| 				 device, dm_percent_to_round_float(percent, 2)); | ||||
| 				 device, dm_percent_to_float(percent)); | ||||
|  | ||||
| 		/* Try to extend the snapshot, in accord with user-set policies */ | ||||
| 		if (!_extend(state->cmd_lvextend)) | ||||
| @@ -254,8 +254,10 @@ int register_device(const char *device, | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_lvextend, | ||||
| 				   sizeof(state->cmd_lvextend), | ||||
| 				   "lvextend --use-policies", device)) | ||||
| 				   "lvextend --use-policies", device)) { | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
| 		goto_bad; | ||||
| 	} | ||||
|  | ||||
| 	state->percent_check = CHECK_MINIMUM; | ||||
| 	*user = state; | ||||
| @@ -266,9 +268,6 @@ int register_device(const char *device, | ||||
| bad: | ||||
| 	log_error("Failed to monitor snapshot %s.", device); | ||||
|  | ||||
| 	if (state) | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /* | ||||
|  * Copyright (C) 2011-2017 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2011-2016 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -18,6 +18,7 @@ | ||||
|  | ||||
| #include <sys/wait.h> | ||||
| #include <stdarg.h> | ||||
| #include <pthread.h> | ||||
|  | ||||
| /* TODO - move this mountinfo code into library to be reusable */ | ||||
| #ifdef __linux__ | ||||
| @@ -39,118 +40,277 @@ | ||||
|  | ||||
| #define UMOUNT_COMMAND "/bin/umount" | ||||
|  | ||||
| #define MAX_FAILS	(256)  /* ~42 mins between cmd call retry with 10s delay */ | ||||
| #define MAX_FAILS	(10) | ||||
|  | ||||
| #define THIN_DEBUG 0 | ||||
|  | ||||
| struct dso_state { | ||||
| 	struct dm_pool *mem; | ||||
| 	int metadata_percent_check; | ||||
| 	int metadata_percent; | ||||
| 	int data_percent_check; | ||||
| 	int data_percent; | ||||
| 	uint64_t known_metadata_size; | ||||
| 	uint64_t known_data_size; | ||||
| 	unsigned fails; | ||||
| 	unsigned max_fails; | ||||
| 	int restore_sigset; | ||||
| 	sigset_t old_sigset; | ||||
| 	pid_t pid; | ||||
| 	char *argv[3]; | ||||
| 	char *cmd_str; | ||||
| 	char cmd_str[1024]; | ||||
| }; | ||||
|  | ||||
| DM_EVENT_LOG_FN("thin") | ||||
|  | ||||
| static int _run_command(struct dso_state *state) | ||||
| #define UUID_PREFIX "LVM-" | ||||
|  | ||||
| /* Figure out device UUID has LVM- prefix and is OPEN */ | ||||
| static int _has_unmountable_prefix(int major, int minor) | ||||
| { | ||||
| 	char val[3][36]; | ||||
| 	char *env[] = { val[0], val[1], val[2], NULL }; | ||||
| 	int i; | ||||
| 	struct dm_task *dmt; | ||||
| 	struct dm_info info; | ||||
| 	const char *uuid; | ||||
| 	int r = 0; | ||||
|  | ||||
| 	/* Mark for possible lvm2 command we are running from dmeventd | ||||
| 	 * lvm2 will not try to talk back to dmeventd while processing it */ | ||||
| 	(void) dm_snprintf(val[0], sizeof(val[0]), "LVM_RUN_BY_DMEVENTD=1"); | ||||
| 	if (!(dmt = dm_task_create(DM_DEVICE_INFO))) | ||||
| 		return_0; | ||||
|  | ||||
| 	if (state->data_percent) { | ||||
| 		/* Prepare some known data to env vars for easy use */ | ||||
| 		(void) dm_snprintf(val[1], sizeof(val[1]), "DMEVENTD_THIN_POOL_DATA=%d", | ||||
| 				   state->data_percent / DM_PERCENT_1); | ||||
| 		(void) dm_snprintf(val[2], sizeof(val[2]), "DMEVENTD_THIN_POOL_METADATA=%d", | ||||
| 				   state->metadata_percent / DM_PERCENT_1); | ||||
| 	} else { | ||||
| 		/* For an error event it's for a user to check status and decide */ | ||||
| 		env[1] = NULL; | ||||
| 		log_debug("Error event processing."); | ||||
| 	if (!dm_task_set_major_minor(dmt, major, minor, 1)) | ||||
| 		goto_out; | ||||
|  | ||||
| 	if (!dm_task_no_flush(dmt)) | ||||
| 		stack; | ||||
|  | ||||
| 	if (!dm_task_run(dmt)) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!dm_task_get_info(dmt, &info)) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!info.exists || !info.open_count) | ||||
| 		goto out; /* Not open -> not mounted */ | ||||
|  | ||||
| 	if (!(uuid = dm_task_get_uuid(dmt))) | ||||
| 		goto out; | ||||
|  | ||||
| 	/* Check it's public mountable LV | ||||
| 	 * has prefix  LVM-  and UUID size is 68 chars */ | ||||
| 	if (memcmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1) || | ||||
| 	    strlen(uuid) != 68) | ||||
| 		goto out; | ||||
|  | ||||
| #if THIN_DEBUG | ||||
| 	log_debug("Found logical volume %s (%u:%u).", uuid, major, minor); | ||||
| #endif | ||||
| 	r = 1; | ||||
| out: | ||||
| 	dm_task_destroy(dmt); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* Get dependencies for device, and try to find matching device */ | ||||
| static int _has_deps(const char *name, int tp_major, int tp_minor, int *dev_minor) | ||||
| { | ||||
| 	struct dm_task *dmt; | ||||
| 	const struct dm_deps *deps; | ||||
| 	struct dm_info info; | ||||
| 	int major, minor; | ||||
| 	int r = 0; | ||||
|  | ||||
| 	if (!(dmt = dm_task_create(DM_DEVICE_DEPS))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!dm_task_set_name(dmt, name)) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!dm_task_no_open_count(dmt)) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!dm_task_run(dmt)) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!dm_task_get_info(dmt, &info)) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!(deps = dm_task_get_deps(dmt))) | ||||
| 		goto out; | ||||
|  | ||||
| 	if (!info.exists || deps->count != 1) | ||||
| 		goto out; | ||||
|  | ||||
| 	major = (int) MAJOR(deps->device[0]); | ||||
| 	minor = (int) MINOR(deps->device[0]); | ||||
| 	if ((major != tp_major) || (minor != tp_minor)) | ||||
| 		goto out; | ||||
|  | ||||
| 	*dev_minor = info.minor; | ||||
|  | ||||
| 	if (!_has_unmountable_prefix(major, info.minor)) | ||||
| 		goto out; | ||||
|  | ||||
| #if THIN_DEBUG | ||||
| 	{ | ||||
| 		char dev_name[PATH_MAX]; | ||||
| 		if (dm_device_get_name(major, minor, 0, dev_name, sizeof(dev_name))) | ||||
| 			log_debug("Found %s (%u:%u) depends on %s.", | ||||
| 				  name, major, *dev_minor, dev_name); | ||||
| 	} | ||||
| #endif | ||||
| 	r = 1; | ||||
| out: | ||||
| 	dm_task_destroy(dmt); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* Get all active devices */ | ||||
| static int _find_all_devs(dm_bitset_t bs, int tp_major, int tp_minor) | ||||
| { | ||||
| 	struct dm_task *dmt; | ||||
| 	struct dm_names *names; | ||||
| 	unsigned next = 0; | ||||
| 	int minor, r = 1; | ||||
|  | ||||
| 	if (!(dmt = dm_task_create(DM_DEVICE_LIST))) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!dm_task_run(dmt)) { | ||||
| 		r = 0; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	log_verbose("Executing command: %s", state->cmd_str); | ||||
| 	if (!(names = dm_task_get_names(dmt))) { | ||||
| 		r = 0; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	/* TODO: | ||||
| 	 *   Support parallel run of 'task' and it's waitpid maintainence | ||||
| 	 *   ATM we can't handle signaling of  SIGALRM | ||||
| 	 *   as signalling is not allowed while 'process_event()' is running | ||||
| 	 */ | ||||
| 	if (!(state->pid = fork())) { | ||||
| 		/* child */ | ||||
| 		(void) close(0); | ||||
| 		for (i = 3; i < 255; ++i) (void) close(i); | ||||
| 		execve(state->argv[0], state->argv, env); | ||||
| 		_exit(errno); | ||||
| 	} else if (state->pid == -1) { | ||||
| 		log_error("Can't fork command %s.", state->cmd_str); | ||||
| 		state->fails = 1; | ||||
| 		return 0; | ||||
| 	if (!names->dev) | ||||
| 		goto out; | ||||
|  | ||||
| 	do { | ||||
| 		names = (struct dm_names *)((char *) names + next); | ||||
| 		if (_has_deps(names->name, tp_major, tp_minor, &minor)) | ||||
| 			dm_bit_set(bs, minor); | ||||
| 		next = names->next; | ||||
| 	} while (next); | ||||
|  | ||||
| out: | ||||
| 	dm_task_destroy(dmt); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _run(const char *cmd, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| 	int argc = 1; /* for argv[0], i.e. cmd */ | ||||
| 	int i = 0; | ||||
| 	const char **argv; | ||||
| 	pid_t pid = fork(); | ||||
| 	int status; | ||||
|  | ||||
| 	if (pid == 0) { /* child */ | ||||
| 		va_start(ap, cmd); | ||||
| 		while (va_arg(ap, const char *)) | ||||
| 			++argc; | ||||
| 		va_end(ap); | ||||
|  | ||||
| 		/* + 1 for the terminating NULL */ | ||||
| 		argv = alloca(sizeof(const char *) * (argc + 1)); | ||||
|  | ||||
| 		argv[0] = cmd; | ||||
| 		va_start(ap, cmd); | ||||
| 		while ((argv[++i] = va_arg(ap, const char *))); | ||||
| 		va_end(ap); | ||||
|  | ||||
| 		execvp(cmd, (char **)argv); | ||||
| 		log_sys_error("exec", cmd); | ||||
| 		exit(127); | ||||
| 	} | ||||
|  | ||||
| 	if (pid > 0) { /* parent */ | ||||
| 		if (waitpid(pid, &status, 0) != pid) | ||||
| 			return 0; /* waitpid failed */ | ||||
| 		if (!WIFEXITED(status) || WEXITSTATUS(status)) | ||||
| 			return 0; /* the child failed */ | ||||
| 	} | ||||
|  | ||||
| 	if (pid < 0) | ||||
| 		return 0; /* fork failed */ | ||||
|  | ||||
| 	return 1; /* all good */ | ||||
| } | ||||
|  | ||||
| struct mountinfo_s { | ||||
| 	const char *device; | ||||
| 	struct dm_info info; | ||||
| 	dm_bitset_t minors; /* Bitset for active thin pool minors */ | ||||
| }; | ||||
|  | ||||
| static int _umount_device(char *buffer, unsigned major, unsigned minor, | ||||
| 			  char *target, void *cb_data) | ||||
| { | ||||
| 	struct mountinfo_s *data = cb_data; | ||||
| 	char *words[10]; | ||||
|  | ||||
| 	if ((major == data->info.major) && dm_bit(data->minors, minor)) { | ||||
| 		if (dm_split_words(buffer, DM_ARRAY_SIZE(words), 0, words) < DM_ARRAY_SIZE(words)) | ||||
| 			words[9] = NULL; /* just don't show device name */ | ||||
| 		log_info("Unmounting thin %s (%d:%d) of thin pool %s (%u:%u) from mount point \"%s\".", | ||||
| 			 words[9] ? : "", major, minor, data->device, | ||||
| 			 data->info.major, data->info.minor, | ||||
| 			 target); | ||||
| 		if (!_run(UMOUNT_COMMAND, "-fl", target, NULL)) | ||||
| 			log_error("Failed to lazy umount thin %s (%d:%d) from %s: %s.", | ||||
| 				  words[9], major, minor, target, strerror(errno)); | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Find all thin pool LV users and try to umount them. | ||||
|  * TODO: work with read-only thin pool support | ||||
|  */ | ||||
| static void _umount(struct dm_task *dmt) | ||||
| { | ||||
| 	/* TODO: Convert to use hash to reduce memory usage */ | ||||
| 	static const size_t MINORS = (1U << 20); /* 20 bit */ | ||||
| 	struct mountinfo_s data = { NULL }; | ||||
|  | ||||
| 	if (!dm_task_get_info(dmt, &data.info)) | ||||
| 		return; | ||||
|  | ||||
| 	data.device = dm_task_get_name(dmt); | ||||
|  | ||||
| 	if (!(data.minors = dm_bitset_create(NULL, MINORS))) { | ||||
| 		log_error("Failed to allocate bitset. Not unmounting %s.", data.device); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!_find_all_devs(data.minors, data.info.major, data.info.minor)) { | ||||
| 		log_error("Failed to detect mounted volumes for %s.", data.device); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!dm_mountinfo_read(_umount_device, &data)) { | ||||
| 		log_error("Could not parse mountinfo file."); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| out: | ||||
| 	if (data.minors) | ||||
| 		dm_bitset_destroy(data.minors); | ||||
| } | ||||
|  | ||||
| static int _use_policy(struct dm_task *dmt, struct dso_state *state) | ||||
| { | ||||
| #if THIN_DEBUG | ||||
| 	log_debug("dmeventd executes: %s.", state->cmd_str); | ||||
| #endif | ||||
| 	if (state->argv[0]) | ||||
| 		return _run_command(state); | ||||
|  | ||||
| 	if (!dmeventd_lvm2_run_with_lock(state->cmd_str)) { | ||||
| 		log_error("Failed command for %s.", dm_task_get_name(dmt)); | ||||
| 		state->fails = 1; | ||||
| 		log_error("Failed to extend thin pool %s.", | ||||
| 			  dm_task_get_name(dmt)); | ||||
| 		state->fails++; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	state->fails = 0; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* Check if executed command has finished | ||||
|  * Only 1 command may run */ | ||||
| static int _wait_for_pid(struct dso_state *state) | ||||
| { | ||||
| 	int status = 0; | ||||
|  | ||||
| 	if (state->pid == -1) | ||||
| 		return 1; | ||||
|  | ||||
| 	if (!waitpid(state->pid, &status, WNOHANG)) | ||||
| 		return 0; | ||||
|  | ||||
| 	/* Wait for finish */ | ||||
| 	if (WIFEXITED(status)) { | ||||
| 		log_verbose("Child %d exited with status %d.", | ||||
| 			    state->pid, WEXITSTATUS(status)); | ||||
| 		state->fails = WEXITSTATUS(status) ? 1 : 0; | ||||
| 	} else { | ||||
| 		if (WIFSIGNALED(status)) | ||||
| 			log_verbose("Child %d was terminated with status %d.", | ||||
| 				    state->pid, WTERMSIG(status)); | ||||
| 		state->fails = 1; | ||||
| 	} | ||||
|  | ||||
| 	state->pid = -1; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| @@ -159,6 +319,7 @@ void process_event(struct dm_task *dmt, | ||||
| 		   void **user) | ||||
| { | ||||
| 	const char *device = dm_task_get_name(dmt); | ||||
| 	int percent; | ||||
| 	struct dso_state *state = *user; | ||||
| 	struct dm_status_thin_pool *tps = NULL; | ||||
| 	void *next = NULL; | ||||
| @@ -166,48 +327,25 @@ void process_event(struct dm_task *dmt, | ||||
| 	char *target_type = NULL; | ||||
| 	char *params; | ||||
| 	int needs_policy = 0; | ||||
| 	struct dm_task *new_dmt = NULL; | ||||
| 	int needs_umount = 0; | ||||
|  | ||||
| #if THIN_DEBUG | ||||
| 	log_debug("Watch for tp-data:%.2f%%  tp-metadata:%.2f%%.", | ||||
| 		  dm_percent_to_round_float(state->data_percent_check, 2), | ||||
| 		  dm_percent_to_round_float(state->metadata_percent_check, 2)); | ||||
| 		  dm_percent_to_float(state->data_percent_check), | ||||
| 		  dm_percent_to_float(state->metadata_percent_check)); | ||||
| #endif | ||||
| 	if (!_wait_for_pid(state)) { | ||||
| 		log_warn("WARNING: Skipping event, child %d is still running (%s).", | ||||
| 			 state->pid, state->cmd_str); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| #if 0 | ||||
| 	/* No longer monitoring, waiting for remove */ | ||||
| 	if (!state->meta_percent_check && !state->data_percent_check) | ||||
| 		return; | ||||
| #endif | ||||
| 	if (event & DM_EVENT_DEVICE_ERROR) { | ||||
| 		/* Error -> no need to check and do instant resize */ | ||||
| 		state->data_percent = state->metadata_percent = 0; | ||||
| 		if (_use_policy(dmt, state)) | ||||
| 			goto out; | ||||
|  | ||||
| 		stack; | ||||
|  | ||||
| 		/* | ||||
| 		 * Rather update oldish status | ||||
| 		 * since after 'command' processing | ||||
| 		 * percentage info could have changed a lot. | ||||
| 		 * If we would get above UMOUNT_THRESH | ||||
| 		 * we would wait for next sigalarm. | ||||
| 		 */ | ||||
| 		if (!(new_dmt = dm_task_create(DM_DEVICE_STATUS))) | ||||
| 			goto_out; | ||||
|  | ||||
| 		if (!dm_task_set_uuid(new_dmt, dm_task_get_uuid(dmt))) | ||||
| 			goto_out; | ||||
|  | ||||
| 		/* Non-blocking status read */ | ||||
| 		if (!dm_task_no_flush(new_dmt)) | ||||
| 			log_warn("WARNING: Can't set no_flush for dm status."); | ||||
|  | ||||
| 		if (!dm_task_run(new_dmt)) | ||||
| 			goto_out; | ||||
|  | ||||
| 		dmt = new_dmt; | ||||
| 	} | ||||
|  | ||||
| 	dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); | ||||
| @@ -219,6 +357,7 @@ void process_event(struct dm_task *dmt, | ||||
|  | ||||
| 	if (!dm_get_status_thin_pool(state->mem, params, &tps)) { | ||||
| 		log_error("Failed to parse status."); | ||||
| 		needs_umount = 1; | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| @@ -233,110 +372,67 @@ void process_event(struct dm_task *dmt, | ||||
| 	if (state->known_metadata_size != tps->total_metadata_blocks) { | ||||
| 		state->metadata_percent_check = CHECK_MINIMUM; | ||||
| 		state->known_metadata_size = tps->total_metadata_blocks; | ||||
| 		state->fails = 0; | ||||
| 	} | ||||
|  | ||||
| 	if (state->known_data_size != tps->total_data_blocks) { | ||||
| 		state->data_percent_check = CHECK_MINIMUM; | ||||
| 		state->known_data_size = tps->total_data_blocks; | ||||
| 		state->fails = 0; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Trigger action when threshold boundary is exceeded. | ||||
| 	 * Report 80% threshold warning when it's used above 80%. | ||||
| 	 * Only 100% is exception as it cannot be surpased so policy | ||||
| 	 * action is called for:  >50%, >55% ... >95%, 100% | ||||
| 	 */ | ||||
| 	state->metadata_percent = dm_make_percent(tps->used_metadata_blocks, tps->total_metadata_blocks); | ||||
| 	if ((state->metadata_percent > WARNING_THRESH) && | ||||
| 	    (state->metadata_percent > state->metadata_percent_check)) | ||||
| 		log_warn("WARNING: Thin pool %s metadata is now %.2f%% full.", | ||||
| 			 device, dm_percent_to_round_float(state->metadata_percent, 2)); | ||||
| 	if (state->metadata_percent > CHECK_MINIMUM) { | ||||
| 		/* Run action when usage raised more than CHECK_STEP since the last time */ | ||||
| 		if (state->metadata_percent > state->metadata_percent_check) | ||||
| 			needs_policy = 1; | ||||
| 		state->metadata_percent_check = (state->metadata_percent / CHECK_STEP + 1) * CHECK_STEP; | ||||
| 		if (state->metadata_percent_check == DM_PERCENT_100) | ||||
| 			state->metadata_percent_check--; /* Can't get bigger then 100% */ | ||||
| 	} else | ||||
| 		state->metadata_percent_check = CHECK_MINIMUM; | ||||
| 	percent = dm_make_percent(tps->used_metadata_blocks, tps->total_metadata_blocks); | ||||
| 	if (percent >= state->metadata_percent_check) { | ||||
| 		/* | ||||
| 		 * Usage has raised more than CHECK_STEP since the last | ||||
| 		 * time. Run actions. | ||||
| 		 */ | ||||
| 		state->metadata_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP; | ||||
|  | ||||
| 	state->data_percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks); | ||||
| 	if ((state->data_percent > WARNING_THRESH) && | ||||
| 	    (state->data_percent > state->data_percent_check)) | ||||
| 		log_warn("WARNING: Thin pool %s data is now %.2f%% full.", | ||||
| 			 device, dm_percent_to_round_float(state->data_percent, 2)); | ||||
| 	if (state->data_percent > CHECK_MINIMUM) { | ||||
| 		/* Run action when usage raised more than CHECK_STEP since the last time */ | ||||
| 		if (state->data_percent > state->data_percent_check) | ||||
| 			needs_policy = 1; | ||||
| 		state->data_percent_check = (state->data_percent / CHECK_STEP + 1) * CHECK_STEP; | ||||
| 		if (state->data_percent_check == DM_PERCENT_100) | ||||
| 			state->data_percent_check--; /* Can't get bigger then 100% */ | ||||
| 	} else | ||||
| 		state->data_percent_check = CHECK_MINIMUM; | ||||
| 		/* FIXME: extension of metadata needs to be written! */ | ||||
| 		if (percent >= WARNING_THRESH) /* Print a warning to syslog. */ | ||||
| 			log_warn("WARNING: Thin pool %s metadata is now %.2f%% full.", | ||||
| 				 device, dm_percent_to_float(percent)); | ||||
| 		needs_policy = 1; | ||||
|  | ||||
| 	/* Reduce number of _use_policy() calls by power-of-2 factor till frequency of MAX_FAILS is reached. | ||||
| 	 * Avoids too high number of error retries, yet shows some status messages in log regularly. | ||||
| 	 * i.e. PV could have been pvmoved and VG/LV was locked for a while... | ||||
| 	 */ | ||||
| 	if (state->fails) { | ||||
| 		if (state->fails++ <= state->max_fails) { | ||||
| 			log_debug("Postponing frequently failing policy (%u <= %u).", | ||||
| 				  state->fails - 1, state->max_fails); | ||||
| 			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 */ | ||||
| 		if (percent >= UMOUNT_THRESH) | ||||
| 			needs_umount = 1; | ||||
| 	} | ||||
|  | ||||
| 	if (needs_policy) | ||||
| 		_use_policy(dmt, state); | ||||
| 	percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks); | ||||
| 	if (percent >= state->data_percent_check) { | ||||
| 		/* | ||||
| 		 * Usage has raised more than CHECK_STEP since | ||||
| 		 * the last time. Run actions. | ||||
| 		 */ | ||||
| 		state->data_percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP; | ||||
|  | ||||
| 		if (percent >= WARNING_THRESH) /* Print a warning to syslog. */ | ||||
| 			log_warn("WARNING: Thin pool %s data is now %.2f%% full.", | ||||
| 				 device, dm_percent_to_float(percent)); | ||||
| 		needs_policy = 1; | ||||
|  | ||||
| 		if (percent >= UMOUNT_THRESH) | ||||
| 			needs_umount = 1; | ||||
| 	} | ||||
|  | ||||
| 	if (needs_policy && | ||||
| 	    _use_policy(dmt, state)) | ||||
| 		needs_umount = 0; /* No umount when command was successful */ | ||||
| out: | ||||
| 	if (needs_umount) { | ||||
| 		_umount(dmt); | ||||
| 		/* Until something changes, do not retry any more actions */ | ||||
| 		state->data_percent_check = state->metadata_percent_check = (DM_PERCENT_1 * 101); | ||||
| 	} | ||||
|  | ||||
| 	if (tps) | ||||
| 		dm_pool_free(state->mem, tps); | ||||
|  | ||||
| 	if (new_dmt) | ||||
| 		dm_task_destroy(new_dmt); | ||||
| } | ||||
|  | ||||
| /* Handle SIGCHLD for a thread */ | ||||
| static void _sig_child(int signum __attribute__((unused))) | ||||
| { | ||||
| 	/* empty SIG_IGN */; | ||||
| } | ||||
|  | ||||
| /* Setup handler for SIGCHLD when executing external command | ||||
|  * to get quick 'waitpid()' reaction | ||||
|  * It will interrupt syscall just like SIGALRM and | ||||
|  * invoke process_event(). | ||||
|  */ | ||||
| static void _init_thread_signals(struct dso_state *state) | ||||
| { | ||||
| 	struct sigaction act = { .sa_handler = _sig_child }; | ||||
| 	sigset_t my_sigset; | ||||
|  | ||||
| 	sigemptyset(&my_sigset); | ||||
|  | ||||
| 	if (sigaction(SIGCHLD, &act, NULL)) | ||||
| 		log_warn("WARNING: Failed to set SIGCHLD action."); | ||||
| 	else if (sigaddset(&my_sigset, SIGCHLD)) | ||||
| 		log_warn("WARNING: Failed to add SIGCHLD to set."); | ||||
| 	else if (pthread_sigmask(SIG_UNBLOCK, &my_sigset, &state->old_sigset)) | ||||
| 		log_warn("WARNING: Failed to unblock SIGCHLD."); | ||||
| 	else | ||||
| 		state->restore_sigset = 1; | ||||
| } | ||||
|  | ||||
| static void _restore_thread_signals(struct dso_state *state) | ||||
| { | ||||
| 	if (state->restore_sigset && | ||||
| 	    pthread_sigmask(SIG_SETMASK, &state->old_sigset, NULL)) | ||||
| 		log_warn("WARNING: Failed to block SIGCHLD."); | ||||
| 	if (state->fails >= MAX_FAILS) { | ||||
| 		log_warn("WARNING: Dropping monitoring of %s. " | ||||
| 			 "lvm2 command fails too often (%u times in row).", | ||||
| 			 device, state->fails); | ||||
| 		pthread_kill(pthread_self(), SIGALRM); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int register_device(const char *device, | ||||
| @@ -346,56 +442,28 @@ int register_device(const char *device, | ||||
| 		    void **user) | ||||
| { | ||||
| 	struct dso_state *state; | ||||
| 	char *str; | ||||
| 	char cmd_str[PATH_MAX + 128 + 2]; /* cmd ' ' vg/lv \0 */ | ||||
|  | ||||
| 	if (!dmeventd_lvm2_init_with_pool("thin_pool_state", state)) | ||||
| 		goto_bad; | ||||
|  | ||||
| 	if (!dmeventd_lvm2_command(state->mem, cmd_str, sizeof(cmd_str), | ||||
| 				   "_dmeventd_thin_command", device)) | ||||
| 	if (!dmeventd_lvm2_command(state->mem, state->cmd_str, | ||||
| 				   sizeof(state->cmd_str), | ||||
| 				   "lvextend --use-policies", | ||||
| 				   device)) { | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
| 		goto_bad; | ||||
| 	} | ||||
|  | ||||
| 	if (strncmp(cmd_str, "lvm ", 4) == 0) { | ||||
| 		if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str + 4))) { | ||||
| 			log_error("Failed to copy lvm command."); | ||||
| 			goto bad; | ||||
| 		} | ||||
| 	} else if (cmd_str[0] == '/') { | ||||
| 		if (!(state->cmd_str = dm_pool_strdup(state->mem, cmd_str))) { | ||||
| 			log_error("Failed to copy thin command."); | ||||
| 			goto bad; | ||||
| 		} | ||||
|  | ||||
| 		/* Find last space before 'vg/lv' */ | ||||
| 		if (!(str = strrchr(state->cmd_str, ' '))) | ||||
| 			goto inval; | ||||
|  | ||||
| 		if (!(state->argv[0] = dm_pool_strndup(state->mem, state->cmd_str, | ||||
| 						       str - state->cmd_str))) { | ||||
| 			log_error("Failed to copy command."); | ||||
| 			goto bad; | ||||
| 		} | ||||
|  | ||||
| 		state->argv[1] = str + 1;  /* 1 argument - vg/lv */ | ||||
| 		_init_thread_signals(state); | ||||
| 	} else /* Unuspported command format */ | ||||
| 		goto inval; | ||||
|  | ||||
| 	state->pid = -1; | ||||
| 	state->metadata_percent_check = CHECK_MINIMUM; | ||||
| 	state->data_percent_check = CHECK_MINIMUM; | ||||
| 	*user = state; | ||||
|  | ||||
| 	log_info("Monitoring thin pool %s.", device); | ||||
|  | ||||
| 	return 1; | ||||
| inval: | ||||
| 	log_error("Invalid command for monitoring: %s.", cmd_str); | ||||
| bad: | ||||
| 	log_error("Failed to monitor thin pool %s.", device); | ||||
|  | ||||
| 	if (state) | ||||
| 		dmeventd_lvm2_exit_with_pool(state); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -406,28 +474,6 @@ int unregister_device(const char *device, | ||||
| 		      void **user) | ||||
| { | ||||
| 	struct dso_state *state = *user; | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; !_wait_for_pid(state) && (i < 6); ++i) { | ||||
| 		if (i == 0) | ||||
| 			/* Give it 2 seconds, then try to terminate & kill it */ | ||||
| 			log_verbose("Child %d still not finished (%s) waiting.", | ||||
| 				    state->pid, state->cmd_str); | ||||
| 		else if (i == 3) { | ||||
| 			log_warn("WARNING: Terminating child %d.", state->pid); | ||||
| 			kill(state->pid, SIGINT); | ||||
| 			kill(state->pid, SIGTERM); | ||||
| 		} else if (i == 5) { | ||||
| 			log_warn("WARNING: Killing child %d.", state->pid); | ||||
| 			kill(state->pid, SIGKILL); | ||||
| 		} | ||||
| 		sleep(1); | ||||
| 	} | ||||
|  | ||||
| 	if (state->pid != -1) | ||||
| 		log_warn("WARNING: Cannot kill child %d!", state->pid); | ||||
|  | ||||
| 	_restore_thread_signals(state); | ||||
|  | ||||
| 	dmeventd_lvm2_exit_with_pool(state); | ||||
| 	log_info("No longer monitoring thin pool %s.", device); | ||||
|   | ||||
							
								
								
									
										1
									
								
								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,834 +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((x),(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, sid; | ||||
| 	int fd; | ||||
|  | ||||
| 	if (!(sid = 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 * _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)) | ||||
| 		return 1; | ||||
|  | ||||
| 	return _dmfilemapd(&fm); | ||||
| } | ||||
							
								
								
									
										3
									
								
								daemons/lvmdbusd/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								daemons/lvmdbusd/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1 @@ | ||||
| path.py | ||||
| lvmdbusd | ||||
| lvmdb.py | ||||
| lvm_shell_proxy.py | ||||
|   | ||||
| @@ -26,7 +26,9 @@ LVMDBUS_SRCDIR_FILES = \ | ||||
| 	__init__.py \ | ||||
| 	job.py \ | ||||
| 	loader.py \ | ||||
| 	lvmdb.py \ | ||||
| 	main.py \ | ||||
| 	lvm_shell_proxy.py \ | ||||
| 	lv.py \ | ||||
| 	manager.py \ | ||||
| 	objectmanager.py \ | ||||
| @@ -38,19 +40,14 @@ LVMDBUS_SRCDIR_FILES = \ | ||||
| 	vg.py | ||||
|  | ||||
| LVMDBUS_BUILDDIR_FILES = \ | ||||
| 	lvmdb.py \ | ||||
| 	lvm_shell_proxy.py \ | ||||
| 	path.py | ||||
|  | ||||
| LVMDBUSD = lvmdbusd | ||||
| LVMDBUSD = $(srcdir)/lvmdbusd | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| .PHONY: install_lvmdbusd | ||||
|  | ||||
| all: | ||||
| 	test -x $(LVMDBUSD) || chmod 755 $(LVMDBUSD) | ||||
|  | ||||
| install_lvmdbusd: | ||||
| 	$(INSTALL_DIR) $(sbindir) | ||||
| 	$(INSTALL_SCRIPT) $(LVMDBUSD) $(sbindir) | ||||
| @@ -66,5 +63,4 @@ install_lvm2: install_lvmdbusd | ||||
| install: install_lvm2 | ||||
|  | ||||
| DISTCLEAN_TARGETS+= \ | ||||
| 	$(LVMDBUS_BUILDDIR_FILES) \ | ||||
| 	$(LVMDBUSD) | ||||
| 	$(LVMDBUS_BUILDDIR_FILES) | ||||
|   | ||||
| @@ -38,7 +38,7 @@ class AutomatedProperties(dbus.service.Object): | ||||
| 		props = {} | ||||
|  | ||||
| 		for i in self.interface(): | ||||
| 			props[i] = AutomatedProperties._get_all_prop(self, i) | ||||
| 			props[i] = self.GetAll(i) | ||||
|  | ||||
| 		return self._ap_o_path, props | ||||
|  | ||||
| @@ -65,52 +65,31 @@ class AutomatedProperties(dbus.service.Object): | ||||
|  | ||||
| 		return self._ap_interface | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _get_prop(obj, interface_name, property_name): | ||||
| 		value = getattr(obj, property_name) | ||||
| 	# Properties | ||||
| 	# noinspection PyUnusedLocal | ||||
| 	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, | ||||
| 							in_signature='ss', out_signature='v') | ||||
| 	def Get(self, interface_name, property_name): | ||||
| 		value = getattr(self, property_name) | ||||
| 		# Note: If we get an exception in this handler we won't know about it, | ||||
| 		# only the side effect of no returned value! | ||||
| 		log_debug('Get (%s), type (%s), value(%s)' % | ||||
| 					(property_name, str(type(value)), str(value))) | ||||
| 		return value | ||||
|  | ||||
| 	# Properties | ||||
| 	# noinspection PyUnusedLocal | ||||
| 	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, | ||||
| 							in_signature='ss', out_signature='v', | ||||
| 							async_callbacks=('cb', 'cbe')) | ||||
| 	def Get(self, interface_name, property_name, cb, cbe): | ||||
| 		# Note: If we get an exception in this handler we won't know about it, | ||||
| 		# only the side effect of no returned value! | ||||
| 		r = cfg.create_request_entry( | ||||
| 			-1, AutomatedProperties._get_prop, | ||||
| 			(self, interface_name, property_name), | ||||
| 			cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _get_all_prop(obj, interface_name): | ||||
| 		if interface_name in obj.interface(True): | ||||
| 							in_signature='s', out_signature='a{sv}') | ||||
| 	def GetAll(self, interface_name): | ||||
| 		if interface_name in self.interface(True): | ||||
| 			# Using introspection, lets build this dynamically | ||||
| 			properties = get_properties(obj) | ||||
| 			properties = get_properties(self) | ||||
| 			if interface_name in properties: | ||||
| 				return properties[interface_name][1] | ||||
| 			return {} | ||||
| 		raise dbus.exceptions.DBusException( | ||||
| 			obj._ap_interface, | ||||
| 			self._ap_interface, | ||||
| 			'The object %s does not implement the %s interface' | ||||
| 			% (obj.__class__, interface_name)) | ||||
|  | ||||
| 	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, | ||||
| 							in_signature='s', out_signature='a{sv}', | ||||
| 							async_callbacks=('cb', 'cbe')) | ||||
| 	def GetAll(self, interface_name, cb, cbe): | ||||
| 		r = cfg.create_request_entry( | ||||
| 			-1, AutomatedProperties._get_all_prop, | ||||
| 			(self, interface_name), | ||||
| 			cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
| 			% (self.__class__, interface_name)) | ||||
|  | ||||
| 	@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, | ||||
| 							in_signature='ssv') | ||||
|   | ||||
| @@ -9,13 +9,11 @@ | ||||
|  | ||||
| import subprocess | ||||
| from . import cfg | ||||
| from .cmdhandler import options_to_cli_args, LvmExecutionMeta | ||||
| from .cmdhandler import options_to_cli_args | ||||
| import dbus | ||||
| from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug,\ | ||||
| 	add_no_notify | ||||
| from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug | ||||
| import os | ||||
| import threading | ||||
| import time | ||||
|  | ||||
|  | ||||
| def pv_move_lv_cmd(move_options, lv_full_name, | ||||
| @@ -44,15 +42,6 @@ def _move_merge(interface_name, command, job_state): | ||||
| 	# the command always as we will be getting periodic output from them on | ||||
| 	# the status of the long running operation. | ||||
| 	command.insert(0, cfg.LVM_CMD) | ||||
|  | ||||
| 	# Instruct lvm to not register an event with us | ||||
| 	command = add_no_notify(command) | ||||
|  | ||||
| 	#(self, start, ended, cmd, ec, stdout_txt, stderr_txt) | ||||
| 	meta = LvmExecutionMeta(time.time(), 0, command, -1000, None, None) | ||||
|  | ||||
| 	cfg.blackbox.add(meta) | ||||
|  | ||||
| 	process = subprocess.Popen(command, stdout=subprocess.PIPE, | ||||
| 								env=os.environ, | ||||
| 								stderr=subprocess.PIPE, close_fds=True) | ||||
| @@ -70,21 +59,12 @@ def _move_merge(interface_name, command, job_state): | ||||
| 				(device, ignore, percentage) = line_str.split(':') | ||||
| 				job_state.Percent = round( | ||||
| 					float(percentage.strip()[:-1]), 1) | ||||
|  | ||||
| 				# While the move is in progress we need to periodically update | ||||
| 				# the state to reflect where everything is at. | ||||
| 				cfg.load() | ||||
| 		except ValueError: | ||||
| 			log_error("Trying to parse percentage which failed for %s" % | ||||
| 				line_str) | ||||
|  | ||||
| 	out = process.communicate() | ||||
|  | ||||
| 	with meta.lock: | ||||
| 		meta.ended = time.time() | ||||
| 		meta.ec = process.returncode | ||||
| 		meta.stderr_txt = out[1] | ||||
|  | ||||
| 	if process.returncode == 0: | ||||
| 		job_state.Percent = 100 | ||||
| 	else: | ||||
| @@ -158,6 +138,5 @@ def _run_cmd(req): | ||||
|  | ||||
|  | ||||
| def cmd_runner(request): | ||||
| 	t = threading.Thread(target=_run_cmd, args=(request,), | ||||
| 							name="cmd_runner %s" % str(request.method)) | ||||
| 	t = threading.Thread(target=_run_cmd, args=(request,)) | ||||
| 	t.start() | ||||
|   | ||||
| @@ -26,7 +26,7 @@ bus = None | ||||
| args = None | ||||
|  | ||||
| # Set to true if we are depending on external events for updates | ||||
| got_external_event = False | ||||
| ee = False | ||||
|  | ||||
| # Shared state variable across all processes | ||||
| run = multiprocessing.Value('i', 1) | ||||
| @@ -84,6 +84,3 @@ db = None | ||||
|  | ||||
| # lvm flight recorder | ||||
| blackbox = None | ||||
|  | ||||
| # RequestEntry ctor | ||||
| create_request_entry = None | ||||
|   | ||||
| @@ -16,7 +16,7 @@ import traceback | ||||
| import os | ||||
|  | ||||
| from lvmdbusd import cfg | ||||
| from lvmdbusd.utils import pv_dest_ranges, log_debug, log_error, add_no_notify | ||||
| from lvmdbusd.utils import pv_dest_ranges, log_debug, log_error | ||||
| from lvmdbusd.lvm_shell_proxy import LVMShellProxy | ||||
|  | ||||
| try: | ||||
| @@ -37,7 +37,6 @@ cmd_lock = threading.RLock() | ||||
| class LvmExecutionMeta(object): | ||||
|  | ||||
| 	def __init__(self, start, ended, cmd, ec, stdout_txt, stderr_txt): | ||||
| 		self.lock = threading.RLock() | ||||
| 		self.start = start | ||||
| 		self.ended = ended | ||||
| 		self.cmd = cmd | ||||
| @@ -46,13 +45,12 @@ class LvmExecutionMeta(object): | ||||
| 		self.stderr_txt = stderr_txt | ||||
|  | ||||
| 	def __str__(self): | ||||
| 		with self.lock: | ||||
| 			return "EC= %d for %s\n" \ | ||||
| 				"STARTED: %f, ENDED: %f\n" \ | ||||
| 				"STDOUT=%s\n" \ | ||||
| 				"STDERR=%s\n" % \ | ||||
| 				(self.ec, str(self.cmd), self.start, self.ended, self.stdout_txt, | ||||
| 				self.stderr_txt) | ||||
| 		return "EC= %d for %s\n" \ | ||||
| 			"STARTED: %f, ENDED: %f\n" \ | ||||
| 			"STDOUT=%s\n" \ | ||||
| 			"STDERR=%s\n" % \ | ||||
| 			(self.ec, str(self.cmd), self.start, self.ended, self.stdout_txt, | ||||
| 			self.stderr_txt) | ||||
|  | ||||
|  | ||||
| class LvmFlightRecorder(object): | ||||
| @@ -95,7 +93,6 @@ def call_lvm(command, debug=False): | ||||
| 	# Prepend the full lvm executable so that we can run different versions | ||||
| 	# in different locations on the same box | ||||
| 	command.insert(0, cfg.LVM_CMD) | ||||
| 	command = add_no_notify(command) | ||||
|  | ||||
| 	process = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True, | ||||
| 					env=os.environ) | ||||
| @@ -281,7 +278,7 @@ 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']) | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	pv_dest_ranges(cmd, pv_dests) | ||||
| 	return call(cmd) | ||||
|  | ||||
| @@ -298,7 +295,20 @@ def vg_lv_snapshot(vg_name, snapshot_options, name, size_bytes): | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool): | ||||
| def vg_lv_create_linear(vg_name, create_options, name, size_bytes, thin_pool): | ||||
| 	cmd = ['lvcreate'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
|  | ||||
| 	if not thin_pool: | ||||
| 		cmd.extend(['--size', str(size_bytes) + 'B']) | ||||
| 	else: | ||||
| 		cmd.extend(['--thin', '--size', str(size_bytes) + 'B']) | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| def vg_lv_create_striped(vg_name, create_options, name, size_bytes, | ||||
| 							num_stripes, stripe_size_kb, thin_pool): | ||||
| 	cmd = ['lvcreate'] | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
|  | ||||
| @@ -307,19 +317,6 @@ def _vg_lv_create_common_cmd(create_options, size_bytes, thin_pool): | ||||
| 	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: | ||||
| @@ -344,7 +341,7 @@ def _vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes, | ||||
| 	if stripe_size_kb != 0: | ||||
| 		cmd.extend(['--stripesize', str(stripe_size_kb)]) | ||||
|  | ||||
| 	cmd.extend(['--name', name, vg_name, '--yes']) | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| @@ -365,7 +362,7 @@ def vg_lv_create_mirror( | ||||
| 	cmd.extend(['--type', 'mirror']) | ||||
| 	cmd.extend(['--mirrors', str(num_copies)]) | ||||
| 	cmd.extend(['--size', str(size_bytes) + 'B']) | ||||
| 	cmd.extend(['--name', name, vg_name, '--yes']) | ||||
| 	cmd.extend(['--name', name, vg_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| @@ -419,7 +416,7 @@ 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']) | ||||
| 	cmd.extend(['--name', name, lv_full_name]) | ||||
| 	return call(cmd) | ||||
|  | ||||
|  | ||||
| @@ -555,7 +552,7 @@ def pv_resize(device, size_bytes, create_options): | ||||
| 	cmd.extend(options_to_cli_args(create_options)) | ||||
|  | ||||
| 	if size_bytes != 0: | ||||
| 		cmd.extend(['--yes', '--setphysicalvolumesize', str(size_bytes) + 'B']) | ||||
| 		cmd.extend(['--setphysicalvolumesize', str(size_bytes) + 'B']) | ||||
|  | ||||
| 	cmd.extend([device]) | ||||
| 	return call(cmd) | ||||
| @@ -620,10 +617,10 @@ def vg_reduce(vg_name, missing, pv_devices, reduce_options): | ||||
| 	cmd = ['vgreduce'] | ||||
| 	cmd.extend(options_to_cli_args(reduce_options)) | ||||
|  | ||||
| 	if len(pv_devices) == 0: | ||||
| 		cmd.append('--all') | ||||
| 	if missing: | ||||
| 		cmd.append('--removemissing') | ||||
| 	elif len(pv_devices) == 0: | ||||
| 		cmd.append('--all') | ||||
|  | ||||
| 	cmd.append(vg_name) | ||||
| 	cmd.extend(pv_devices) | ||||
|   | ||||
| @@ -82,10 +82,10 @@ class StateUpdate(object): | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def update_thread(obj): | ||||
| 		queued_requests = [] | ||||
| 		while cfg.run.value != 0: | ||||
| 			# noinspection PyBroadException | ||||
| 			try: | ||||
| 				queued_requests = [] | ||||
| 				refresh = True | ||||
| 				emit_signal = True | ||||
| 				cache_refresh = True | ||||
| @@ -96,7 +96,7 @@ class StateUpdate(object): | ||||
| 					wait = not obj.deferred | ||||
| 					obj.deferred = False | ||||
|  | ||||
| 				if len(queued_requests) == 0 and wait: | ||||
| 				if wait: | ||||
| 					queued_requests.append(obj.queue.get(True, 2)) | ||||
|  | ||||
| 				# Ok we have one or the deferred queue has some, | ||||
| @@ -131,17 +131,11 @@ class StateUpdate(object): | ||||
| 				for i in queued_requests: | ||||
| 					i.set_result(num_changes) | ||||
|  | ||||
| 				# Only clear out the requests after we have given them a result | ||||
| 				# otherwise we can orphan the waiting threads and they never | ||||
| 				# wake up if we get an exception | ||||
| 				queued_requests = [] | ||||
|  | ||||
| 			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() | ||||
| @@ -152,8 +146,7 @@ class StateUpdate(object): | ||||
| 		load(refresh=False, emit_signal=False, need_main_thread=False) | ||||
|  | ||||
| 		self.thread = threading.Thread(target=StateUpdate.update_thread, | ||||
| 										args=(self,), | ||||
| 										name="StateUpdate.update_thread") | ||||
| 										args=(self,)) | ||||
|  | ||||
| 	def load(self, refresh=True, emit_signal=True, cache_refresh=True, | ||||
| 					log=True, need_main_thread=True): | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| from .automatedproperties import AutomatedProperties | ||||
| from .utils import job_obj_path_generate, mt_async_call | ||||
| from .utils import job_obj_path_generate, mt_async_result, mt_run_no_wait | ||||
| from . import cfg | ||||
| from .cfg import JOB_INTERFACE | ||||
| import dbus | ||||
| @@ -30,7 +30,7 @@ class WaitingClient(object): | ||||
| 				# Remove ourselves from waiting client | ||||
| 				wc.job_state.remove_waiting_client(wc) | ||||
| 				wc.timer_id = -1 | ||||
| 				mt_async_call(wc.cb, wc.job_state.Complete) | ||||
| 				mt_async_result(wc.cb, wc.job_state.Complete) | ||||
| 				wc.job_state = None | ||||
|  | ||||
| 	def __init__(self, job_state, tmo, cb, cbe): | ||||
| @@ -55,7 +55,7 @@ class WaitingClient(object): | ||||
| 					GLib.source_remove(self.timer_id) | ||||
| 					self.timer_id = -1 | ||||
|  | ||||
| 				mt_async_call(self.cb, self.job_state.Complete) | ||||
| 				mt_async_result(self.cb, self.job_state.Complete) | ||||
| 				self.job_state = None | ||||
|  | ||||
|  | ||||
| @@ -188,7 +188,7 @@ class Job(AutomatedProperties): | ||||
| 	@Complete.setter | ||||
| 	def Complete(self, value): | ||||
| 		self.state.Complete = value | ||||
| 		mt_async_call(Job._signal_complete, self) | ||||
| 		mt_run_no_wait(Job._signal_complete, self) | ||||
|  | ||||
| 	@property | ||||
| 	def GetError(self): | ||||
|   | ||||
| @@ -272,26 +272,6 @@ class LvCommon(AutomatedProperties): | ||||
| 		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', | ||||
| @@ -428,10 +408,24 @@ class Lv(LvCommon): | ||||
| 	@staticmethod | ||||
| 	def _remove(lv_uuid, lv_name, remove_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		LvCommon.validate_dbus_object(lv_uuid, lv_name) | ||||
| 		# Remove the LV, if successful then remove from the model | ||||
| 		rc, out, err = cmdhandler.lv_remove(lv_name, remove_options) | ||||
| 		LvCommon.handle_execute(rc, out, err) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			# Remove the LV, if successful then remove from the model | ||||
| 			rc, out, err = cmdhandler.lv_remove(lv_name, remove_options) | ||||
|  | ||||
| 			if rc == 0: | ||||
| 				cfg.load() | ||||
| 			else: | ||||
| 				# Need to work on error handling, need consistent | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					LV_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				LV_INTERFACE, | ||||
| 				'LV with uuid %s and name %s not present!' % | ||||
| 				(lv_uuid, lv_name)) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -449,11 +443,24 @@ class Lv(LvCommon): | ||||
| 	@staticmethod | ||||
| 	def _rename(lv_uuid, lv_name, new_name, rename_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		LvCommon.validate_dbus_object(lv_uuid, lv_name) | ||||
| 		# Rename the logical volume | ||||
| 		rc, out, err = cmdhandler.lv_rename(lv_name, new_name, | ||||
| 											rename_options) | ||||
| 		LvCommon.handle_execute(rc, out, err) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			# Rename the logical volume | ||||
| 			rc, out, err = cmdhandler.lv_rename(lv_name, new_name, | ||||
| 												rename_options) | ||||
| 			if rc == 0: | ||||
| 				cfg.load() | ||||
| 			else: | ||||
| 				# Need to work on error handling, need consistent | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					LV_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				LV_INTERFACE, | ||||
| 				'LV with uuid %s and name %s not present!' % | ||||
| 				(lv_uuid, lv_name)) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -493,21 +500,32 @@ class Lv(LvCommon): | ||||
| 	def _snap_shot(lv_uuid, lv_name, name, optional_size, | ||||
| 			snapshot_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name) | ||||
| 		# If you specify a size you get a 'thick' snapshot even if | ||||
| 		# it is a thin lv | ||||
| 		if not dbo.IsThinVolume: | ||||
| 			if optional_size == 0: | ||||
| 				space = dbo.SizeBytes / 80 | ||||
| 				remainder = space % 512 | ||||
| 				optional_size = space + 512 - remainder | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name) | ||||
|  | ||||
| 		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) | ||||
| 		if dbo: | ||||
| 			# If you specify a size you get a 'thick' snapshot even if | ||||
| 			# it is a thin lv | ||||
| 			if not dbo.IsThinVolume: | ||||
| 				if optional_size == 0: | ||||
| 					space = dbo.SizeBytes / 80 | ||||
| 					remainder = space % 512 | ||||
| 					optional_size = space + 512 - remainder | ||||
|  | ||||
| 			rc, out, err = cmdhandler.vg_lv_snapshot( | ||||
| 				lv_name, snapshot_options, name, optional_size) | ||||
| 			if rc == 0: | ||||
| 				cfg.load() | ||||
| 				full_name = "%s/%s" % (dbo.vg_name_lookup(), name) | ||||
| 				return cfg.om.get_object_path_by_lvm_id(full_name) | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					LV_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				LV_INTERFACE, | ||||
| 				'LV with uuid %s and name %s not present!' % | ||||
| 				(lv_uuid, lv_name)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=LV_INTERFACE, | ||||
| @@ -530,24 +548,38 @@ class Lv(LvCommon): | ||||
| 				resize_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		pv_dests = [] | ||||
| 		dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name) | ||||
|  | ||||
| 		# If we have PVs, verify them | ||||
| 		if len(pv_dests_and_ranges): | ||||
| 			for pr in pv_dests_and_ranges: | ||||
| 				pv_dbus_obj = cfg.om.get_object_by_path(pr[0]) | ||||
| 				if not pv_dbus_obj: | ||||
| 					raise dbus.exceptions.DBusException( | ||||
| 						LV_INTERFACE, | ||||
| 						'PV Destination (%s) not found' % pr[0]) | ||||
| 		if dbo: | ||||
| 			# If we have PVs, verify them | ||||
| 			if len(pv_dests_and_ranges): | ||||
| 				for pr in pv_dests_and_ranges: | ||||
| 					pv_dbus_obj = cfg.om.get_object_by_path(pr[0]) | ||||
| 					if not pv_dbus_obj: | ||||
| 						raise dbus.exceptions.DBusException( | ||||
| 							LV_INTERFACE, | ||||
| 							'PV Destination (%s) not found' % pr[0]) | ||||
|  | ||||
| 				pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2])) | ||||
| 					pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2])) | ||||
|  | ||||
| 		size_change = new_size_bytes - dbo.SizeBytes | ||||
| 		rc, out, err = cmdhandler.lv_resize(dbo.lvm_id, size_change, | ||||
| 											pv_dests, resize_options) | ||||
| 		LvCommon.handle_execute(rc, out, err) | ||||
| 		return "/" | ||||
| 			size_change = new_size_bytes - dbo.SizeBytes | ||||
|  | ||||
| 			rc, out, err = cmdhandler.lv_resize(dbo.lvm_id, size_change, | ||||
| 												pv_dests, resize_options) | ||||
|  | ||||
| 			if rc == 0: | ||||
| 				# Refresh what's changed | ||||
| 				cfg.load() | ||||
| 				return "/" | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					LV_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				LV_INTERFACE, | ||||
| 				'LV with uuid %s and name %s not present!' % | ||||
| 				(lv_uuid, lv_name)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=LV_INTERFACE, | ||||
| @@ -580,11 +612,23 @@ class Lv(LvCommon): | ||||
| 	def _lv_activate_deactivate(uuid, lv_name, activate, control_flags, | ||||
| 								options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		LvCommon.validate_dbus_object(uuid, lv_name) | ||||
| 		rc, out, err = cmdhandler.activate_deactivate( | ||||
| 			'lvchange', lv_name, activate, control_flags, options) | ||||
| 		LvCommon.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, lv_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.activate_deactivate( | ||||
| 				'lvchange', lv_name, activate, control_flags, options) | ||||
| 			if rc == 0: | ||||
| 				dbo.refresh() | ||||
| 				return '/' | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					LV_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				LV_INTERFACE, | ||||
| 				'LV with uuid %s and name %s not present!' % | ||||
| 				(uuid, lv_name)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=LV_INTERFACE, | ||||
| @@ -616,11 +660,25 @@ class Lv(LvCommon): | ||||
| 	@staticmethod | ||||
| 	def _add_rm_tags(uuid, lv_name, tags_add, tags_del, tag_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		LvCommon.validate_dbus_object(uuid, lv_name) | ||||
| 		rc, out, err = cmdhandler.lv_tag( | ||||
| 			lv_name, tags_add, tags_del, tag_options) | ||||
| 		LvCommon.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, lv_name) | ||||
|  | ||||
| 		if dbo: | ||||
|  | ||||
| 			rc, out, err = cmdhandler.lv_tag( | ||||
| 				lv_name, tags_add, tags_del, tag_options) | ||||
| 			if rc == 0: | ||||
| 				dbo.refresh() | ||||
| 				return '/' | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					LV_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				LV_INTERFACE, | ||||
| 				'LV with uuid %s and name %s not present!' % | ||||
| 				(uuid, lv_name)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=LV_INTERFACE, | ||||
| @@ -678,13 +736,24 @@ class LvThinPool(Lv): | ||||
| 	@staticmethod | ||||
| 	def _lv_create(lv_uuid, lv_name, name, size_bytes, create_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(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) | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.lv_lv_create( | ||||
| 				lv_name, create_options, name, size_bytes) | ||||
| 			if rc == 0: | ||||
| 				full_name = "%s/%s" % (dbo.vg_name_lookup(), name) | ||||
| 				cfg.load() | ||||
| 				return cfg.om.get_object_path_by_lvm_id(full_name) | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					LV_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				LV_INTERFACE, | ||||
| 				'LV with uuid %s and name %s not present!' % | ||||
| 				(lv_uuid, lv_name)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=THIN_POOL_INTERFACE, | ||||
| @@ -721,13 +790,14 @@ class LvCachePool(Lv): | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _cache_lv(lv_uuid, lv_name, lv_object_path, cache_options): | ||||
|  | ||||
| 		# Make sure we have a dbus object representing cache pool | ||||
| 		dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name) | ||||
|  | ||||
| 		# Make sure we have dbus object representing lv to cache | ||||
| 		lv_to_cache = cfg.om.get_object_by_path(lv_object_path) | ||||
|  | ||||
| 		if lv_to_cache: | ||||
| 		if dbo and lv_to_cache: | ||||
| 			fcn = lv_to_cache.lv_full_name() | ||||
| 			rc, out, err = cmdhandler.lv_cache_lv( | ||||
| 				dbo.lv_full_name(), fcn, cache_options) | ||||
| @@ -739,14 +809,22 @@ class LvCachePool(Lv): | ||||
| 				cfg.load() | ||||
|  | ||||
| 				lv_converted = cfg.om.get_object_path_by_lvm_id(fcn) | ||||
|  | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					LV_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				LV_INTERFACE, 'LV to cache with object path %s not present!' % | ||||
| 				lv_object_path) | ||||
| 			msg = "" | ||||
| 			if not dbo: | ||||
| 				dbo += 'CachePool LV with uuid %s and name %s not present!' % \ | ||||
| 					(lv_uuid, lv_name) | ||||
|  | ||||
| 			if not lv_to_cache: | ||||
| 				dbo += 'LV to cache with object path %s not present!' % \ | ||||
| 					(lv_object_path) | ||||
|  | ||||
| 			raise dbus.exceptions.DBusException(LV_INTERFACE, msg) | ||||
| 		return lv_converted | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -777,25 +855,31 @@ class LvCacheLv(Lv): | ||||
| 	@staticmethod | ||||
| 	def _detach_lv(lv_uuid, lv_name, detach_options, destroy_cache): | ||||
| 		# Make sure we have a dbus object representing cache pool | ||||
| 		dbo = LvCommon.validate_dbus_object(lv_uuid, lv_name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name) | ||||
|  | ||||
| 		# Get current cache name | ||||
| 		cache_pool = cfg.om.get_object_by_path(dbo.CachePool) | ||||
| 		if dbo: | ||||
|  | ||||
| 		rc, out, err = cmdhandler.lv_detach_cache( | ||||
| 			dbo.lv_full_name(), detach_options, destroy_cache) | ||||
| 		if rc == 0: | ||||
| 			# The cache pool gets removed as hidden and put back to | ||||
| 			# visible, so lets delete | ||||
| 			mt_remove_dbus_objects((cache_pool, dbo)) | ||||
| 			cfg.load() | ||||
| 			# Get current cache name | ||||
| 			cache_pool = cfg.om.get_object_by_path(dbo.CachePool) | ||||
|  | ||||
| 			uncached_lv_path = cfg.om.get_object_path_by_lvm_id(lv_name) | ||||
| 			rc, out, err = cmdhandler.lv_detach_cache( | ||||
| 				dbo.lv_full_name(), detach_options, destroy_cache) | ||||
| 			if rc == 0: | ||||
| 				# The cache pool gets removed as hidden and put back to | ||||
| 				# visible, so lets delete | ||||
| 				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)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				LV_INTERFACE, | ||||
| 				'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 				'LV with uuid %s and name %s not present!' % | ||||
| 				(lv_uuid, lv_name)) | ||||
| 		return uncached_lv_path | ||||
|  | ||||
| 	@dbus.service.method( | ||||
|   | ||||
							
								
								
									
										6
									
								
								daemons/lvmdbusd/lvm_shell_proxy.py.in → daemons/lvmdbusd/lvm_shell_proxy.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										6
									
								
								daemons/lvmdbusd/lvm_shell_proxy.py.in → daemons/lvmdbusd/lvm_shell_proxy.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,4 +1,4 @@ | ||||
| #!@PYTHON3@ | ||||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| # Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved. | ||||
| # | ||||
| @@ -29,7 +29,7 @@ except ImportError: | ||||
| 
 | ||||
| 
 | ||||
| from lvmdbusd.cfg import LVM_CMD | ||||
| from lvmdbusd.utils import log_debug, log_error, add_no_notify | ||||
| from lvmdbusd.utils import log_debug, log_error | ||||
| 
 | ||||
| SHELL_PROMPT = "lvm> " | ||||
| 
 | ||||
| @@ -206,8 +206,6 @@ class LVMShellProxy(object): | ||||
| 				self.lvm_shell.returncode, | ||||
| 				"Underlying lvm shell process is not present!") | ||||
| 
 | ||||
| 		argv = add_no_notify(argv) | ||||
| 
 | ||||
| 		# create the command string | ||||
| 		cmd = " ".join(_quote_arg(arg) for arg in argv) | ||||
| 		cmd += "\n" | ||||
							
								
								
									
										41
									
								
								daemons/lvmdbusd/lvmdb.py.in → daemons/lvmdbusd/lvmdb.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										41
									
								
								daemons/lvmdbusd/lvmdb.py.in → daemons/lvmdbusd/lvmdb.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,4 +1,4 @@ | ||||
| #!@PYTHON3@ | ||||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| # Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved. | ||||
| # | ||||
| @@ -68,20 +68,6 @@ class DataStore(object): | ||||
| 		else: | ||||
| 			table[key] = record | ||||
| 
 | ||||
| 	@staticmethod | ||||
| 	def _pvs_parse_common(c_pvs, c_pvs_in_vgs, c_lookup): | ||||
| 		for p in c_pvs.values(): | ||||
| 			# Capture which PVs are associated with which VG | ||||
| 			if p['vg_uuid'] not in c_pvs_in_vgs: | ||||
| 				c_pvs_in_vgs[p['vg_uuid']] = [] | ||||
| 
 | ||||
| 			if p['vg_name']: | ||||
| 				c_pvs_in_vgs[p['vg_uuid']].append( | ||||
| 					(p['pv_name'], p['pv_uuid'])) | ||||
| 
 | ||||
| 			# Lookup for translating between /dev/<name> and pv uuid | ||||
| 			c_lookup[p['pv_name']] = p['pv_uuid'] | ||||
| 
 | ||||
| 	@staticmethod | ||||
| 	def _parse_pvs(_pvs): | ||||
| 		pvs = sorted(_pvs, key=lambda pk: pk['pv_name']) | ||||
| @@ -95,7 +81,18 @@ class DataStore(object): | ||||
| 				c_pvs, p['pv_uuid'], p, | ||||
| 				['pvseg_start', 'pvseg_size', 'segtype']) | ||||
| 
 | ||||
| 		DataStore._pvs_parse_common(c_pvs, c_pvs_in_vgs, c_lookup) | ||||
| 		for p in c_pvs.values(): | ||||
| 			# Capture which PVs are associated with which VG | ||||
| 			if p['vg_uuid'] not in c_pvs_in_vgs: | ||||
| 				c_pvs_in_vgs[p['vg_uuid']] = [] | ||||
| 
 | ||||
| 			if p['vg_name']: | ||||
| 				c_pvs_in_vgs[p['vg_uuid']].append( | ||||
| 					(p['pv_name'], p['pv_uuid'])) | ||||
| 
 | ||||
| 			# Lookup for translating between /dev/<name> and pv uuid | ||||
| 			c_lookup[p['pv_name']] = p['pv_uuid'] | ||||
| 
 | ||||
| 		return c_pvs, c_lookup, c_pvs_in_vgs | ||||
| 
 | ||||
| 	@staticmethod | ||||
| @@ -135,7 +132,17 @@ class DataStore(object): | ||||
| 						i['pvseg_size'] = i['pv_pe_count'] | ||||
| 						i['segtype'] = 'free' | ||||
| 
 | ||||
| 		DataStore._pvs_parse_common(c_pvs, c_pvs_in_vgs, c_lookup) | ||||
| 		for p in c_pvs.values(): | ||||
| 			# Capture which PVs are associated with which VG | ||||
| 			if p['vg_uuid'] not in c_pvs_in_vgs: | ||||
| 				c_pvs_in_vgs[p['vg_uuid']] = [] | ||||
| 
 | ||||
| 			if p['vg_name']: | ||||
| 				c_pvs_in_vgs[p['vg_uuid']].append( | ||||
| 					(p['pv_name'], p['pv_uuid'])) | ||||
| 
 | ||||
| 			# Lookup for translating between /dev/<name> and pv uuid | ||||
| 			c_lookup[p['pv_name']] = p['pv_uuid'] | ||||
| 
 | ||||
| 		return c_pvs, c_lookup, c_pvs_in_vgs | ||||
| 
 | ||||
							
								
								
									
										2
									
								
								daemons/lvmdbusd/lvmdbusd.in → daemons/lvmdbusd/lvmdbusd
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										2
									
								
								daemons/lvmdbusd/lvmdbusd.in → daemons/lvmdbusd/lvmdbusd
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,4 +1,4 @@ | ||||
| #!@PYTHON3@ | ||||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| # Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved. | ||||
| # | ||||
| @@ -30,7 +30,6 @@ import argparse | ||||
| import os | ||||
| import sys | ||||
| from .cmdhandler import LvmFlightRecorder | ||||
| from .request import RequestEntry | ||||
|  | ||||
|  | ||||
| class Lvm(objectmanager.ObjectManager): | ||||
| @@ -63,24 +62,6 @@ def check_bb_size(value): | ||||
| 	return v | ||||
|  | ||||
|  | ||||
| def install_signal_handlers(): | ||||
| 	# Because of the glib main loop stuff the python signal handler code is | ||||
| 	# apparently not usable and we need to use the glib calls instead | ||||
| 	signal_add = None | ||||
|  | ||||
| 	if hasattr(GLib, 'unix_signal_add'): | ||||
| 		signal_add = GLib.unix_signal_add | ||||
| 	elif hasattr(GLib, 'unix_signal_add_full'): | ||||
| 		signal_add = GLib.unix_signal_add_full | ||||
|  | ||||
| 	if signal_add: | ||||
| 		signal_add(GLib.PRIORITY_HIGH, signal.SIGHUP, utils.handler, signal.SIGHUP) | ||||
| 		signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, utils.handler, signal.SIGINT) | ||||
| 		signal_add(GLib.PRIORITY_HIGH, signal.SIGUSR1, utils.handler, signal.SIGUSR1) | ||||
| 	else: | ||||
| 		log_error("GLib.unix_signal_[add|add_full] are NOT available!") | ||||
|  | ||||
|  | ||||
| def main(): | ||||
| 	start = time.time() | ||||
| 	# Add simple command line handling | ||||
| @@ -116,7 +97,6 @@ def main(): | ||||
| 	os.environ["LC_ALL"] = "C" | ||||
|  | ||||
| 	cfg.args = parser.parse_args() | ||||
| 	cfg.create_request_entry = RequestEntry | ||||
|  | ||||
| 	# We create a flight recorder in cmdhandler too, but we replace it here | ||||
| 	# as the user may be specifying a different size.  The default one in | ||||
| @@ -130,7 +110,12 @@ def main(): | ||||
| 	# List of threads that we start up | ||||
| 	thread_list = [] | ||||
|  | ||||
| 	install_signal_handlers() | ||||
| 	# Install signal handlers | ||||
| 	for s in [signal.SIGHUP, signal.SIGINT]: | ||||
| 		try: | ||||
| 			signal.signal(s, utils.handler) | ||||
| 		except RuntimeError: | ||||
| 			pass | ||||
|  | ||||
| 	dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) | ||||
| 	dbus.mainloop.glib.threads_init() | ||||
| @@ -151,8 +136,7 @@ def main(): | ||||
|  | ||||
| 	# Using a thread to process requests, we cannot hang the dbus library | ||||
| 	# thread that is handling the dbus interface | ||||
| 	thread_list.append(threading.Thread(target=process_request, | ||||
| 										name='process_request')) | ||||
| 	thread_list.append(threading.Thread(target=process_request)) | ||||
|  | ||||
| 	# Have a single thread handling updating lvm and the dbus model so we | ||||
| 	# don't have multiple threads doing this as the same time | ||||
| @@ -160,6 +144,7 @@ def main(): | ||||
| 	thread_list.append(updater.thread) | ||||
|  | ||||
| 	cfg.load = updater.load | ||||
| 	cfg.event = updater.event | ||||
|  | ||||
| 	cfg.loop = GLib.MainLoop() | ||||
|  | ||||
| @@ -190,7 +175,5 @@ def main(): | ||||
| 			for thread in thread_list: | ||||
| 				thread.join() | ||||
| 	except KeyboardInterrupt: | ||||
| 		# If we are unable to register signal handler, we will end up here when | ||||
| 		# the service gets a ^C or a kill -2 <parent pid> | ||||
| 		utils.handler(signal.SIGINT) | ||||
| 		utils.handler(signal.SIGINT, None) | ||||
| 	return 0 | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| from .automatedproperties import AutomatedProperties | ||||
|  | ||||
| from . import utils | ||||
| @@ -29,16 +30,6 @@ class Manager(AutomatedProperties): | ||||
| 	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): | ||||
|  | ||||
| @@ -47,11 +38,18 @@ class Manager(AutomatedProperties): | ||||
| 		pv = cfg.om.get_object_path_by_uuid_lvm_id(device, device) | ||||
| 		if pv: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				MANAGER_INTERFACE, "PV %s Already exists!" % device) | ||||
| 				MANAGER_INTERFACE, "PV Already exists!") | ||||
|  | ||||
| 		rc, out, err = cmdhandler.pv_create(create_options, [device]) | ||||
| 		Manager.handle_execute(rc, out, err) | ||||
| 		return cfg.om.get_object_path_by_lvm_id(device) | ||||
| 		if rc == 0: | ||||
| 			cfg.load() | ||||
| 			created_pv = cfg.om.get_object_path_by_lvm_id(device) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				MANAGER_INTERFACE, | ||||
| 				'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 		return created_pv | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
| @@ -78,8 +76,14 @@ class Manager(AutomatedProperties): | ||||
| 					MANAGER_INTERFACE, 'object path = %s not found' % p) | ||||
|  | ||||
| 		rc, out, err = cmdhandler.vg_create(create_options, pv_devices, name) | ||||
| 		Manager.handle_execute(rc, out, err) | ||||
| 		return cfg.om.get_object_path_by_lvm_id(name) | ||||
|  | ||||
| 		if rc == 0: | ||||
| 			cfg.load() | ||||
| 			return cfg.om.get_object_path_by_lvm_id(name) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				MANAGER_INTERFACE, | ||||
| 				'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
| @@ -131,28 +135,11 @@ class Manager(AutomatedProperties): | ||||
| 		r = RequestEntry(-1, Manager._refresh, (), cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE) | ||||
| 	def FlightRecorderDump(self): | ||||
| 		""" | ||||
| 		Dump the flight recorder to syslog | ||||
| 		""" | ||||
| 		cfg.blackbox.dump() | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _lookup_by_lvm_id(key): | ||||
| 		p = cfg.om.get_object_path_by_uuid_lvm_id(key, key) | ||||
| 		if not p: | ||||
| 			p = '/' | ||||
| 		utils.log_debug('LookUpByLvmId: key = %s, result = %s' % (key, p)) | ||||
| 		return p | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
| 		in_signature='s', | ||||
| 		out_signature='o', | ||||
| 		async_callbacks=('cb', 'cbe')) | ||||
| 	def LookUpByLvmId(self, key, cb, cbe): | ||||
| 		out_signature='o') | ||||
| 	def LookUpByLvmId(self, key): | ||||
| 		""" | ||||
| 		Given a lvm id in one of the forms: | ||||
|  | ||||
| @@ -166,8 +153,10 @@ class Manager(AutomatedProperties): | ||||
| 		: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) | ||||
| 		p = cfg.om.get_object_path_by_uuid_lvm_id(key, key) | ||||
| 		if p: | ||||
| 			return p | ||||
| 		return '/' | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _use_lvm_shell(yes_no): | ||||
| @@ -183,33 +172,25 @@ class Manager(AutomatedProperties): | ||||
| 		:param yes_no: | ||||
| 		:param cb:	dbus python call back parameter, not client visible | ||||
| 		:param cbe:	dbus python error call back parameter, not client visible | ||||
| 		:return: Boolean | ||||
| 		:return: Nothing | ||||
| 		""" | ||||
| 		r = RequestEntry(-1, Manager._use_lvm_shell, (yes_no,), cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _external_event(command): | ||||
| 		utils.log_debug("Processing _external_event= %s" % command, | ||||
| 							'bg_black', 'fg_orange') | ||||
| 		cfg.load() | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
| 		in_signature='s', out_signature='i') | ||||
| 	def ExternalEvent(self, command): | ||||
| 		utils.log_debug("ExternalEvent %s" % command) | ||||
|  | ||||
| 		# If a user didn't explicitly specify udev, we will turn it off now. | ||||
| 		if not cfg.args.use_udev: | ||||
| 			if udevwatch.remove(): | ||||
| 				utils.log_debug("ExternalEvent received, disabling " | ||||
| 								"udev monitoring") | ||||
| 				# We are dependent on external events now to stay current! | ||||
| 				cfg.got_external_event = True | ||||
|  | ||||
| 		r = RequestEntry( | ||||
| 			-1, Manager._external_event, (command,), None, None, False) | ||||
| 		cfg.worker_q.put(r) | ||||
| 				cfg.ee = True | ||||
| 		utils.log_debug("ExternalEvent %s" % command) | ||||
| 		cfg.event() | ||||
| 		return dbus.Int32(0) | ||||
|  | ||||
| 	@staticmethod | ||||
| @@ -219,8 +200,15 @@ class Manager(AutomatedProperties): | ||||
| 			activate, cache, device_path, | ||||
| 			major_minor, scan_options) | ||||
|  | ||||
| 		Manager.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
| 		if rc == 0: | ||||
| 			# This could potentially change the state quite a bit, so lets | ||||
| 			# update everything to be safe | ||||
| 			cfg.load() | ||||
| 			return '/' | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				MANAGER_INTERFACE, | ||||
| 				'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=MANAGER_INTERFACE, | ||||
|   | ||||
| @@ -32,12 +32,14 @@ class ObjectManager(AutomatedProperties): | ||||
| 		self._id_to_object_path = {} | ||||
| 		self.rlock = threading.RLock() | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _get_managed_objects(obj): | ||||
| 		with obj.rlock: | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface="org.freedesktop.DBus.ObjectManager", | ||||
| 		out_signature='a{oa{sa{sv}}}') | ||||
| 	def GetManagedObjects(self): | ||||
| 		with self.rlock: | ||||
| 			rc = {} | ||||
| 			try: | ||||
| 				for k, v in list(obj._objects.items()): | ||||
| 				for k, v in list(self._objects.items()): | ||||
| 					path, props = v[0].emit_data() | ||||
| 					rc[path] = props | ||||
| 			except Exception: | ||||
| @@ -45,14 +47,6 @@ class ObjectManager(AutomatedProperties): | ||||
| 				sys.exit(1) | ||||
| 			return rc | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface="org.freedesktop.DBus.ObjectManager", | ||||
| 		out_signature='a{oa{sa{sv}}}', async_callbacks=('cb', 'cbe')) | ||||
| 	def GetManagedObjects(self, cb, cbe): | ||||
| 		r = cfg.create_request_entry(-1, ObjectManager._get_managed_objects, | ||||
| 									(self, ), cb, cbe, False) | ||||
| 		cfg.worker_q.put(r) | ||||
|  | ||||
| 	def locked(self): | ||||
| 		""" | ||||
| 		If some external code need to run across a number of different | ||||
| @@ -223,9 +217,8 @@ class ObjectManager(AutomatedProperties): | ||||
| 		:param lvm_id: The lvm identifier | ||||
| 		""" | ||||
| 		with self.rlock: | ||||
| 			lookup_rc = self._id_lookup(lvm_id) | ||||
| 			if lookup_rc: | ||||
| 				return self.get_object_by_path(lookup_rc) | ||||
| 			if lvm_id in self._id_to_object_path: | ||||
| 				return self.get_object_by_path(self._id_to_object_path[lvm_id]) | ||||
| 			return None | ||||
|  | ||||
| 	def get_object_path_by_lvm_id(self, lvm_id): | ||||
| @@ -235,9 +228,8 @@ class ObjectManager(AutomatedProperties): | ||||
| 		:return: Object path or '/' if not found | ||||
| 		""" | ||||
| 		with self.rlock: | ||||
| 			lookup_rc = self._id_lookup(lvm_id) | ||||
| 			if lookup_rc: | ||||
| 				return lookup_rc | ||||
| 			if lvm_id in self._id_to_object_path: | ||||
| 				return self._id_to_object_path[lvm_id] | ||||
| 			return '/' | ||||
|  | ||||
| 	def _uuid_verify(self, path, uuid, lvm_id): | ||||
|   | ||||
| @@ -79,9 +79,7 @@ class PvState(State): | ||||
|  | ||||
| 		self.lv = self._lv_object_list(vg_name) | ||||
|  | ||||
| 		# It's possible to have a vg_name and no uuid with the main example | ||||
| 		# being when the vg_name == '[unknown]' | ||||
| 		if vg_uuid and vg_name: | ||||
| 		if vg_name: | ||||
| 			self.vg_path = cfg.om.get_object_path_by_uuid_lvm_id( | ||||
| 				vg_uuid, vg_name, vg_obj_path_generate) | ||||
| 		else: | ||||
| @@ -137,30 +135,23 @@ class Pv(AutomatedProperties): | ||||
| 	def _remove(pv_uuid, pv_name, remove_options): | ||||
| 		# Remove the PV, if successful then remove from the model | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Pv.validate_dbus_object(pv_uuid, pv_name) | ||||
| 		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: | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.pv_remove(pv_name, remove_options) | ||||
| 			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)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				PV_INTERFACE, | ||||
| 				'PV with uuid %s and name %s not present!' % | ||||
| 				(pv_uuid, pv_name)) | ||||
| 		return dbo | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=PV_INTERFACE, | ||||
| @@ -177,11 +168,22 @@ class Pv(AutomatedProperties): | ||||
| 	@staticmethod | ||||
| 	def _resize(pv_uuid, pv_name, new_size_bytes, resize_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Pv.validate_dbus_object(pv_uuid, pv_name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name) | ||||
|  | ||||
| 		rc, out, err = cmdhandler.pv_resize(pv_name, new_size_bytes, | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.pv_resize(pv_name, new_size_bytes, | ||||
| 												resize_options) | ||||
| 		Pv.handle_execute(rc, out, err) | ||||
| 			if rc == 0: | ||||
| 				cfg.load() | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					PV_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				PV_INTERFACE, | ||||
| 				'PV with uuid %s and name %s not present!' % | ||||
| 				(pv_uuid, pv_name)) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -199,10 +201,21 @@ class Pv(AutomatedProperties): | ||||
| 	@staticmethod | ||||
| 	def _allocation_enabled(pv_uuid, pv_name, yes_no, allocation_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Pv.validate_dbus_object(pv_uuid, pv_name) | ||||
| 		rc, out, err = cmdhandler.pv_allocatable( | ||||
| 			pv_name, yes_no, allocation_options) | ||||
| 		Pv.handle_execute(rc, out, err) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.pv_allocatable( | ||||
| 				pv_name, yes_no, allocation_options) | ||||
| 			if rc == 0: | ||||
| 				cfg.load() | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					PV_INTERFACE, 'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				PV_INTERFACE, | ||||
| 				'PV with uuid %s and name %s not present!' % | ||||
| 				(pv_uuid, pv_name)) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
|   | ||||
| @@ -13,12 +13,13 @@ from gi.repository import GLib | ||||
| from .job import Job | ||||
| from . import cfg | ||||
| import traceback | ||||
| from .utils import log_error, mt_async_call | ||||
| from .utils import log_error, mt_async_result | ||||
|  | ||||
|  | ||||
| class RequestEntry(object): | ||||
| 	def __init__(self, tmo, method, arguments, cb, cb_error, | ||||
| 			return_tuple=True, job_state=None): | ||||
| 		self.tmo = tmo | ||||
| 		self.method = method | ||||
| 		self.arguments = arguments | ||||
| 		self.cb = cb | ||||
| @@ -34,38 +35,31 @@ class RequestEntry(object): | ||||
| 		self._return_tuple = return_tuple | ||||
| 		self._job_state = job_state | ||||
|  | ||||
| 		if tmo < 0: | ||||
| 		if self.tmo < 0: | ||||
| 			# Client is willing to block forever | ||||
| 			pass | ||||
| 		elif tmo == 0: | ||||
| 			self._return_job() | ||||
| 		else: | ||||
| 			# Note: using 990 instead of 1000 for second to ms conversion to | ||||
| 			# account for overhead.  Goal is to return just before the | ||||
| 			# timeout amount has expired.  Better to be a little early than | ||||
| 			# late. | ||||
| 			self.timer_id = GLib.timeout_add( | ||||
| 				tmo * 990, RequestEntry._request_timeout, self) | ||||
| 			self.timer_id = GLib.timeout_add_seconds( | ||||
| 				tmo, RequestEntry._request_timeout, self) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _request_timeout(r): | ||||
| 		""" | ||||
| 		Method which gets called when the timer runs out! | ||||
| 		:param r:  RequestEntry which timed out | ||||
| 		:return: Result of timer_expired | ||||
| 		:return: Nothing | ||||
| 		""" | ||||
| 		return r.timer_expired() | ||||
| 		r.timer_expired() | ||||
|  | ||||
| 	def _return_job(self): | ||||
| 		# Return job is only called when we create a request object or when | ||||
| 		# we pop a timer.  In both cases we are running in the correct context | ||||
| 		# and do not need to schedule the call back in main context. | ||||
| 		self._job = Job(self, self._job_state) | ||||
| 		cfg.om.register_object(self._job, True) | ||||
| 		if self._return_tuple: | ||||
| 			self.cb(('/', self._job.dbus_object_path())) | ||||
| 			mt_async_result(self.cb, ('/', self._job.dbus_object_path())) | ||||
| 		else: | ||||
| 			self.cb(self._job.dbus_object_path()) | ||||
| 			mt_async_result(self.cb, self._job.dbus_object_path()) | ||||
|  | ||||
| 	def run_cmd(self): | ||||
| 		try: | ||||
| @@ -116,9 +110,9 @@ class RequestEntry(object): | ||||
| 				if error_rc == 0: | ||||
| 					if self.cb: | ||||
| 						if self._return_tuple: | ||||
| 							mt_async_call(self.cb, (result, '/')) | ||||
| 							mt_async_result(self.cb, (result, '/')) | ||||
| 						else: | ||||
| 							mt_async_call(self.cb, result) | ||||
| 							mt_async_result(self.cb, result) | ||||
| 				else: | ||||
| 					if self.cb_error: | ||||
| 						if not error_exception: | ||||
| @@ -129,7 +123,7 @@ class RequestEntry(object): | ||||
| 							else: | ||||
| 								error_exception = Exception(error_msg) | ||||
|  | ||||
| 						mt_async_call(self.cb_error, error_exception) | ||||
| 						mt_async_result(self.cb_error, error_exception) | ||||
| 			else: | ||||
| 				# We have a job and it's complete, indicate that it's done. | ||||
| 				self._job.Complete = True | ||||
|   | ||||
| @@ -10,41 +10,10 @@ | ||||
| import pyudev | ||||
| import threading | ||||
| from . import cfg | ||||
| from .request import RequestEntry | ||||
| from . import utils | ||||
|  | ||||
| observer = None | ||||
| observer_lock = threading.RLock() | ||||
|  | ||||
| _udev_lock = threading.RLock() | ||||
| _udev_count = 0 | ||||
|  | ||||
|  | ||||
| def udev_add(): | ||||
| 	global _udev_count | ||||
| 	with _udev_lock: | ||||
| 		if _udev_count == 0: | ||||
| 			_udev_count += 1 | ||||
|  | ||||
| 			# Place this on the queue so any other operations will sequence | ||||
| 			# behind it | ||||
| 			r = RequestEntry( | ||||
| 				-1, _udev_event, (), None, None, False) | ||||
| 			cfg.worker_q.put(r) | ||||
|  | ||||
|  | ||||
| def udev_complete(): | ||||
| 	global _udev_count | ||||
| 	with _udev_lock: | ||||
| 		if _udev_count > 0: | ||||
| 			_udev_count -= 1 | ||||
|  | ||||
|  | ||||
| def _udev_event(): | ||||
| 	utils.log_debug("Processing udev event") | ||||
| 	udev_complete() | ||||
| 	cfg.load() | ||||
|  | ||||
|  | ||||
| # noinspection PyUnusedLocal | ||||
| def filter_event(action, device): | ||||
| @@ -68,7 +37,7 @@ def filter_event(action, device): | ||||
| 		refresh = True | ||||
|  | ||||
| 	if refresh: | ||||
| 		udev_add() | ||||
| 		cfg.event() | ||||
|  | ||||
|  | ||||
| def add(): | ||||
|   | ||||
| @@ -20,8 +20,7 @@ from lvmdbusd import cfg | ||||
| # noinspection PyUnresolvedReferences | ||||
| from gi.repository import GLib | ||||
| import threading | ||||
| import traceback | ||||
| import signal | ||||
|  | ||||
|  | ||||
| STDOUT_TTY = os.isatty(sys.stdout.fileno()) | ||||
|  | ||||
| @@ -282,47 +281,12 @@ def log_error(msg, *attributes): | ||||
| 	_common_log(msg, *attributes) | ||||
|  | ||||
|  | ||||
| def dump_threads_stackframe(): | ||||
| 	ident_to_name = {} | ||||
|  | ||||
| 	for thread_object in threading.enumerate(): | ||||
| 		ident_to_name[thread_object.ident] = thread_object | ||||
|  | ||||
| 	stacks = [] | ||||
| 	for thread_ident, frame in sys._current_frames().items(): | ||||
| 		stack = traceback.format_list(traceback.extract_stack(frame)) | ||||
|  | ||||
| 		# There is a possibility that a thread gets created after we have | ||||
| 		# enumerated all threads, so this lookup table may be incomplete, so | ||||
| 		# account for this | ||||
| 		if thread_ident in ident_to_name: | ||||
| 			thread_name = ident_to_name[thread_ident].name | ||||
| 		else: | ||||
| 			thread_name = "unknown" | ||||
|  | ||||
| 		stacks.append("Thread: %s" % (thread_name)) | ||||
| 		stacks.append("".join(stack)) | ||||
|  | ||||
| 	log_error("Dumping thread stack frames!\n" + "\n".join(stacks)) | ||||
|  | ||||
|  | ||||
| # noinspection PyUnusedLocal | ||||
| def handler(signum): | ||||
| 	try: | ||||
| 		if signum == signal.SIGUSR1: | ||||
| 			dump_threads_stackframe() | ||||
| 		else: | ||||
| 			cfg.run.value = 0 | ||||
| 			log_debug('Exiting daemon with signal %d' % signum) | ||||
| 			if cfg.loop is not None: | ||||
| 				cfg.loop.quit() | ||||
| 	except: | ||||
| 		st = traceback.format_exc() | ||||
| 		log_error("signal handler: exception (logged, not reported!) \n %s" % st) | ||||
|  | ||||
| 	# It's important we report that we handled the exception for the exception | ||||
| 	# handler to continue to work, especially for signal 10 (SIGUSR1) | ||||
| 	return True | ||||
| def handler(signum, frame): | ||||
| 	cfg.run.value = 0 | ||||
| 	log_debug('Signal handler called with signal %d' % signum) | ||||
| 	if cfg.loop is not None: | ||||
| 		cfg.loop.quit() | ||||
|  | ||||
|  | ||||
| def pv_obj_path_generate(): | ||||
| @@ -535,62 +499,27 @@ def validate_tag(interface, tag): | ||||
| 			% (tag, _ALLOWABLE_TAG_CH)) | ||||
|  | ||||
|  | ||||
| def add_no_notify(cmdline): | ||||
| 	""" | ||||
| 	Given a command line to execute we will see if `--config` is present, if it | ||||
| 	is we will add the global/notify_dbus=0 to it, otherwise we will append it | ||||
| 	to the end of the list. | ||||
| 	:param: cmdline: The command line to inspect | ||||
| 	:type: cmdline: list | ||||
| 	:return: cmdline with notify_dbus config option present | ||||
| 	:rtype: list | ||||
| 	""" | ||||
|  | ||||
| 	# Only after we have seen an external event will be disable lvm from sending | ||||
| 	# us one when we call lvm | ||||
| 	if cfg.got_external_event: | ||||
| 		if 'help' in cmdline: | ||||
| 			return cmdline | ||||
|  | ||||
| 		if '--config' in cmdline: | ||||
| 			for i, arg in enumerate(cmdline): | ||||
| 				if arg == '--config': | ||||
| 					if len(cmdline) <= i+1: | ||||
| 						raise dbus.exceptions.DBusException("Missing value for --config option.") | ||||
| 					cmdline[i+1] += " global/notify_dbus=0" | ||||
| 					break | ||||
| 		else: | ||||
| 			cmdline.extend(['--config', 'global/notify_dbus=0']) | ||||
| 	return cmdline | ||||
|  | ||||
|  | ||||
| # The methods below which start with mt_* are used to execute the desired code | ||||
| # on the the main thread of execution to alleviate any issues the dbus-python | ||||
| # library with regards to multi-threaded access.  Essentially, we are trying to | ||||
| # ensure all dbus library interaction is done from the same thread! | ||||
|  | ||||
|  | ||||
| def _async_handler(call_back, parameters): | ||||
| 	params_str = ", ".join(str(x) for x in parameters) | ||||
| 	log_debug('Main thread execution, callback = %s, parameters = (%s)' % | ||||
| 				(str(call_back), params_str)) | ||||
|  | ||||
| 	try: | ||||
| 		if parameters: | ||||
| 			call_back(*parameters) | ||||
| 		else: | ||||
| 			call_back() | ||||
| 	except: | ||||
| 		st = traceback.format_exc() | ||||
| 		log_error("mt_async_call: exception (logged, not reported!) \n %s" % st) | ||||
| def _async_result(call_back, results): | ||||
| 	log_debug('Results = %s' % str(results)) | ||||
| 	call_back(results) | ||||
|  | ||||
|  | ||||
| # Execute the function on the main thread with the provided parameters, do | ||||
| # not return *any* value or wait for the execution to complete! | ||||
| def mt_async_call(function_call_back, *parameters): | ||||
| 	GLib.idle_add(_async_handler, function_call_back, parameters) | ||||
| # Return result in main thread | ||||
| def mt_async_result(call_back, results): | ||||
| 	GLib.idle_add(_async_result, call_back, results) | ||||
|  | ||||
|  | ||||
| # Take the supplied function and run it on the main thread and not wait for | ||||
| # a result! | ||||
| def mt_run_no_wait(function, param): | ||||
| 	GLib.idle_add(function, param) | ||||
|  | ||||
| # Run the supplied function and arguments on the main thread and wait for them | ||||
| # to complete while allowing the ability to get the return value too. | ||||
| # | ||||
| @@ -610,7 +539,6 @@ class MThreadRunner(object): | ||||
| 	def __init__(self, function, *args): | ||||
| 		self.f = function | ||||
| 		self.rc = None | ||||
| 		self.exception = None | ||||
| 		self.args = args | ||||
| 		self.function_complete = False | ||||
| 		self.cond = threading.Condition(threading.Lock()) | ||||
| @@ -620,21 +548,13 @@ class MThreadRunner(object): | ||||
| 		with self.cond: | ||||
| 			if not self.function_complete: | ||||
| 				self.cond.wait() | ||||
| 		if self.exception: | ||||
| 			raise self.exception | ||||
| 		return self.rc | ||||
|  | ||||
| 	def _run(self): | ||||
| 		try: | ||||
| 			if self.args: | ||||
| 				self.rc = self.f(*self.args) | ||||
| 			else: | ||||
| 				self.rc = self.f() | ||||
| 		except BaseException as be: | ||||
| 			self.exception = be | ||||
| 			st = traceback.format_exc() | ||||
| 			log_error("MThreadRunner: exception \n %s" % st) | ||||
| 			log_error("Exception will be raised in calling thread!") | ||||
| 		if len(self.args): | ||||
| 			self.rc = self.f(*self.args) | ||||
| 		else: | ||||
| 			self.rc = self.f() | ||||
|  | ||||
|  | ||||
| def _remove_objects(dbus_objects_rm): | ||||
|   | ||||
| @@ -145,35 +145,29 @@ class Vg(AutomatedProperties): | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def fetch_new_lv(vg_name, lv_name): | ||||
| 		cfg.load() | ||||
| 		return cfg.om.get_object_path_by_lvm_id("%s/%s" % (vg_name, lv_name)) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def handle_execute(rc, out, err): | ||||
| 		if rc == 0: | ||||
| 			cfg.load() | ||||
| 		else: | ||||
| 			# Need to work on error handling, need consistent | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def validate_dbus_object(vg_uuid, vg_name): | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(vg_uuid, vg_name) | ||||
| 		if not dbo: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(vg_uuid, vg_name)) | ||||
| 		return dbo | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _rename(uuid, vg_name, new_name, rename_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		rc, out, err = cmdhandler.vg_rename( | ||||
| 			vg_name, new_name, rename_options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.vg_rename(vg_name, new_name, | ||||
| 												rename_options) | ||||
| 			if rc == 0: | ||||
| 				cfg.load() | ||||
| 			else: | ||||
| 				# Need to work on error handling, need consistent | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -190,10 +184,24 @@ class Vg(AutomatedProperties): | ||||
| 	@staticmethod | ||||
| 	def _remove(uuid, vg_name, remove_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		# Remove the VG, if successful then remove from the model | ||||
| 		rc, out, err = cmdhandler.vg_remove(vg_name, remove_options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			# Remove the VG, if successful then remove from the model | ||||
| 			rc, out, err = cmdhandler.vg_remove(vg_name, remove_options) | ||||
|  | ||||
| 			if rc == 0: | ||||
| 				cfg.load() | ||||
| 			else: | ||||
| 				# Need to work on error handling, need consistent | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -208,9 +216,26 @@ class Vg(AutomatedProperties): | ||||
|  | ||||
| 	@staticmethod | ||||
| 	def _change(uuid, vg_name, change_options): | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		rc, out, err = cmdhandler.vg_change(change_options, vg_name) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.vg_change(change_options, vg_name) | ||||
|  | ||||
| 			# To use an example with d-feet (Method input) | ||||
| 			# {"activate": __import__('gi.repository.GLib', globals(), | ||||
| 			# locals(), ['Variant']).Variant("s", "n")} | ||||
|  | ||||
| 			if rc == 0: | ||||
| 				cfg.load() | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
| 		return '/' | ||||
|  | ||||
| 	# TODO: This should be broken into a number of different methods | ||||
| @@ -231,24 +256,34 @@ class Vg(AutomatedProperties): | ||||
| 	@staticmethod | ||||
| 	def _reduce(uuid, vg_name, missing, pv_object_paths, reduce_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		pv_devices = [] | ||||
| 		if dbo: | ||||
| 			pv_devices = [] | ||||
|  | ||||
| 		# If pv_object_paths is not empty, then get the device paths | ||||
| 		if pv_object_paths and len(pv_object_paths) > 0: | ||||
| 			for pv_op in pv_object_paths: | ||||
| 				pv = cfg.om.get_object_by_path(pv_op) | ||||
| 				if pv: | ||||
| 					pv_devices.append(pv.lvm_id) | ||||
| 				else: | ||||
| 					raise dbus.exceptions.DBusException( | ||||
| 						VG_INTERFACE, | ||||
| 						'PV Object path not found = %s!' % pv_op) | ||||
| 			# If pv_object_paths is not empty, then get the device paths | ||||
| 			if pv_object_paths and len(pv_object_paths) > 0: | ||||
| 				for pv_op in pv_object_paths: | ||||
| 					pv = cfg.om.get_object_by_path(pv_op) | ||||
| 					if pv: | ||||
| 						pv_devices.append(pv.lvm_id) | ||||
| 					else: | ||||
| 						raise dbus.exceptions.DBusException( | ||||
| 							VG_INTERFACE, | ||||
| 							'PV Object path not found = %s!' % pv_op) | ||||
|  | ||||
| 		rc, out, err = cmdhandler.vg_reduce(vg_name, missing, pv_devices, | ||||
| 											reduce_options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 			rc, out, err = cmdhandler.vg_reduce(vg_name, missing, pv_devices, | ||||
| 												reduce_options) | ||||
| 			if rc == 0: | ||||
| 				cfg.load() | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, 'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -265,26 +300,36 @@ class Vg(AutomatedProperties): | ||||
| 	@staticmethod | ||||
| 	def _extend(uuid, vg_name, pv_object_paths, extend_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		extend_devices = [] | ||||
| 		if dbo: | ||||
| 			extend_devices = [] | ||||
|  | ||||
| 		for i in pv_object_paths: | ||||
| 			pv = cfg.om.get_object_by_path(i) | ||||
| 			if pv: | ||||
| 				extend_devices.append(pv.lvm_id) | ||||
| 			for i in pv_object_paths: | ||||
| 				pv = cfg.om.get_object_by_path(i) | ||||
| 				if pv: | ||||
| 					extend_devices.append(pv.lvm_id) | ||||
| 				else: | ||||
| 					raise dbus.exceptions.DBusException( | ||||
| 						VG_INTERFACE, 'PV Object path not found = %s!' % i) | ||||
|  | ||||
| 			if len(extend_devices): | ||||
| 				rc, out, err = cmdhandler.vg_extend(vg_name, extend_devices, | ||||
| 													extend_options) | ||||
| 				if rc == 0: | ||||
| 					cfg.load() | ||||
| 				else: | ||||
| 					raise dbus.exceptions.DBusException( | ||||
| 						VG_INTERFACE, | ||||
| 						'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, 'PV Object path not found = %s!' % i) | ||||
|  | ||||
| 		if len(extend_devices): | ||||
| 			rc, out, err = cmdhandler.vg_extend(vg_name, extend_devices, | ||||
| 												extend_options) | ||||
| 			Vg.handle_execute(rc, out, err) | ||||
| 					VG_INTERFACE, 'No pv_object_paths provided!') | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, 'No pv_object_paths provided!') | ||||
|  | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
| 		return '/' | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| @@ -321,24 +366,33 @@ class Vg(AutomatedProperties): | ||||
| 			create_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		pv_dests = [] | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		if dbo: | ||||
| 			if len(pv_dests_and_ranges): | ||||
| 				for pr in pv_dests_and_ranges: | ||||
| 					pv_dbus_obj = cfg.om.get_object_by_path(pr[0]) | ||||
| 					if not pv_dbus_obj: | ||||
| 						raise dbus.exceptions.DBusException( | ||||
| 							VG_INTERFACE, | ||||
| 							'PV Destination (%s) not found' % pr[0]) | ||||
|  | ||||
| 		if len(pv_dests_and_ranges): | ||||
| 			for pr in pv_dests_and_ranges: | ||||
| 				pv_dbus_obj = cfg.om.get_object_by_path(pr[0]) | ||||
| 				if not pv_dbus_obj: | ||||
| 					raise dbus.exceptions.DBusException( | ||||
| 						VG_INTERFACE, | ||||
| 						'PV Destination (%s) not found' % pr[0]) | ||||
| 					pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2])) | ||||
|  | ||||
| 				pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2])) | ||||
| 			rc, out, err = cmdhandler.vg_lv_create( | ||||
| 				vg_name, create_options, name, size_bytes, pv_dests) | ||||
|  | ||||
| 		rc, out, err = cmdhandler.vg_lv_create( | ||||
| 			vg_name, create_options, name, size_bytes, pv_dests) | ||||
|  | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return Vg.fetch_new_lv(vg_name, name) | ||||
| 			if rc == 0: | ||||
| 				return Vg.fetch_new_lv(vg_name, name) | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| @@ -374,13 +428,25 @@ class Vg(AutomatedProperties): | ||||
| 	def _lv_create_linear(uuid, vg_name, name, size_bytes, | ||||
| 			thin_pool, create_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		rc, out, err = cmdhandler.vg_lv_create_linear( | ||||
| 			vg_name, create_options, name, size_bytes, thin_pool) | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.vg_lv_create_linear( | ||||
| 				vg_name, create_options, name, size_bytes, thin_pool) | ||||
|  | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return Vg.fetch_new_lv(vg_name, name) | ||||
| 			if rc == 0: | ||||
| 				created_lv = Vg.fetch_new_lv(vg_name, name) | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
|  | ||||
| 		return created_lv | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| @@ -400,12 +466,24 @@ class Vg(AutomatedProperties): | ||||
| 	def _lv_create_striped(uuid, vg_name, name, size_bytes, num_stripes, | ||||
| 			stripe_size_kb, thin_pool, create_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		rc, out, err = cmdhandler.vg_lv_create_striped( | ||||
| 			vg_name, create_options, name, size_bytes, | ||||
| 			num_stripes, stripe_size_kb, thin_pool) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return Vg.fetch_new_lv(vg_name, name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.vg_lv_create_striped( | ||||
| 				vg_name, create_options, name, size_bytes, | ||||
| 				num_stripes, stripe_size_kb, thin_pool) | ||||
| 			if rc == 0: | ||||
| 				created_lv = Vg.fetch_new_lv(vg_name, name) | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, 'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
|  | ||||
| 		return created_lv | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| @@ -428,11 +506,25 @@ class Vg(AutomatedProperties): | ||||
| 	def _lv_create_mirror(uuid, vg_name, name, size_bytes, | ||||
| 			num_copies, create_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		rc, out, err = cmdhandler.vg_lv_create_mirror( | ||||
| 			vg_name, create_options, name, size_bytes, num_copies) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return Vg.fetch_new_lv(vg_name, name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.vg_lv_create_mirror( | ||||
| 				vg_name, create_options, name, size_bytes, num_copies) | ||||
| 			if rc == 0: | ||||
| 				created_lv = Vg.fetch_new_lv(vg_name, name) | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
|  | ||||
| 		return created_lv | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| @@ -453,12 +545,26 @@ class Vg(AutomatedProperties): | ||||
| 	def _lv_create_raid(uuid, vg_name, name, raid_type, size_bytes, | ||||
| 						num_stripes, stripe_size_kb, create_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		rc, out, err = cmdhandler.vg_lv_create_raid( | ||||
| 			vg_name, create_options, name, raid_type, size_bytes, | ||||
| 			num_stripes, stripe_size_kb) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return Vg.fetch_new_lv(vg_name, name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.vg_lv_create_raid( | ||||
| 				vg_name, create_options, name, raid_type, size_bytes, | ||||
| 				num_stripes, stripe_size_kb) | ||||
| 			if rc == 0: | ||||
| 				created_lv = Vg.fetch_new_lv(vg_name, name) | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
|  | ||||
| 		return created_lv | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| @@ -479,27 +585,33 @@ class Vg(AutomatedProperties): | ||||
| 	def _create_pool(uuid, vg_name, meta_data_lv, data_lv, | ||||
| 						create_options, create_method): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		# Retrieve the full names for the metadata and data lv | ||||
| 		md = cfg.om.get_object_by_path(meta_data_lv) | ||||
| 		data = cfg.om.get_object_by_path(data_lv) | ||||
|  | ||||
| 		if md and data: | ||||
| 		if dbo and md and data: | ||||
|  | ||||
| 			new_name = data.Name | ||||
|  | ||||
| 			rc, out, err = create_method( | ||||
| 				md.lv_full_name(), data.lv_full_name(), create_options) | ||||
|  | ||||
| 			if rc == 0: | ||||
| 				mt_remove_dbus_objects((md, data)) | ||||
|  | ||||
| 			Vg.handle_execute(rc, out, err) | ||||
| 				cache_pool_lv = Vg.fetch_new_lv(vg_name, new_name) | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 		else: | ||||
| 			msg = "" | ||||
|  | ||||
| 			if not dbo: | ||||
| 				msg += 'VG with uuid %s and name %s not present!' % \ | ||||
| 					(uuid, vg_name) | ||||
|  | ||||
| 			if not md: | ||||
| 				msg += 'Meta data LV with object path %s not present!' % \ | ||||
| 					(meta_data_lv) | ||||
| @@ -510,7 +622,7 @@ class Vg(AutomatedProperties): | ||||
|  | ||||
| 			raise dbus.exceptions.DBusException(VG_INTERFACE, msg) | ||||
|  | ||||
| 		return Vg.fetch_new_lv(vg_name, new_name) | ||||
| 		return cache_pool_lv | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| @@ -544,21 +656,33 @@ class Vg(AutomatedProperties): | ||||
| 		pv_devices = [] | ||||
|  | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		# Check for existence of pv object paths | ||||
| 		for p in pv_object_paths: | ||||
| 			pv = cfg.om.get_object_by_path(p) | ||||
| 			if pv: | ||||
| 				pv_devices.append(pv.Name) | ||||
| 		if dbo: | ||||
| 			# Check for existence of pv object paths | ||||
| 			for p in pv_object_paths: | ||||
| 				pv = cfg.om.get_object_by_path(p) | ||||
| 				if pv: | ||||
| 					pv_devices.append(pv.Name) | ||||
| 				else: | ||||
| 					raise dbus.exceptions.DBusException( | ||||
| 						VG_INTERFACE, 'PV object path = %s not found' % p) | ||||
|  | ||||
| 			rc, out, err = cmdhandler.pv_tag( | ||||
| 				pv_devices, tags_add, tags_del, tag_options) | ||||
| 			if rc == 0: | ||||
| 				cfg.load() | ||||
| 				return '/' | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, 'PV object path = %s not found' % p) | ||||
| 					VG_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 		rc, out, err = cmdhandler.pv_tag( | ||||
| 			pv_devices, tags_add, tags_del, tag_options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| @@ -596,12 +720,25 @@ class Vg(AutomatedProperties): | ||||
| 	@staticmethod | ||||
| 	def _vg_add_rm_tags(uuid, vg_name, tags_add, tags_del, tag_options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		rc, out, err = cmdhandler.vg_tag( | ||||
| 			vg_name, tags_add, tags_del, tag_options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
| 		if dbo: | ||||
|  | ||||
| 			rc, out, err = cmdhandler.vg_tag( | ||||
| 				vg_name, tags_add, tags_del, tag_options) | ||||
| 			if rc == 0: | ||||
| 				dbo.refresh() | ||||
| 				return '/' | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| @@ -638,10 +775,23 @@ class Vg(AutomatedProperties): | ||||
| 	@staticmethod | ||||
| 	def _vg_change_set(uuid, vg_name, method, value, options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		rc, out, err = method(vg_name, value, options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = method(vg_name, value, options) | ||||
| 			if rc == 0: | ||||
| 				dbo.refresh() | ||||
| 				return '/' | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
|  | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
| @@ -699,11 +849,23 @@ class Vg(AutomatedProperties): | ||||
| 	def _vg_activate_deactivate(uuid, vg_name, activate, control_flags, | ||||
| 								options): | ||||
| 		# Make sure we have a dbus object representing it | ||||
| 		Vg.validate_dbus_object(uuid, vg_name) | ||||
| 		rc, out, err = cmdhandler.activate_deactivate( | ||||
| 			'vgchange', vg_name, activate, control_flags, options) | ||||
| 		Vg.handle_execute(rc, out, err) | ||||
| 		return '/' | ||||
| 		dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name) | ||||
|  | ||||
| 		if dbo: | ||||
| 			rc, out, err = cmdhandler.activate_deactivate( | ||||
| 				'vgchange', vg_name, activate, control_flags, options) | ||||
| 			if rc == 0: | ||||
| 				cfg.load() | ||||
| 				return '/' | ||||
| 			else: | ||||
| 				raise dbus.exceptions.DBusException( | ||||
| 					VG_INTERFACE, | ||||
| 					'Exit code %s, stderr = %s' % (str(rc), err)) | ||||
| 		else: | ||||
| 			raise dbus.exceptions.DBusException( | ||||
| 				VG_INTERFACE, | ||||
| 				'VG with uuid %s and name %s not present!' % | ||||
| 				(uuid, vg_name)) | ||||
|  | ||||
| 	@dbus.service.method( | ||||
| 		dbus_interface=VG_INTERFACE, | ||||
|   | ||||
| @@ -16,7 +16,7 @@ top_srcdir = @top_srcdir@ | ||||
| top_builddir = @top_builddir@ | ||||
|  | ||||
| SOURCES = lvmetad-core.c | ||||
| SOURCES2 = lvmetactl.c | ||||
| SOURCES2 = testclient.c | ||||
|  | ||||
| TARGETS = lvmetad lvmetactl | ||||
|  | ||||
| @@ -28,19 +28,22 @@ CFLOW_TARGET = lvmetad | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| CFLAGS_lvmetactl.o += $(EXTRA_EXEC_CFLAGS) | ||||
| CFLAGS_lvmetad-core.o += $(EXTRA_EXEC_CFLAGS) | ||||
| INCLUDES += -I$(top_srcdir)/libdaemon/server | ||||
| LDFLAGS += -L$(top_builddir)/libdaemon/server $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) | ||||
| LIBS += $(RT_LIBS) $(DAEMON_LIBS) -ldevmapper $(PTHREAD_LIBS) | ||||
| LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper | ||||
|  | ||||
| LIBS += $(PTHREAD_LIBS) | ||||
|  | ||||
| LDFLAGS += -L$(top_builddir)/libdaemon/server $(EXTRA_EXEC_LDFLAGS) | ||||
| CLDFLAGS += -L$(top_builddir)/libdaemon/server | ||||
| CFLAGS += $(EXTRA_EXEC_CFLAGS) | ||||
|  | ||||
| lvmetad: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \ | ||||
| 		    $(top_builddir)/libdaemon/server/libdaemonserver.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) -ldaemonserver $(LIBS) | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LVMLIBS) $(LIBS) | ||||
|  | ||||
| lvmetactl: lvmetactl.o $(top_builddir)/libdaemon/client/libdaemonclient.a \ | ||||
| 	$(top_builddir)/libdaemon/server/libdaemonserver.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmetactl.o $(LIBS) | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmetactl.o $(LVMLIBS) | ||||
|  | ||||
| CLEAN_TARGETS += lvmetactl.o | ||||
|  | ||||
|   | ||||
| @@ -25,7 +25,6 @@ | ||||
| #define LVMETAD_DISABLE_REASON_LVM1		"LVM1" | ||||
| #define LVMETAD_DISABLE_REASON_DUPLICATES	"DUPLICATES" | ||||
| #define LVMETAD_DISABLE_REASON_VGRESTORE	"VGRESTORE" | ||||
| #define LVMETAD_DISABLE_REASON_REPAIR		"REPAIR" | ||||
|  | ||||
| struct volume_group; | ||||
|  | ||||
|   | ||||
| @@ -203,9 +203,8 @@ struct vg_info { | ||||
| #define GLFL_DISABLE_REASON_LVM1       0x00000008 | ||||
| #define GLFL_DISABLE_REASON_DUPLICATES 0x00000010 | ||||
| #define GLFL_DISABLE_REASON_VGRESTORE  0x00000020 | ||||
| #define GLFL_DISABLE_REASON_REPAIR     0x00000040 | ||||
|  | ||||
| #define GLFL_DISABLE_REASON_ALL (GLFL_DISABLE_REASON_DIRECT | GLFL_DISABLE_REASON_REPAIR | GLFL_DISABLE_REASON_LVM1 | GLFL_DISABLE_REASON_DUPLICATES | GLFL_DISABLE_REASON_VGRESTORE) | ||||
| #define GLFL_DISABLE_REASON_ALL (GLFL_DISABLE_REASON_DIRECT | GLFL_DISABLE_REASON_LVM1 | GLFL_DISABLE_REASON_DUPLICATES | GLFL_DISABLE_REASON_VGRESTORE) | ||||
|  | ||||
| #define VGFL_INVALID 0x00000001 | ||||
|  | ||||
| @@ -258,21 +257,6 @@ static void destroy_metadata_hashes(lvmetad_state *s) | ||||
| 	dm_hash_iterate(n, s->pvid_to_pvmeta) | ||||
| 		dm_config_destroy(dm_hash_get_data(s->pvid_to_pvmeta, n)); | ||||
|  | ||||
| 	dm_hash_iterate(n, s->vgid_to_vgname) | ||||
| 		dm_free(dm_hash_get_data(s->vgid_to_vgname, n)); | ||||
|  | ||||
| 	dm_hash_iterate(n, s->vgname_to_vgid) | ||||
| 		dm_free(dm_hash_get_data(s->vgname_to_vgid, n)); | ||||
|  | ||||
| 	dm_hash_iterate(n, s->vgid_to_info) | ||||
| 		dm_free(dm_hash_get_data(s->vgid_to_info, n)); | ||||
|  | ||||
| 	dm_hash_iterate(n, s->device_to_pvid) | ||||
| 		dm_free(dm_hash_get_data(s->device_to_pvid, n)); | ||||
|  | ||||
| 	dm_hash_iterate(n, s->pvid_to_vgid) | ||||
| 		dm_free(dm_hash_get_data(s->pvid_to_vgid, n)); | ||||
|  | ||||
| 	dm_hash_destroy(s->pvid_to_pvmeta); | ||||
| 	dm_hash_destroy(s->vgid_to_metadata); | ||||
| 	dm_hash_destroy(s->vgid_to_vgname); | ||||
| @@ -808,8 +792,7 @@ static int _update_pvid_to_vgid(lvmetad_state *s, struct dm_config_tree *vg, | ||||
|  | ||||
| 		if ((mode == REMOVE_EMPTY) && vgid_old) { | ||||
| 			/* This copies the vgid_old string, doesn't reference it. */ | ||||
| 			if ((dm_hash_lookup(to_check, vgid_old) != (void*) 1) && | ||||
| 			    !dm_hash_insert(to_check, vgid_old, (void*) 1)) { | ||||
| 			if (!dm_hash_insert(to_check, vgid_old, (void*) 1)) { | ||||
| 				ERROR(s, "update_pvid_to_vgid out of memory for hash insert vgid_old %s", vgid_old); | ||||
| 				goto abort_daemon; | ||||
| 			} | ||||
| @@ -885,13 +868,16 @@ static int remove_metadata(lvmetad_state *s, const char *vgid, int update_pvids) | ||||
|  | ||||
| 	/* free the unmapped data */ | ||||
|  | ||||
| 	if (info_lookup) | ||||
| 		dm_free(info_lookup); | ||||
| 	if (meta_lookup) | ||||
| 		dm_config_destroy(meta_lookup); | ||||
| 	if (name_lookup) | ||||
| 		dm_free(name_lookup); | ||||
| 	if (outdated_pvs_lookup) | ||||
| 		dm_config_destroy(outdated_pvs_lookup); | ||||
| 	dm_free(info_lookup); | ||||
| 	dm_free(name_lookup); | ||||
| 	dm_free(vgid_lookup); | ||||
| 	if (vgid_lookup) | ||||
| 		dm_free(vgid_lookup); | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| @@ -1218,8 +1204,10 @@ static int _update_metadata_add_new(lvmetad_state *s, const char *new_name, cons | ||||
| out: | ||||
| out_free: | ||||
| 	if (!new_name_dup || !new_vgid_dup || abort_daemon) { | ||||
| 		dm_free(new_name_dup); | ||||
| 		dm_free(new_vgid_dup); | ||||
| 		if (new_name_dup) | ||||
| 			dm_free(new_name_dup); | ||||
| 		if (new_vgid_dup) | ||||
| 			dm_free(new_vgid_dup); | ||||
| 		ERROR(s, "lvmetad could not be updated and is aborting."); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
| @@ -1809,7 +1797,8 @@ static response pv_gone(lvmetad_state *s, request r) | ||||
| 	} | ||||
|  | ||||
| 	dm_config_destroy(pvmeta); | ||||
| 	dm_free(old_pvid); | ||||
| 	if (old_pvid) | ||||
| 		dm_free(old_pvid); | ||||
|  | ||||
| 	return daemon_reply_simple("OK", NULL ); | ||||
| } | ||||
| @@ -1922,7 +1911,7 @@ static response pv_found(lvmetad_state *s, request r) | ||||
| 	const char *arg_pvid = NULL; | ||||
| 	const char *arg_pvid_lookup = NULL; | ||||
| 	const char *new_pvid = NULL; | ||||
| 	char *new_pvid_dup = NULL; | ||||
| 	const char *new_pvid_dup = NULL; | ||||
| 	const char *arg_name = NULL; | ||||
| 	const char *arg_vgid = NULL; | ||||
| 	const char *arg_vgid_lookup = NULL; | ||||
| @@ -2085,7 +2074,7 @@ static response pv_found(lvmetad_state *s, request r) | ||||
| 		if (!(new_pvid_dup = dm_strdup(new_pvid))) | ||||
| 			goto nomem_free1; | ||||
|  | ||||
| 		if (!dm_hash_insert_binary(s->device_to_pvid, &new_device, sizeof(new_device), new_pvid_dup)) | ||||
| 		if (!dm_hash_insert_binary(s->device_to_pvid, &new_device, sizeof(new_device), (char *)new_pvid_dup)) | ||||
| 			goto nomem_free2; | ||||
|  | ||||
| 		if (!dm_hash_insert(s->pvid_to_pvmeta, new_pvid, new_pvmeta)) | ||||
| @@ -2121,8 +2110,6 @@ static response pv_found(lvmetad_state *s, request r) | ||||
| 				DEBUGLOG(s, "pv_found ignore duplicate device %" PRIu64 " of existing PV for pvid %s", | ||||
| 				         arg_device, arg_pvid); | ||||
| 				dm_config_destroy(new_pvmeta); | ||||
| 				/* device_to_pvid no longer references prev_pvid_lookup */ | ||||
| 				dm_free((void*)prev_pvid_on_dev); | ||||
| 				s->flags |= GLFL_DISABLE; | ||||
| 				s->flags |= GLFL_DISABLE_REASON_DUPLICATES; | ||||
| 				return reply_fail("Ignore duplicate PV"); | ||||
| @@ -2133,7 +2120,7 @@ static response pv_found(lvmetad_state *s, request r) | ||||
| 		if (!(new_pvid_dup = dm_strdup(new_pvid))) | ||||
| 			goto nomem_free1; | ||||
|  | ||||
| 		if (!dm_hash_insert_binary(s->device_to_pvid, &arg_device, sizeof(arg_device), new_pvid_dup)) | ||||
| 		if (!dm_hash_insert_binary(s->device_to_pvid, &arg_device, sizeof(arg_device), (char *)new_pvid_dup)) | ||||
| 			goto nomem_free2; | ||||
|  | ||||
| 		if (!dm_hash_insert(s->pvid_to_pvmeta, new_pvid, new_pvmeta)) | ||||
| @@ -2233,7 +2220,8 @@ static response pv_found(lvmetad_state *s, request r) | ||||
| 	} | ||||
|  | ||||
| 	/* This was unhashed from device_to_pvid above. */ | ||||
| 	dm_free((void *)prev_pvid_on_dev); | ||||
| 	if (prev_pvid_on_dev) | ||||
| 		dm_free((void *)prev_pvid_on_dev); | ||||
|  | ||||
| 	return daemon_reply_simple("OK", | ||||
| 				   "status = %s", vg_status, | ||||
| @@ -2245,7 +2233,7 @@ static response pv_found(lvmetad_state *s, request r) | ||||
| 				   NULL); | ||||
|  | ||||
|  nomem_free2: | ||||
| 	dm_free(new_pvid_dup); | ||||
| 	dm_free((char *)new_pvid_dup); | ||||
|  nomem_free1: | ||||
| 	dm_config_destroy(new_pvmeta); | ||||
|  nomem: | ||||
| @@ -2367,8 +2355,6 @@ static response set_global_info(lvmetad_state *s, request r) | ||||
| 	if ((reason = daemon_request_str(r, "disable_reason", NULL))) { | ||||
| 		if (strstr(reason, LVMETAD_DISABLE_REASON_DIRECT)) | ||||
| 			reason_flags |= GLFL_DISABLE_REASON_DIRECT; | ||||
| 		if (strstr(reason, LVMETAD_DISABLE_REASON_REPAIR)) | ||||
| 			reason_flags |= GLFL_DISABLE_REASON_REPAIR; | ||||
| 		if (strstr(reason, LVMETAD_DISABLE_REASON_LVM1)) | ||||
| 			reason_flags |= GLFL_DISABLE_REASON_LVM1; | ||||
| 		if (strstr(reason, LVMETAD_DISABLE_REASON_DUPLICATES)) | ||||
| @@ -2432,9 +2418,8 @@ static response get_global_info(lvmetad_state *s, request r) | ||||
| 	pid = (int)daemon_request_int(r, "pid", 0); | ||||
|  | ||||
| 	if (s->flags & GLFL_DISABLE) { | ||||
| 		snprintf(reason, REASON_BUF_SIZE - 1, "%s%s%s%s%s", | ||||
| 		snprintf(reason, REASON_BUF_SIZE - 1, "%s%s%s%s", | ||||
| 			 (s->flags & GLFL_DISABLE_REASON_DIRECT)     ? LVMETAD_DISABLE_REASON_DIRECT "," : "", | ||||
| 			 (s->flags & GLFL_DISABLE_REASON_REPAIR)     ? LVMETAD_DISABLE_REASON_REPAIR "," : "", | ||||
| 			 (s->flags & GLFL_DISABLE_REASON_LVM1)       ? LVMETAD_DISABLE_REASON_LVM1 "," : "", | ||||
| 			 (s->flags & GLFL_DISABLE_REASON_DUPLICATES) ? LVMETAD_DISABLE_REASON_DUPLICATES "," : "", | ||||
| 			 (s->flags & GLFL_DISABLE_REASON_VGRESTORE)  ? LVMETAD_DISABLE_REASON_VGRESTORE "," : ""); | ||||
| @@ -2572,12 +2557,14 @@ static void _dump_pairs(struct buffer *buf, struct dm_hash_table *ht, const char | ||||
| 	dm_hash_iterate(n, ht) { | ||||
| 		const char *key = dm_hash_get_key(ht, n), | ||||
| 			   *val = dm_hash_get_data(ht, n); | ||||
| 		buffer_append(buf, "    "); | ||||
| 		if (int_key) | ||||
| 			(void) dm_asprintf(&append, "    %d = \"%s\"\n", *(const int*)key, val); | ||||
| 			(void) dm_asprintf(&append, "%d = \"%s\"", *(const int*)key, val); | ||||
| 		else | ||||
| 			(void) dm_asprintf(&append, "    %s = \"%s\"\n", key, val); | ||||
| 			(void) dm_asprintf(&append, "%s = \"%s\"", key, val); | ||||
| 		if (append) | ||||
| 			buffer_append(buf, append); | ||||
| 		buffer_append(buf, "\n"); | ||||
| 		dm_free(append); | ||||
| 	} | ||||
| 	buffer_append(buf, "}\n"); | ||||
| @@ -2595,9 +2582,11 @@ static void _dump_info_version(struct buffer *buf, struct dm_hash_table *ht, con | ||||
| 	while (n) { | ||||
| 		const char *key = dm_hash_get_key(ht, n); | ||||
| 		info = dm_hash_get_data(ht, n); | ||||
| 		(void) dm_asprintf(&append, "    %s = %lld\n", key, (long long)info->external_version); | ||||
| 		buffer_append(buf, "    "); | ||||
| 		(void) dm_asprintf(&append, "%s = %lld", key, (long long)info->external_version); | ||||
| 		if (append) | ||||
| 			buffer_append(buf, append); | ||||
| 		buffer_append(buf, "\n"); | ||||
| 		dm_free(append); | ||||
| 		n = dm_hash_get_next(ht, n); | ||||
| 	} | ||||
| @@ -2616,9 +2605,11 @@ static void _dump_info_flags(struct buffer *buf, struct dm_hash_table *ht, const | ||||
| 	while (n) { | ||||
| 		const char *key = dm_hash_get_key(ht, n); | ||||
| 		info = dm_hash_get_data(ht, n); | ||||
| 		(void) dm_asprintf(&append, "    %s = %llx\n", key, (long long)info->flags); | ||||
| 		buffer_append(buf, "    "); | ||||
| 		(void) dm_asprintf(&append, "%s = %llx", key, (long long)info->flags); | ||||
| 		if (append) | ||||
| 			buffer_append(buf, append); | ||||
| 		buffer_append(buf, "\n"); | ||||
| 		dm_free(append); | ||||
| 		n = dm_hash_get_next(ht, n); | ||||
| 	} | ||||
|   | ||||
| @@ -19,12 +19,10 @@ SOURCES = lvmlockd-core.c | ||||
|  | ||||
| ifeq ("@BUILD_LOCKDSANLOCK@", "yes") | ||||
|   SOURCES += lvmlockd-sanlock.c | ||||
|   LOCK_LIBS += -lsanlock_client | ||||
| endif | ||||
|  | ||||
| ifeq ("@BUILD_LOCKDDLM@", "yes") | ||||
|   SOURCES += lvmlockd-dlm.c | ||||
|   LOCK_LIBS += -ldlm_lt | ||||
| endif | ||||
|  | ||||
| TARGETS = lvmlockd lvmlockctl | ||||
| @@ -33,17 +31,29 @@ TARGETS = lvmlockd lvmlockctl | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| CFLAGS += $(EXTRA_EXEC_CFLAGS) | ||||
| INCLUDES += -I$(top_srcdir)/libdaemon/server | ||||
| LDFLAGS += -L$(top_builddir)/libdaemon/server $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) | ||||
| LIBS += $(RT_LIBS) $(DAEMON_LIBS) -ldevmapper $(PTHREAD_LIBS) | ||||
| LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper | ||||
|  | ||||
| LIBS += $(PTHREAD_LIBS) | ||||
|  | ||||
| ifeq ("@BUILD_LOCKDSANLOCK@", "yes") | ||||
|   LIBS += -lsanlock_client | ||||
| endif | ||||
|  | ||||
| ifeq ("@BUILD_LOCKDDLM@", "yes") | ||||
|   LIBS += -ldlm_lt | ||||
| endif | ||||
|  | ||||
| LDFLAGS += -L$(top_builddir)/libdaemon/server | ||||
| CLDFLAGS += -L$(top_builddir)/libdaemon/server | ||||
|  | ||||
| lvmlockd: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \ | ||||
| 		    $(top_builddir)/libdaemon/server/libdaemonserver.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LOCK_LIBS) -ldaemonserver $(LIBS) | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LVMLIBS) $(LIBS) | ||||
|  | ||||
| lvmlockctl: lvmlockctl.o $(top_builddir)/libdaemon/client/libdaemonclient.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmlockctl.o $(LIBS) | ||||
| lvmlockctl: lvmlockctl.o $(top_builddir)/libdaemon/client/libdaemonclient.a \ | ||||
| 		    $(top_builddir)/libdaemon/server/libdaemonserver.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ lvmlockctl.o $(LVMLIBS) | ||||
|  | ||||
| install_lvmlockd: lvmlockd | ||||
| 	$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F) | ||||
|   | ||||
| @@ -379,7 +379,7 @@ static int setup_dump_socket(void) | ||||
| 	rv = bind(s, (struct sockaddr *) &dump_addr, dump_addrlen); | ||||
| 	if (rv < 0) { | ||||
| 		rv = -errno; | ||||
| 		if (close(s)) | ||||
| 		if (!close(s)) | ||||
| 			log_error("failed to close dump socket"); | ||||
| 		return rv; | ||||
| 	} | ||||
|   | ||||
| @@ -48,7 +48,5 @@ static inline void lvmlockd_close(daemon_handle h) | ||||
| #define EVGKILLED 217 /* sanlock lost access to leases and VG is killed. */ | ||||
| #define ELOCKIO   218 /* sanlock io errors during lock op, may be transient. */ | ||||
| #define EREMOVED  219 | ||||
| #define EDEVOPEN  220 /* sanlock failed to open lvmlock LV */ | ||||
| #define ELMERR    221 | ||||
|  | ||||
| #endif	/* _LVM_LVMLOCKD_CLIENT_H */ | ||||
|   | ||||
| @@ -19,7 +19,6 @@ | ||||
| #include "lvm-version.h" | ||||
| #include "lvmetad-client.h" | ||||
| #include "lvmlockd-client.h" | ||||
| #include "dm-ioctl.h" /* for DM_UUID_LEN */ | ||||
|  | ||||
| /* #include <assert.h> */ | ||||
| #include <errno.h> | ||||
| @@ -1389,11 +1388,12 @@ static int res_convert(struct lockspace *ls, struct resource *r, | ||||
| 	} | ||||
|  | ||||
| 	rv = lm_convert(ls, r, act->mode, act, r_version); | ||||
|  | ||||
| 	log_debug("S %s R %s res_convert rv %d", ls->name, r->name, rv); | ||||
|  | ||||
| 	if (rv < 0) | ||||
| 	if (rv < 0) { | ||||
| 		log_error("S %s R %s res_convert lm error %d", ls->name, r->name, rv); | ||||
| 		return rv; | ||||
| 	} | ||||
|  | ||||
| 	log_debug("S %s R %s res_convert lm done", ls->name, r->name); | ||||
|  | ||||
| 	if (lk->mode == LD_LK_EX && act->mode == LD_LK_SH) { | ||||
| 		r->sh_count = 1; | ||||
| @@ -2651,16 +2651,10 @@ out_act: | ||||
| 	ls->drop_vg = drop_vg; | ||||
| 	if (ls->lm_type == LD_LM_DLM && !strcmp(ls->name, gl_lsname_dlm)) | ||||
| 		global_dlm_lockspace_exists = 0; | ||||
|  | ||||
| 	/* | ||||
| 	 * Avoid a name collision of the same lockspace is added again before | ||||
| 	 * this thread is cleaned up.  We just set ls->name to a "junk" value | ||||
| 	 * for the short period until the struct is freed.  We could make it | ||||
| 	 * blank or fill it with garbage, but instead set it to REM:<name> | ||||
| 	 * to make it easier to follow progress of freeing is via log_debug. | ||||
| 	 */ | ||||
| 	dm_strncpy(tmp_name, ls->name, sizeof(tmp_name)); | ||||
| 	snprintf(ls->name, sizeof(ls->name), "REM:%s", tmp_name); | ||||
| 	/* Avoid a name collision of the same lockspace is added again before this thread is cleaned up. */ | ||||
| 	memset(tmp_name, 0, sizeof(tmp_name)); | ||||
| 	snprintf(tmp_name, MAX_NAME, "REM:%s", ls->name); | ||||
| 	memcpy(ls->name, tmp_name, MAX_NAME); | ||||
| 	pthread_mutex_unlock(&lockspaces_mutex); | ||||
|  | ||||
| 	/* worker_thread will join this thread, and free the ls */ | ||||
| @@ -3309,6 +3303,7 @@ static int work_init_lv(struct action *act) | ||||
| 		lm_type = ls->lm_type; | ||||
| 		memcpy(vg_args, ls->vg_args, MAX_ARGS); | ||||
| 		free_offset = ls->free_lock_offset; | ||||
| 		ls->free_lock_offset = 0; | ||||
| 	} | ||||
| 	pthread_mutex_unlock(&lockspaces_mutex); | ||||
|  | ||||
| @@ -3538,15 +3533,11 @@ static int setup_worker_thread(void) | ||||
|  | ||||
| static void close_worker_thread(void) | ||||
| { | ||||
| 	int perrno; | ||||
|  | ||||
| 	pthread_mutex_lock(&worker_mutex); | ||||
| 	worker_stop = 1; | ||||
| 	pthread_cond_signal(&worker_cond); | ||||
| 	pthread_mutex_unlock(&worker_mutex); | ||||
|  | ||||
| 	if ((perrno = pthread_join(worker_thread, NULL))) | ||||
| 		log_error("pthread_join worker_thread error %d", perrno); | ||||
| 	pthread_join(worker_thread, NULL); | ||||
| } | ||||
|  | ||||
| /* client_mutex is locked */ | ||||
| @@ -3675,17 +3666,7 @@ static int client_send_result(struct client *cl, struct action *act) | ||||
| 			if (!gl_lsname_dlm[0]) | ||||
| 				strcat(result_flags, "NO_GL_LS,"); | ||||
| 		} else { | ||||
| 			int found_lm = 0; | ||||
|  | ||||
| 			if (lm_support_dlm() && lm_is_running_dlm()) | ||||
| 				found_lm++; | ||||
| 			if (lm_support_sanlock() && lm_is_running_sanlock()) | ||||
| 				found_lm++; | ||||
|  | ||||
| 			if (!found_lm) | ||||
| 				strcat(result_flags, "NO_GL_LS,NO_LM"); | ||||
| 			else | ||||
| 				strcat(result_flags, "NO_GL_LS"); | ||||
| 			strcat(result_flags, "NO_GL_LS,"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -3782,8 +3763,7 @@ static int client_send_result(struct client *cl, struct action *act) | ||||
| 	if (dump_fd >= 0) { | ||||
| 		/* To avoid deadlock, send data here after the reply. */ | ||||
| 		send_dump_buf(dump_fd, dump_len); | ||||
| 		if (close(dump_fd)) | ||||
| 			log_error("failed to close dump socket %d", dump_fd); | ||||
| 		close(dump_fd); | ||||
| 	} | ||||
|  | ||||
| 	return rv; | ||||
| @@ -3856,9 +3836,8 @@ static int add_lock_action(struct action *act) | ||||
| 	pthread_mutex_lock(&lockspaces_mutex); | ||||
| 	if (ls_name[0]) | ||||
| 		ls = find_lockspace_name(ls_name); | ||||
| 	pthread_mutex_unlock(&lockspaces_mutex); | ||||
| 	if (!ls) { | ||||
| 		pthread_mutex_unlock(&lockspaces_mutex); | ||||
|  | ||||
| 		if (act->op == LD_OP_UPDATE && act->rt == LD_RT_VG) { | ||||
| 			log_debug("lockspace \"%s\" not found ignored for vg update", ls_name); | ||||
| 			return -ENOLS; | ||||
| @@ -4775,8 +4754,8 @@ static void *client_thread_main(void *arg_in) | ||||
| 			} else { | ||||
| 				pthread_mutex_unlock(&cl->mutex); | ||||
| 			} | ||||
| 		} else | ||||
| 			pthread_mutex_unlock(&client_mutex); | ||||
| 		} | ||||
| 		pthread_mutex_unlock(&client_mutex); | ||||
| 	} | ||||
| out: | ||||
| 	return NULL; | ||||
| @@ -4800,15 +4779,11 @@ static int setup_client_thread(void) | ||||
|  | ||||
| static void close_client_thread(void) | ||||
| { | ||||
| 	int perrno; | ||||
|  | ||||
| 	pthread_mutex_lock(&client_mutex); | ||||
| 	client_stop = 1; | ||||
| 	pthread_cond_signal(&client_cond); | ||||
| 	pthread_mutex_unlock(&client_mutex); | ||||
|  | ||||
| 	if ((perrno = pthread_join(client_thread, NULL))) | ||||
| 		log_error("pthread_join client_thread error %d", perrno); | ||||
| 	pthread_join(client_thread, NULL); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -4932,10 +4907,14 @@ static int get_lockd_vgs(struct list_head *vg_lockd) | ||||
| 				continue; | ||||
|  | ||||
| 			for (lv_cn = md_cn->child; lv_cn; lv_cn = lv_cn->sib) { | ||||
| 				snprintf(find_str_path, PATH_MAX, "%s/lock_type", lv_cn->key); | ||||
| 				lock_type = dm_config_find_str(lv_cn, find_str_path, NULL); | ||||
|  | ||||
| 				if (!lock_type) | ||||
| 					continue; | ||||
|  | ||||
| 				snprintf(find_str_path, PATH_MAX, "%s/lock_args", lv_cn->key); | ||||
| 				lock_args = dm_config_find_str(lv_cn, find_str_path, NULL); | ||||
| 				if (!lock_args) | ||||
| 					continue; | ||||
|  | ||||
| 				snprintf(find_str_path, PATH_MAX, "%s/id", lv_cn->key); | ||||
| 				lv_uuid = dm_config_find_str(lv_cn, find_str_path, NULL); | ||||
| @@ -4981,7 +4960,7 @@ out: | ||||
| 	return rv; | ||||
| } | ||||
|  | ||||
| static char _dm_uuid[DM_UUID_LEN]; | ||||
| static char _dm_uuid[64]; | ||||
|  | ||||
| static char *get_dm_uuid(char *dm_name) | ||||
| { | ||||
| @@ -5200,17 +5179,20 @@ static void adopt_locks(void) | ||||
| 	 * Get list of lockspaces from lock managers. | ||||
| 	 * Get list of VGs from lvmetad with a lockd type. | ||||
| 	 * Get list of active lockd type LVs from /dev. | ||||
| 	 * | ||||
| 	 * ECONNREFUSED means the lock manager is not running. | ||||
| 	 * This is expected for at least one of them. | ||||
| 	 */ | ||||
|  | ||||
| 	if (lm_support_dlm() && lm_is_running_dlm()) { | ||||
| 	if (lm_support_dlm()) { | ||||
| 		rv = lm_get_lockspaces_dlm(&ls_found); | ||||
| 		if (rv < 0) | ||||
| 		if ((rv < 0) && (rv != -ECONNREFUSED)) | ||||
| 			goto fail; | ||||
| 	} | ||||
|  | ||||
| 	if (lm_support_sanlock() && lm_is_running_sanlock()) { | ||||
| 	if (lm_support_sanlock()) { | ||||
| 		rv = lm_get_lockspaces_sanlock(&ls_found); | ||||
| 		if (rv < 0) | ||||
| 		if ((rv < 0) && (rv != -ECONNREFUSED)) | ||||
| 			goto fail; | ||||
| 	} | ||||
|  | ||||
| @@ -5287,7 +5269,7 @@ static void adopt_locks(void) | ||||
| 	list_for_each_entry_safe(ls1, l1safe, &ls_found, list) { | ||||
|  | ||||
| 		/* The dlm global lockspace is special and doesn't match a VG. */ | ||||
| 		if ((ls1->lm_type == LD_LM_DLM) && !strcmp(ls1->name, gl_lsname_dlm)) { | ||||
| 		if (!strcmp(ls1->name, gl_lsname_dlm)) { | ||||
| 			list_del(&ls1->list); | ||||
| 			free(ls1); | ||||
| 			continue; | ||||
|   | ||||
| @@ -508,7 +508,7 @@ lockrv: | ||||
| 	} | ||||
| 	if (rv < 0) { | ||||
| 		log_error("S %s R %s lock_dlm acquire error %d errno %d", ls->name, r->name, rv, errno); | ||||
| 		return -ELMERR; | ||||
| 		return rv; | ||||
| 	} | ||||
|  | ||||
| 	if (rdd->vb) { | ||||
| @@ -581,7 +581,6 @@ int lm_convert_dlm(struct lockspace *ls, struct resource *r, | ||||
| 	} | ||||
| 	if (rv < 0) { | ||||
| 		log_error("S %s R %s convert_dlm error %d", ls->name, r->name, rv); | ||||
| 		rv = -ELMERR; | ||||
| 	} | ||||
| 	return rv; | ||||
| } | ||||
| @@ -655,7 +654,6 @@ int lm_unlock_dlm(struct lockspace *ls, struct resource *r, | ||||
| 			      0, NULL, NULL, NULL); | ||||
| 	if (rv < 0) { | ||||
| 		log_error("S %s R %s unlock_dlm error %d", ls->name, r->name, rv); | ||||
| 		rv = -ELMERR; | ||||
| 	} | ||||
|  | ||||
| 	return rv; | ||||
|   | ||||
| @@ -224,10 +224,7 @@ static int lock_lv_offset_from_args(char *lv_args, uint64_t *lock_lv_offset) | ||||
| 	if (rv < 0) | ||||
| 		return rv; | ||||
|  | ||||
| 	errno = 0; | ||||
| 	*lock_lv_offset = strtoull(offset_str, NULL, 10); | ||||
| 	if (errno) | ||||
| 		return -1; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -356,19 +353,12 @@ int lm_init_vg_sanlock(char *ls_name, char *vg_name, uint32_t flags, char *vg_ar | ||||
| 	log_debug("sanlock daemon version %08x proto %08x", | ||||
| 		  daemon_version, daemon_proto); | ||||
|  | ||||
| 	rv = sanlock_align(&disk); | ||||
| 	if (rv <= 0) { | ||||
| 		if (rv == -EACCES) { | ||||
| 			log_error("S %s init_vg_san sanlock error -EACCES: no permission to access %s", | ||||
| 				  ls_name, disk.path); | ||||
| 			return -EDEVOPEN; | ||||
| 		} else { | ||||
| 			log_error("S %s init_vg_san sanlock error %d trying to get align size of %s", | ||||
| 				  ls_name, rv, disk.path); | ||||
| 			return -EARGS; | ||||
| 		} | ||||
| 	} else | ||||
| 		align_size = rv; | ||||
| 	align_size = sanlock_align(&disk); | ||||
| 	if (align_size <= 0) { | ||||
| 		log_error("S %s init_vg_san bad disk align size %d %s", | ||||
| 			  ls_name, align_size, disk.path); | ||||
| 		return -EARGS; | ||||
| 	} | ||||
|  | ||||
| 	strncpy(ss.name, ls_name, SANLK_NAME_LEN); | ||||
| 	memcpy(ss.host_id_disk.path, disk.path, SANLK_PATH_LEN); | ||||
| @@ -945,9 +935,7 @@ int lm_find_free_lock_sanlock(struct lockspace *ls, uint64_t *free_offset) | ||||
| 	struct lm_sanlock *lms = (struct lm_sanlock *)ls->lm_data; | ||||
| 	struct sanlk_resourced rd; | ||||
| 	uint64_t offset; | ||||
| 	uint64_t start_offset; | ||||
| 	int rv; | ||||
| 	int round = 0; | ||||
|  | ||||
| 	if (daemon_test) { | ||||
| 		*free_offset = (1048576 * LV_LOCK_BEGIN) + (1048576 * (daemon_test_lv_count + 1)); | ||||
| @@ -960,22 +948,9 @@ int lm_find_free_lock_sanlock(struct lockspace *ls, uint64_t *free_offset) | ||||
| 	rd.rs.num_disks = 1; | ||||
| 	strncpy(rd.rs.disks[0].path, lms->ss.host_id_disk.path, SANLK_PATH_LEN-1); | ||||
|  | ||||
| 	if (ls->free_lock_offset) | ||||
| 		offset = ls->free_lock_offset; | ||||
| 	else | ||||
| 		offset = lms->align_size * LV_LOCK_BEGIN; | ||||
|  | ||||
| 	start_offset = offset; | ||||
| 	offset = lms->align_size * LV_LOCK_BEGIN; | ||||
|  | ||||
| 	while (1) { | ||||
| 		if (offset >= start_offset && round) { | ||||
| 			/* This indicates the all space are allocated. */ | ||||
| 			log_debug("S %s init_lv_san read back to start offset %llu", | ||||
| 				ls->name, (unsigned long long)offset); | ||||
| 			rv = -EMSGSIZE; | ||||
| 			return rv; | ||||
| 		} | ||||
|  | ||||
| 		rd.rs.disks[0].offset = offset; | ||||
|  | ||||
| 		memset(rd.rs.name, 0, SANLK_NAME_LEN); | ||||
| @@ -985,14 +960,7 @@ int lm_find_free_lock_sanlock(struct lockspace *ls, uint64_t *free_offset) | ||||
| 			/* This indicates the end of the device is reached. */ | ||||
| 			log_debug("S %s find_free_lock_san read limit offset %llu", | ||||
| 				  ls->name, (unsigned long long)offset); | ||||
|  | ||||
| 			/* remember the NO SPACE offset, if no free area left, | ||||
| 			 * search from this offset after extend */ | ||||
| 			*free_offset = offset; | ||||
|  | ||||
| 			offset = lms->align_size * LV_LOCK_BEGIN; | ||||
| 			round = 1; | ||||
| 			continue; | ||||
| 			return -EMSGSIZE; | ||||
| 		} | ||||
|  | ||||
| 		/* | ||||
| @@ -1460,12 +1428,6 @@ int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode, | ||||
|  | ||||
| 	rv = sanlock_acquire(lms->sock, -1, flags, 1, &rs, &opt); | ||||
|  | ||||
| 	/* | ||||
| 	 * errors: translate the sanlock error number to an lvmlockd error. | ||||
| 	 * We don't want to return an sanlock-specific error number from | ||||
| 	 * this function to code that doesn't recognize sanlock error numbers. | ||||
| 	 */ | ||||
|  | ||||
| 	if (rv == -EAGAIN) { | ||||
| 		/* | ||||
| 		 * It appears that sanlock_acquire returns EAGAIN when we request | ||||
| @@ -1534,26 +1496,6 @@ int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode, | ||||
| 		return -EAGAIN; | ||||
| 	} | ||||
|  | ||||
| 	if (rv == SANLK_AIO_TIMEOUT) { | ||||
| 		/* | ||||
| 		 * sanlock got an i/o timeout when trying to acquire the | ||||
| 		 * lease on disk. | ||||
| 		 */ | ||||
| 		log_debug("S %s R %s lock_san acquire mode %d rv %d", ls->name, r->name, ld_mode, rv); | ||||
| 		*retry = 0; | ||||
| 		return -EAGAIN; | ||||
| 	} | ||||
|  | ||||
| 	if (rv == SANLK_DBLOCK_LVER || rv == SANLK_DBLOCK_MBAL) { | ||||
| 		/* | ||||
| 		 * There was contention with another host for the lease, | ||||
| 		 * and we lost. | ||||
| 		 */ | ||||
| 		log_debug("S %s R %s lock_san acquire mode %d rv %d", ls->name, r->name, ld_mode, rv); | ||||
| 		*retry = 0; | ||||
| 		return -EAGAIN; | ||||
| 	} | ||||
|  | ||||
| 	if (rv == SANLK_ACQUIRE_OWNED_RETRY) { | ||||
| 		/* | ||||
| 		 * The lock is held by a failed host, and will eventually | ||||
| @@ -1604,25 +1546,15 @@ int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode, | ||||
| 		if (rv == -ENOSPC) | ||||
| 			rv = -ELOCKIO; | ||||
|  | ||||
| 		/* | ||||
| 		 * generic error number for sanlock errors that we are not | ||||
| 		 * catching above. | ||||
| 		 */ | ||||
| 		return -ELMERR; | ||||
| 		return rv; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * sanlock acquire success (rv 0) | ||||
| 	 */ | ||||
|  | ||||
| 	if (rds->vb) { | ||||
| 		rv = sanlock_get_lvb(0, rs, (char *)&vb, sizeof(vb)); | ||||
| 		if (rv < 0) { | ||||
| 			log_error("S %s R %s lock_san get_lvb error %d", ls->name, r->name, rv); | ||||
| 			memset(rds->vb, 0, sizeof(struct val_blk)); | ||||
| 			memset(vb_out, 0, sizeof(struct val_blk)); | ||||
| 			/* the lock is still acquired, the vb values considered invalid */ | ||||
| 			rv = 0; | ||||
| 			goto out; | ||||
| 		} | ||||
|  | ||||
| @@ -1675,7 +1607,6 @@ int lm_convert_sanlock(struct lockspace *ls, struct resource *r, | ||||
| 		if (rv < 0) { | ||||
| 			log_error("S %s R %s convert_san set_lvb error %d", | ||||
| 				  ls->name, r->name, rv); | ||||
| 			return -ELMERR; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -1688,35 +1619,14 @@ int lm_convert_sanlock(struct lockspace *ls, struct resource *r, | ||||
| 	if (daemon_test) | ||||
| 		return 0; | ||||
|  | ||||
| 	/* | ||||
| 	 * Don't block waiting for a failed lease to expire since it causes | ||||
| 	 * sanlock_convert to block for a long time, which would prevent this | ||||
| 	 * thread from processing other lock requests. | ||||
| 	 * | ||||
| 	 * FIXME: SANLK_CONVERT_OWNER_NOWAIT is the same as SANLK_ACQUIRE_OWNER_NOWAIT. | ||||
| 	 * Change to use the CONVERT define when the latest sanlock version has it. | ||||
| 	 */ | ||||
| 	flags |= SANLK_ACQUIRE_OWNER_NOWAIT; | ||||
|  | ||||
| 	rv = sanlock_convert(lms->sock, -1, flags, rs); | ||||
| 	if (!rv) | ||||
| 		return 0; | ||||
|  | ||||
| 	switch (rv) { | ||||
| 	case -EAGAIN: | ||||
| 	case SANLK_ACQUIRE_IDLIVE: | ||||
| 	case SANLK_ACQUIRE_OWNED: | ||||
| 	case SANLK_ACQUIRE_OWNED_RETRY: | ||||
| 	case SANLK_ACQUIRE_OTHER: | ||||
| 	case SANLK_AIO_TIMEOUT: | ||||
| 	case SANLK_DBLOCK_LVER: | ||||
| 	case SANLK_DBLOCK_MBAL: | ||||
| 		/* expected errors from known/normal cases like lock contention or io timeouts */ | ||||
| 		log_debug("S %s R %s convert_san error %d", ls->name, r->name, rv); | ||||
| 	if (rv == -EAGAIN) { | ||||
| 		/* FIXME: When could this happen?  Should something different be done? */ | ||||
| 		log_error("S %s R %s convert_san EAGAIN", ls->name, r->name); | ||||
| 		return -EAGAIN; | ||||
| 	default: | ||||
| 	} | ||||
| 	if (rv < 0) { | ||||
| 		log_error("S %s R %s convert_san convert error %d", ls->name, r->name, rv); | ||||
| 		rv = -ELMERR; | ||||
| 	} | ||||
|  | ||||
| 	return rv; | ||||
| @@ -1753,7 +1663,6 @@ static int release_rename(struct lockspace *ls, struct resource *r) | ||||
| 	rv = sanlock_release(lms->sock, -1, SANLK_REL_RENAME, 2, res_args); | ||||
| 	if (rv < 0) { | ||||
| 		log_error("S %s R %s unlock_san release rename error %d", ls->name, r->name, rv); | ||||
| 		rv = -ELMERR; | ||||
| 	} | ||||
|  | ||||
| 	free(res_args); | ||||
| @@ -1810,7 +1719,6 @@ int lm_unlock_sanlock(struct lockspace *ls, struct resource *r, | ||||
| 		if (rv < 0) { | ||||
| 			log_error("S %s R %s unlock_san set_lvb error %d", | ||||
| 				  ls->name, r->name, rv); | ||||
| 			return -ELMERR; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -1829,8 +1737,6 @@ int lm_unlock_sanlock(struct lockspace *ls, struct resource *r, | ||||
|  | ||||
| 	if (rv == -EIO) | ||||
| 		rv = -ELOCKIO; | ||||
| 	else if (rv < 0) | ||||
| 		rv = -ELMERR; | ||||
|  | ||||
| 	return rv; | ||||
| } | ||||
|   | ||||
| @@ -27,14 +27,18 @@ CFLOW_TARGET = lvmpolld | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| CFLAGS += $(EXTRA_EXEC_CFLAGS) | ||||
| INCLUDES += -I$(top_srcdir)/libdaemon/server | ||||
| LDFLAGS += -L$(top_builddir)/libdaemon/server $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) | ||||
| LIBS += $(DAEMON_LIBS) -ldaemonserver -ldevmapper $(PTHREAD_LIBS) | ||||
| LVMLIBS = -ldaemonserver $(LVMINTERNAL_LIBS) -ldevmapper | ||||
|  | ||||
| LIBS += $(PTHREAD_LIBS) | ||||
|  | ||||
| LDFLAGS += -L$(top_builddir)/libdaemon/server $(DAEMON_LDFLAGS) | ||||
| CLDFLAGS += -L$(top_builddir)/libdaemon/server | ||||
| CFLAGS += $(DAEMON_CFLAGS) | ||||
|  | ||||
| lvmpolld: $(OBJECTS) $(top_builddir)/libdaemon/client/libdaemonclient.a \ | ||||
| 		    $(top_builddir)/libdaemon/server/libdaemonserver.a | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBS) | ||||
| 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LVMLIBS) $(LIBS) | ||||
|  | ||||
| install_lvmpolld: lvmpolld | ||||
| 	$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F) | ||||
|   | ||||
| @@ -19,12 +19,10 @@ | ||||
|  | ||||
| #define MIN_ARGV_SIZE  8 | ||||
|  | ||||
| static const char *const polling_ops[] = { | ||||
| 	[PVMOVE] = LVMPD_REQ_PVMOVE, | ||||
| 	[CONVERT] = LVMPD_REQ_CONVERT, | ||||
| 	[MERGE] = LVMPD_REQ_MERGE, | ||||
| 	[MERGE_THIN] = LVMPD_REQ_MERGE_THIN | ||||
| }; | ||||
| static const char *const const polling_ops[] = { [PVMOVE] = LVMPD_REQ_PVMOVE, | ||||
| 						 [CONVERT] = LVMPD_REQ_CONVERT, | ||||
| 						 [MERGE] = LVMPD_REQ_MERGE, | ||||
| 						 [MERGE_THIN] = LVMPD_REQ_MERGE_THIN }; | ||||
|  | ||||
| const char *polling_op(enum poll_type type) | ||||
| { | ||||
|   | ||||
| @@ -207,10 +207,6 @@ Optional feature arguments are: | ||||
| 		   block, then the cache block is invalidated. | ||||
| 		   To enable passthrough mode the cache must be clean. | ||||
|  | ||||
|    metadata2	: use version 2 of the metadata.  This stores the dirty bits | ||||
|                   in a separate btree, which improves speed of shutting | ||||
| 		  down the cache. | ||||
|  | ||||
| A policy called 'default' is always registered.  This is an alias for | ||||
| the policy we currently think is giving best all round performance. | ||||
|  | ||||
| @@ -290,7 +286,7 @@ message, which takes an arbitrary number of cblock ranges.  Each cblock | ||||
| range's end value is "one past the end", meaning 5-10 expresses a range | ||||
| of values from 5 to 9.  Each cblock must be expressed as a decimal | ||||
| value, in the future a variant message that takes cblock ranges | ||||
| expressed in hexadecimal may be needed to better support efficient | ||||
| expressed in hexidecimal may be needed to better support efficient | ||||
| invalidation of larger caches.  The cache must be in passthrough mode | ||||
| when invalidate_cblocks is used. | ||||
|  | ||||
|   | ||||
| @@ -11,57 +11,23 @@ Parameters: <cipher> <key> <iv_offset> <device path> \ | ||||
| 	      <offset> [<#opt_params> <opt_params>] | ||||
|  | ||||
| <cipher> | ||||
|     Encryption cipher, encryption mode and Initial Vector (IV) generator. | ||||
|  | ||||
|     The cipher specifications format is: | ||||
|        cipher[:keycount]-chainmode-ivmode[:ivopts] | ||||
|     Encryption cipher and an optional IV generation mode. | ||||
|     (In format cipher[:keycount]-chainmode-ivmode[:ivopts]). | ||||
|     Examples: | ||||
|        des | ||||
|        aes-cbc-essiv:sha256 | ||||
|        aes-xts-plain64 | ||||
|        serpent-xts-plain64 | ||||
|        twofish-ecb | ||||
|  | ||||
|     Cipher format also supports direct specification with kernel crypt API | ||||
|     format (selected by capi: prefix). The IV specification is the same | ||||
|     as for the first format type. | ||||
|     This format is mainly used for specification of authenticated modes. | ||||
|  | ||||
|     The crypto API cipher specifications format is: | ||||
|         capi:cipher_api_spec-ivmode[:ivopts] | ||||
|     Examples: | ||||
|         capi:cbc(aes)-essiv:sha256 | ||||
|         capi:xts(aes)-plain64 | ||||
|     Examples of authenticated modes: | ||||
|         capi:gcm(aes)-random | ||||
|         capi:authenc(hmac(sha256),xts(aes))-random | ||||
|         capi:rfc7539(chacha20,poly1305)-random | ||||
|  | ||||
|     The /proc/crypto contains a list of curently loaded crypto modes. | ||||
|     /proc/crypto contains supported crypto modes | ||||
|  | ||||
| <key> | ||||
|     Key used for encryption. It is encoded either as a hexadecimal number | ||||
|     or it can be passed as <key_string> prefixed with single colon | ||||
|     character (':') for keys residing in kernel keyring service. | ||||
|     Key used for encryption. It is encoded as a hexadecimal number. | ||||
|     You can only use key sizes that are valid for the selected cipher | ||||
|     in combination with the selected iv mode. | ||||
|     Note that for some iv modes the key string can contain additional | ||||
|     keys (for example IV seed) so the key contains more parts concatenated | ||||
|     into a single string. | ||||
|  | ||||
| <key_string> | ||||
|     The kernel keyring key is identified by string in following format: | ||||
|     <key_size>:<key_type>:<key_description>. | ||||
|  | ||||
| <key_size> | ||||
|     The encryption key size in bytes. The kernel key payload size must match | ||||
|     the value passed in <key_size>. | ||||
|  | ||||
| <key_type> | ||||
|     Either 'logon' or 'user' kernel key type. | ||||
|  | ||||
| <key_description> | ||||
|     The kernel keyring key description crypt target should look for | ||||
|     when loading key of <key_type>. | ||||
|  | ||||
| <keycount> | ||||
|     Multi-key compatibility mode. You can define <keycount> keys and | ||||
|     then sectors are encrypted according to their offsets (sector 0 uses key0; | ||||
| @@ -110,32 +76,6 @@ submit_from_crypt_cpus | ||||
|     thread because it benefits CFQ to have writes submitted using the | ||||
|     same context. | ||||
|  | ||||
| integrity:<bytes>:<type> | ||||
|     The device requires additional <bytes> metadata per-sector stored | ||||
|     in per-bio integrity structure. This metadata must by provided | ||||
|     by underlying dm-integrity target. | ||||
|  | ||||
|     The <type> can be "none" if metadata is used only for persistent IV. | ||||
|  | ||||
|     For Authenticated Encryption with Additional Data (AEAD) | ||||
|     the <type> is "aead". An AEAD mode additionally calculates and verifies | ||||
|     integrity for the encrypted device. The additional space is then | ||||
|     used for storing authentication tag (and persistent IV if needed). | ||||
|  | ||||
| sector_size:<bytes> | ||||
|     Use <bytes> as the encryption unit instead of 512 bytes sectors. | ||||
|     This option can be in range 512 - 4096 bytes and must be power of two. | ||||
|     Virtual device will announce this size as a minimal IO and logical sector. | ||||
|  | ||||
| iv_large_sectors | ||||
|    IV generators will use sector number counted in <sector_size> units | ||||
|    instead of default 512 bytes sectors. | ||||
|  | ||||
|    For example, if <sector_size> is 4096 bytes, plain64 IV for the second | ||||
|    sector will be 8 (without flag) and 1 if iv_large_sectors is present. | ||||
|    The <iv_offset> must be multiple of <sector_size> (in 512 bytes units) | ||||
|    if this flag is specified. | ||||
|  | ||||
| Example scripts | ||||
| =============== | ||||
| LUKS (Linux Unified Key Setup) is now the preferred way to set up disk | ||||
| @@ -145,13 +85,7 @@ https://gitlab.com/cryptsetup/cryptsetup | ||||
| [[ | ||||
| #!/bin/sh | ||||
| # Create a crypt device using dmsetup | ||||
| dmsetup create crypt1 --table "0 `blockdev --getsz $1` crypt aes-cbc-essiv:sha256 babebabebabebabebabebabebabebabe 0 $1 0" | ||||
| ]] | ||||
|  | ||||
| [[ | ||||
| #!/bin/sh | ||||
| # Create a crypt device using dmsetup when encryption key is stored in keyring service | ||||
| dmsetup create crypt2 --table "0 `blockdev --getsize $1` crypt aes-cbc-essiv:sha256 :32:logon:my_prefix:my_key 0 $1 0" | ||||
| dmsetup create crypt1 --table "0 `blockdev --getsize $1` crypt aes-cbc-essiv:sha256 babebabebabebabebabebabebabebabe 0 $1 0" | ||||
| ]] | ||||
|  | ||||
| [[ | ||||
|   | ||||
| @@ -16,12 +16,12 @@ Example scripts | ||||
| [[ | ||||
| #!/bin/sh | ||||
| # Create device delaying rw operation for 500ms | ||||
| echo "0 `blockdev --getsz $1` delay $1 0 500" | dmsetup create delayed | ||||
| echo "0 `blockdev --getsize $1` delay $1 0 500" | dmsetup create delayed | ||||
| ]] | ||||
|  | ||||
| [[ | ||||
| #!/bin/sh | ||||
| # Create device delaying only write operation for 500ms and | ||||
| # splitting reads and writes to different devices $1 $2 | ||||
| echo "0 `blockdev --getsz $1` delay $1 0 0 $2 0 500" | dmsetup create delayed | ||||
| echo "0 `blockdev --getsize $1` delay $1 0 0 $2 0 500" | dmsetup create delayed | ||||
| ]] | ||||
|   | ||||
| @@ -42,7 +42,7 @@ Optional feature parameters: | ||||
|     <direction>: Either 'r' to corrupt reads or 'w' to corrupt writes. | ||||
| 		 'w' is incompatible with drop_writes. | ||||
|     <value>: The value (from 0-255) to write. | ||||
|     <flags>: Perform the replacement only if bio->bi_opf has all the | ||||
|     <flags>: Perform the replacement only if bio->bi_rw has all the | ||||
| 	     selected flags set. | ||||
|  | ||||
| Examples: | ||||
|   | ||||
| @@ -1,199 +0,0 @@ | ||||
| The dm-integrity target emulates a block device that has additional | ||||
| per-sector tags that can be used for storing integrity information. | ||||
|  | ||||
| A general problem with storing integrity tags with every sector is that | ||||
| writing the sector and the integrity tag must be atomic - i.e. in case of | ||||
| crash, either both sector and integrity tag or none of them is written. | ||||
|  | ||||
| To guarantee write atomicity, the dm-integrity target uses journal, it | ||||
| writes sector data and integrity tags into a journal, commits the journal | ||||
| and then copies the data and integrity tags to their respective location. | ||||
|  | ||||
| The dm-integrity target can be used with the dm-crypt target - in this | ||||
| situation the dm-crypt target creates the integrity data and passes them | ||||
| to the dm-integrity target via bio_integrity_payload attached to the bio. | ||||
| In this mode, the dm-crypt and dm-integrity targets provide authenticated | ||||
| disk encryption - if the attacker modifies the encrypted device, an I/O | ||||
| error is returned instead of random data. | ||||
|  | ||||
| The dm-integrity target can also be used as a standalone target, in this | ||||
| mode it calculates and verifies the integrity tag internally. In this | ||||
| mode, the dm-integrity target can be used to detect silent data | ||||
| corruption on the disk or in the I/O path. | ||||
|  | ||||
|  | ||||
| When loading the target for the first time, the kernel driver will format | ||||
| the device. But it will only format the device if the superblock contains | ||||
| zeroes. If the superblock is neither valid nor zeroed, the dm-integrity | ||||
| target can't be loaded. | ||||
|  | ||||
| To use the target for the first time: | ||||
| 1. overwrite the superblock with zeroes | ||||
| 2. load the dm-integrity target with one-sector size, the kernel driver | ||||
| 	will format the device | ||||
| 3. unload the dm-integrity target | ||||
| 4. read the "provided_data_sectors" value from the superblock | ||||
| 5. load the dm-integrity target with the the target size | ||||
| 	"provided_data_sectors" | ||||
| 6. if you want to use dm-integrity with dm-crypt, load the dm-crypt target | ||||
| 	with the size "provided_data_sectors" | ||||
|  | ||||
|  | ||||
| Target arguments: | ||||
|  | ||||
| 1. the underlying block device | ||||
|  | ||||
| 2. the number of reserved sector at the beginning of the device - the | ||||
| 	dm-integrity won't read of write these sectors | ||||
|  | ||||
| 3. the size of the integrity tag (if "-" is used, the size is taken from | ||||
| 	the internal-hash algorithm) | ||||
|  | ||||
| 4. mode: | ||||
| 	D - direct writes (without journal) - in this mode, journaling is | ||||
| 		not used and data sectors and integrity tags are written | ||||
| 		separately. In case of crash, it is possible that the data | ||||
| 		and integrity tag doesn't match. | ||||
| 	J - journaled writes - data and integrity tags are written to the | ||||
| 		journal and atomicity is guaranteed. In case of crash, | ||||
| 		either both data and tag or none of them are written. The | ||||
| 		journaled mode degrades write throughput twice because the | ||||
| 		data have to be written twice. | ||||
| 	R - recovery mode - in this mode, journal is not replayed, | ||||
| 		checksums are not checked and writes to the device are not | ||||
| 		allowed. This mode is useful for data recovery if the | ||||
| 		device cannot be activated in any of the other standard | ||||
| 		modes. | ||||
|  | ||||
| 5. the number of additional arguments | ||||
|  | ||||
| Additional arguments: | ||||
|  | ||||
| journal_sectors:number | ||||
| 	The size of journal, this argument is used only if formatting the | ||||
| 	device. If the device is already formatted, the value from the | ||||
| 	superblock is used. | ||||
|  | ||||
| interleave_sectors:number | ||||
| 	The number of interleaved sectors. This values is rounded down to | ||||
| 	a power of two. If the device is already formatted, the value from | ||||
| 	the superblock is used. | ||||
|  | ||||
| buffer_sectors:number | ||||
| 	The number of sectors in one buffer. The value is rounded down to | ||||
| 	a power of two. | ||||
|  | ||||
| 	The tag area is accessed using buffers, the buffer size is | ||||
| 	configurable. The large buffer size means that the I/O size will | ||||
| 	be larger, but there could be less I/Os issued. | ||||
|  | ||||
| journal_watermark:number | ||||
| 	The journal watermark in percents. When the size of the journal | ||||
| 	exceeds this watermark, the thread that flushes the journal will | ||||
| 	be started. | ||||
|  | ||||
| commit_time:number | ||||
| 	Commit time in milliseconds. When this time passes, the journal is | ||||
| 	written. The journal is also written immediatelly if the FLUSH | ||||
| 	request is received. | ||||
|  | ||||
| internal_hash:algorithm(:key)	(the key is optional) | ||||
| 	Use internal hash or crc. | ||||
| 	When this argument is used, the dm-integrity target won't accept | ||||
| 	integrity tags from the upper target, but it will automatically | ||||
| 	generate and verify the integrity tags. | ||||
|  | ||||
| 	You can use a crc algorithm (such as crc32), then integrity target | ||||
| 	will protect the data against accidental corruption. | ||||
| 	You can also use a hmac algorithm (for example | ||||
| 	"hmac(sha256):0123456789abcdef"), in this mode it will provide | ||||
| 	cryptographic authentication of the data without encryption. | ||||
|  | ||||
| 	When this argument is not used, the integrity tags are accepted | ||||
| 	from an upper layer target, such as dm-crypt. The upper layer | ||||
| 	target should check the validity of the integrity tags. | ||||
|  | ||||
| journal_crypt:algorithm(:key)	(the key is optional) | ||||
| 	Encrypt the journal using given algorithm to make sure that the | ||||
| 	attacker can't read the journal. You can use a block cipher here | ||||
| 	(such as "cbc(aes)") or a stream cipher (for example "chacha20", | ||||
| 	"salsa20", "ctr(aes)" or "ecb(arc4)"). | ||||
|  | ||||
| 	The journal contains history of last writes to the block device, | ||||
| 	an attacker reading the journal could see the last sector nubmers | ||||
| 	that were written. From the sector numbers, the attacker can infer | ||||
| 	the size of files that were written. To protect against this | ||||
| 	situation, you can encrypt the journal. | ||||
|  | ||||
| journal_mac:algorithm(:key)	(the key is optional) | ||||
| 	Protect sector numbers in the journal from accidental or malicious | ||||
| 	modification. To protect against accidental modification, use a | ||||
| 	crc algorithm, to protect against malicious modification, use a | ||||
| 	hmac algorithm with a key. | ||||
|  | ||||
| 	This option is not needed when using internal-hash because in this | ||||
| 	mode, the integrity of journal entries is checked when replaying | ||||
| 	the journal. Thus, modified sector number would be detected at | ||||
| 	this stage. | ||||
|  | ||||
| block_size:number | ||||
| 	The size of a data block in bytes.  The larger the block size the | ||||
| 	less overhead there is for per-block integrity metadata. | ||||
| 	Supported values are 512, 1024, 2048 and 4096 bytes.  If not | ||||
| 	specified the default block size is 512 bytes. | ||||
|  | ||||
| The journal mode (D/J), buffer_sectors, journal_watermark, commit_time can | ||||
| be changed when reloading the target (load an inactive table and swap the | ||||
| tables with suspend and resume). The other arguments should not be changed | ||||
| when reloading the target because the layout of disk data depend on them | ||||
| and the reloaded target would be non-functional. | ||||
|  | ||||
|  | ||||
| The layout of the formatted block device: | ||||
| * reserved sectors (they are not used by this target, they can be used for | ||||
|   storing LUKS metadata or for other purpose), the size of the reserved | ||||
|   area is specified in the target arguments | ||||
| * superblock (4kiB) | ||||
| 	* magic string - identifies that the device was formatted | ||||
| 	* version | ||||
| 	* log2(interleave sectors) | ||||
| 	* integrity tag size | ||||
| 	* the number of journal sections | ||||
| 	* provided data sectors - the number of sectors that this target | ||||
| 	  provides (i.e. the size of the device minus the size of all | ||||
| 	  metadata and padding). The user of this target should not send | ||||
| 	  bios that access data beyond the "provided data sectors" limit. | ||||
| 	* flags - a flag is set if journal_mac is used | ||||
| * journal | ||||
| 	The journal is divided into sections, each section contains: | ||||
| 	* metadata area (4kiB), it contains journal entries | ||||
| 	  every journal entry contains: | ||||
| 		* logical sector (specifies where the data and tag should | ||||
| 		  be written) | ||||
| 		* last 8 bytes of data | ||||
| 		* integrity tag (the size is specified in the superblock) | ||||
| 	    every metadata sector ends with | ||||
| 		* mac (8-bytes), all the macs in 8 metadata sectors form a | ||||
| 		  64-byte value. It is used to store hmac of sector | ||||
| 		  numbers in the journal section, to protect against a | ||||
| 		  possibility that the attacker tampers with sector | ||||
| 		  numbers in the journal. | ||||
| 		* commit id | ||||
| 	* data area (the size is variable; it depends on how many journal | ||||
| 	  entries fit into the metadata area) | ||||
| 	    every sector in the data area contains: | ||||
| 		* data (504 bytes of data, the last 8 bytes are stored in | ||||
| 		  the journal entry) | ||||
| 		* commit id | ||||
| 	To test if the whole journal section was written correctly, every | ||||
| 	512-byte sector of the journal ends with 8-byte commit id. If the | ||||
| 	commit id matches on all sectors in a journal section, then it is | ||||
| 	assumed that the section was written correctly. If the commit id | ||||
| 	doesn't match, the section was written partially and it should not | ||||
| 	be replayed. | ||||
| * one or more runs of interleaved tags and data. Each run contains: | ||||
| 	* tag area - it contains integrity tags. There is one tag for each | ||||
| 	  sector in the data area | ||||
| 	* data area - it contains data sectors. The number of data sectors | ||||
| 	  in one run must be a power of two. log2 of this value is stored | ||||
| 	  in the superblock. | ||||
| @@ -16,15 +16,15 @@ Example scripts | ||||
| [[ | ||||
| #!/bin/sh | ||||
| # Create an identity mapping for a device | ||||
| echo "0 `blockdev --getsz $1` linear $1 0" | dmsetup create identity | ||||
| echo "0 `blockdev --getsize $1` linear $1 0" | dmsetup create identity | ||||
| ]] | ||||
|  | ||||
|  | ||||
| [[ | ||||
| #!/bin/sh | ||||
| # Join 2 devices together | ||||
| size1=`blockdev --getsz $1` | ||||
| size2=`blockdev --getsz $2` | ||||
| size1=`blockdev --getsize $1` | ||||
| size2=`blockdev --getsize $2` | ||||
| echo "0 $size1 linear $1 0 | ||||
| $size1 $size2 linear $2 0" | dmsetup create joined | ||||
| ]] | ||||
| @@ -44,7 +44,7 @@ if (!defined($dev)) { | ||||
|         die("Please specify a device.\n"); | ||||
| } | ||||
|  | ||||
| my $dev_size = `blockdev --getsz $dev`; | ||||
| my $dev_size = `blockdev --getsize $dev`; | ||||
| my $extents = int($dev_size / $extent_size) - | ||||
|               (($dev_size % $extent_size) ? 1 : 0); | ||||
|  | ||||
|   | ||||
| @@ -14,14 +14,14 @@ Log Ordering | ||||
|  | ||||
| We log things in order of completion once we are sure the write is no longer in | ||||
| cache.  This means that normal WRITE requests are not actually logged until the | ||||
| next REQ_PREFLUSH request.  This is to make it easier for userspace to replay | ||||
| the log in a way that correlates to what is on disk and not what is in cache, | ||||
| to make it easier to detect improper waiting/flushing. | ||||
| next REQ_FLUSH request.  This is to make it easier for userspace to replay the | ||||
| log in a way that correlates to what is on disk and not what is in cache, to | ||||
| make it easier to detect improper waiting/flushing. | ||||
|  | ||||
| This works by attaching all WRITE requests to a list once the write completes. | ||||
| Once we see a REQ_PREFLUSH request we splice this list onto the request and once | ||||
| Once we see a REQ_FLUSH request we splice this list onto the request and once | ||||
| the FLUSH request completes we log all of the WRITEs and then the FLUSH.  Only | ||||
| completed WRITEs, at the time the REQ_PREFLUSH is issued, are added in order to | ||||
| completed WRITEs, at the time the REQ_FLUSH is issued, are added in order to | ||||
| simulate the worst case scenario with regard to power failures.  Consider the | ||||
| following example (W means write, C means complete): | ||||
|  | ||||
|   | ||||
| @@ -14,12 +14,8 @@ The target is named "raid" and it accepts the following parameters: | ||||
|     <#raid_devs> <metadata_dev0> <dev0> [.. <metadata_devN> <devN>] | ||||
|  | ||||
| <raid_type>: | ||||
|   raid0		RAID0 striping (no resilience) | ||||
|   raid1		RAID1 mirroring | ||||
|   raid4		RAID4 with dedicated last parity disk | ||||
|   raid5_n 	RAID5 with dedicated last parity disk supporting takeover | ||||
| 		Same as raid4 | ||||
| 		-Transitory layout | ||||
|   raid4		RAID4 dedicated parity disk | ||||
|   raid5_la	RAID5 left asymmetric | ||||
| 		- rotating parity 0 with data continuation | ||||
|   raid5_ra	RAID5 right asymmetric | ||||
| @@ -34,19 +30,7 @@ The target is named "raid" and it accepts the following parameters: | ||||
| 		- rotating parity N (right-to-left) with data restart | ||||
|   raid6_nc	RAID6 N continue | ||||
| 		- rotating parity N (right-to-left) with data continuation | ||||
|   raid6_n_6	RAID6 with dedicate parity disks | ||||
| 		- parity and Q-syndrome on the last 2 disks; | ||||
| 		  layout for takeover from/to raid4/raid5_n | ||||
|   raid6_la_6	Same as "raid_la" plus dedicated last Q-syndrome disk | ||||
| 		- layout for takeover from raid5_la from/to raid6 | ||||
|   raid6_ra_6	Same as "raid5_ra" dedicated last Q-syndrome disk | ||||
| 		- layout for takeover from raid5_ra from/to raid6 | ||||
|   raid6_ls_6	Same as "raid5_ls" dedicated last Q-syndrome disk | ||||
| 		- layout for takeover from raid5_ls from/to raid6 | ||||
|   raid6_rs_6	Same as "raid5_rs" dedicated last Q-syndrome disk | ||||
| 		- layout for takeover from raid5_rs from/to raid6 | ||||
|   raid10        Various RAID10 inspired algorithms chosen by additional params | ||||
| 		(see raid10_format and raid10_copies below) | ||||
| 		- RAID10: Striped Mirrors (aka 'Striping on top of mirrors') | ||||
| 		- RAID1E: Integrated Adjacent Stripe Mirroring | ||||
| 		- RAID1E: Integrated Offset Stripe Mirroring | ||||
| @@ -132,57 +116,10 @@ The target is named "raid" and it accepts the following parameters: | ||||
| 		Here we see layouts closely akin to 'RAID1E - Integrated | ||||
| 		Offset Stripe Mirroring'. | ||||
|  | ||||
|         [delta_disks <N>] | ||||
| 		The delta_disks option value (-251 < N < +251) triggers | ||||
| 		device removal (negative value) or device addition (positive | ||||
| 		value) to any reshape supporting raid levels 4/5/6 and 10. | ||||
| 		RAID levels 4/5/6 allow for addition of devices (metadata | ||||
| 		and data device tuple), raid10_near and raid10_offset only | ||||
| 		allow for device addition. raid10_far does not support any | ||||
| 		reshaping at all. | ||||
| 		A minimum of devices have to be kept to enforce resilience, | ||||
| 		which is 3 devices for raid4/5 and 4 devices for raid6. | ||||
|  | ||||
|         [data_offset <sectors>] | ||||
| 		This option value defines the offset into each data device | ||||
| 		where the data starts. This is used to provide out-of-place | ||||
| 		reshaping space to avoid writing over data whilst | ||||
| 		changing the layout of stripes, hence an interruption/crash | ||||
| 		may happen at any time without the risk of losing data. | ||||
| 		E.g. when adding devices to an existing raid set during | ||||
| 		forward reshaping, the out-of-place space will be allocated | ||||
| 		at the beginning of each raid device. The kernel raid4/5/6/10 | ||||
| 		MD personalities supporting such device addition will read the data from | ||||
| 		the existing first stripes (those with smaller number of stripes) | ||||
| 		starting at data_offset to fill up a new stripe with the larger | ||||
| 		number of stripes, calculate the redundancy blocks (CRC/Q-syndrome) | ||||
| 		and write that new stripe to offset 0. Same will be applied to all | ||||
| 		N-1 other new stripes. This out-of-place scheme is used to change | ||||
| 		the RAID type (i.e. the allocation algorithm) as well, e.g. | ||||
| 		changing from raid5_ls to raid5_n. | ||||
|  | ||||
| 	[journal_dev <dev>] | ||||
| 		This option adds a journal device to raid4/5/6 raid sets and | ||||
| 		uses it to close the 'write hole' caused by the non-atomic updates | ||||
| 		to the component devices which can cause data loss during recovery. | ||||
| 		The journal device is used as writethrough thus causing writes to | ||||
| 		be throttled versus non-journaled raid4/5/6 sets. | ||||
| 		Takeover/reshape is not possible with a raid4/5/6 journal device; | ||||
| 		it has to be deconfigured before requesting these. | ||||
|  | ||||
| 	[journal_mode <mode>] | ||||
| 		This option sets the caching mode on journaled raid4/5/6 raid sets | ||||
| 		(see 'journal_dev <dev>' above) to 'writethrough' or 'writeback'. | ||||
| 		If 'writeback' is selected the journal device has to be resilient | ||||
| 		and must not suffer from the 'write hole' problem itself (e.g. use | ||||
| 		raid1 or raid10) to avoid a single point of failure. | ||||
|  | ||||
| <#raid_devs>: The number of devices composing the array. | ||||
| 	Each device consists of two entries.  The first is the device | ||||
| 	containing the metadata (if any); the second is the one containing the | ||||
| 	data. A Maximum of 64 metadata/data device entries are supported | ||||
| 	up to target version 1.8.0. | ||||
| 	1.9.0 supports up to 253 which is enforced by the used MD kernel runtime. | ||||
| 	data. | ||||
|  | ||||
| 	If a drive has failed or is missing at creation time, a '-' can be | ||||
| 	given for both the metadata and data drives for a given position. | ||||
| @@ -258,14 +195,6 @@ recovery.  Here is a fuller description of the individual fields: | ||||
| 			in RAID1/10 or wrong parity values found in RAID4/5/6. | ||||
| 			This value is valid only after a "check" of the array | ||||
| 			is performed.  A healthy array has a 'mismatch_cnt' of 0. | ||||
| 	<data_offset>   The current data offset to the start of the user data on | ||||
| 			each component device of a raid set (see the respective | ||||
| 			raid parameter to support out-of-place reshaping). | ||||
| 	<journal_char>	'A' - active write-through journal device. | ||||
| 			'a' - active write-back journal device. | ||||
| 			'D' - dead journal device. | ||||
| 			'-' - no journal device. | ||||
|  | ||||
|  | ||||
| Message Interface | ||||
| ----------------- | ||||
| @@ -278,6 +207,7 @@ include: | ||||
| 	"recover"- Initiate/continue a recover process. | ||||
| 	"check"  - Initiate a check (i.e. a "scrub") of the array. | ||||
| 	"repair" - Initiate a repair of the array. | ||||
| 	"reshape"- Currently unsupported (-EINVAL). | ||||
|  | ||||
|  | ||||
| Discard Support | ||||
| @@ -327,19 +257,3 @@ Version History | ||||
| 1.5.2   'mismatch_cnt' is zero unless [last_]sync_action is "check". | ||||
| 1.6.0   Add discard support (and devices_handle_discard_safely module param). | ||||
| 1.7.0   Add support for MD RAID0 mappings. | ||||
| 1.8.0   Explicitly check for compatible flags in the superblock metadata | ||||
| 	and reject to start the raid set if any are set by a newer | ||||
| 	target version, thus avoiding data corruption on a raid set | ||||
| 	with a reshape in progress. | ||||
| 1.9.0   Add support for RAID level takeover/reshape/region size | ||||
| 	and set size reduction. | ||||
| 1.9.1   Fix activation of existing RAID 4/10 mapped devices | ||||
| 1.9.2   Don't emit '- -' on the status table line in case the constructor | ||||
| 	fails reading a superblock. Correctly emit 'maj:min1 maj:min2' and | ||||
| 	'D' on the status line.  If '- -' is passed into the constructor, emit | ||||
| 	'- -' on the table line and '-' as the status line health character. | ||||
| 1.10.0  Add support for raid4/5/6 journal device | ||||
| 1.10.1  Fix data corruption on reshape request | ||||
| 1.11.0  Fix table line argument order | ||||
| 	(wrong raid10_copies/raid10_format sequence) | ||||
| 1.11.1  Add raid4/5/6 journal write-back support via journal_mode option | ||||
|   | ||||
| @@ -37,9 +37,9 @@ if (!$num_devs) { | ||||
|         die("Specify at least one device\n"); | ||||
| } | ||||
|  | ||||
| $min_dev_size = `blockdev --getsz $devs[0]`; | ||||
| $min_dev_size = `blockdev --getsize $devs[0]`; | ||||
| for ($i = 1; $i < $num_devs; $i++) { | ||||
|         my $this_size = `blockdev --getsz $devs[$i]`; | ||||
|         my $this_size = `blockdev --getsize $devs[$i]`; | ||||
|         $min_dev_size = ($min_dev_size < $this_size) ? | ||||
|                         $min_dev_size : $this_size; | ||||
| } | ||||
|   | ||||
| @@ -123,7 +123,7 @@ Assume that you have volumes vg1/switch0 vg1/switch1 vg1/switch2 with | ||||
| the same size. | ||||
|  | ||||
| Create a switch device with 64kB region size: | ||||
|     dmsetup create switch --table "0 `blockdev --getsz /dev/vg1/switch0` | ||||
|     dmsetup create switch --table "0 `blockdev --getsize /dev/vg1/switch0` | ||||
| 	switch 3 128 0 /dev/vg1/switch0 0 /dev/vg1/switch1 0 /dev/vg1/switch2 0" | ||||
|  | ||||
| Set mappings for the first 7 entries to point to devices switch0, switch1, | ||||
|   | ||||
| @@ -1,144 +0,0 @@ | ||||
| dm-zoned | ||||
| ======== | ||||
|  | ||||
| The dm-zoned device mapper target exposes a zoned block device (ZBC and | ||||
| ZAC compliant devices) as a regular block device without any write | ||||
| pattern constraints. In effect, it implements a drive-managed zoned | ||||
| block device which hides from the user (a file system or an application | ||||
| doing raw block device accesses) the sequential write constraints of | ||||
| host-managed zoned block devices and can mitigate the potential | ||||
| device-side performance degradation due to excessive random writes on | ||||
| host-aware zoned block devices. | ||||
|  | ||||
| For a more detailed description of the zoned block device models and | ||||
| their constraints see (for SCSI devices): | ||||
|  | ||||
| http://www.t10.org/drafts.htm#ZBC_Family | ||||
|  | ||||
| and (for ATA devices): | ||||
|  | ||||
| http://www.t13.org/Documents/UploadedDocuments/docs2015/di537r05-Zoned_Device_ATA_Command_Set_ZAC.pdf | ||||
|  | ||||
| The dm-zoned implementation is simple and minimizes system overhead (CPU | ||||
| and memory usage as well as storage capacity loss). For a 10TB | ||||
| host-managed disk with 256 MB zones, dm-zoned memory usage per disk | ||||
| instance is at most 4.5 MB and as little as 5 zones will be used | ||||
| internally for storing metadata and performaing reclaim operations. | ||||
|  | ||||
| dm-zoned target devices are formatted and checked using the dmzadm | ||||
| utility available at: | ||||
|  | ||||
| https://github.com/hgst/dm-zoned-tools | ||||
|  | ||||
| Algorithm | ||||
| ========= | ||||
|  | ||||
| dm-zoned implements an on-disk buffering scheme to handle non-sequential | ||||
| write accesses to the sequential zones of a zoned block device. | ||||
| Conventional zones are used for caching as well as for storing internal | ||||
| metadata. | ||||
|  | ||||
| The zones of the device are separated into 2 types: | ||||
|  | ||||
| 1) Metadata zones: these are conventional zones used to store metadata. | ||||
| Metadata zones are not reported as useable capacity to the user. | ||||
|  | ||||
| 2) Data zones: all remaining zones, the vast majority of which will be | ||||
| sequential zones used exclusively to store user data. The conventional | ||||
| zones of the device may be used also for buffering user random writes. | ||||
| Data in these zones may be directly mapped to the conventional zone, but | ||||
| later moved to a sequential zone so that the conventional zone can be | ||||
| reused for buffering incoming random writes. | ||||
|  | ||||
| dm-zoned exposes a logical device with a sector size of 4096 bytes, | ||||
| irrespective of the physical sector size of the backend zoned block | ||||
| device being used. This allows reducing the amount of metadata needed to | ||||
| manage valid blocks (blocks written). | ||||
|  | ||||
| The on-disk metadata format is as follows: | ||||
|  | ||||
| 1) The first block of the first conventional zone found contains the | ||||
| super block which describes the on disk amount and position of metadata | ||||
| blocks. | ||||
|  | ||||
| 2) Following the super block, a set of blocks is used to describe the | ||||
| mapping of the logical device blocks. The mapping is done per chunk of | ||||
| blocks, with the chunk size equal to the zoned block device size. The | ||||
| mapping table is indexed by chunk number and each mapping entry | ||||
| indicates the zone number of the device storing the chunk of data. Each | ||||
| mapping entry may also indicate if the zone number of a conventional | ||||
| zone used to buffer random modification to the data zone. | ||||
|  | ||||
| 3) A set of blocks used to store bitmaps indicating the validity of | ||||
| blocks in the data zones follows the mapping table. A valid block is | ||||
| defined as a block that was written and not discarded. For a buffered | ||||
| data chunk, a block is always valid only in the data zone mapping the | ||||
| chunk or in the buffer zone of the chunk. | ||||
|  | ||||
| For a logical chunk mapped to a conventional zone, all write operations | ||||
| are processed by directly writing to the zone. If the mapping zone is a | ||||
| sequential zone, the write operation is processed directly only if the | ||||
| write offset within the logical chunk is equal to the write pointer | ||||
| offset within of the sequential data zone (i.e. the write operation is | ||||
| aligned on the zone write pointer). Otherwise, write operations are | ||||
| processed indirectly using a buffer zone. In that case, an unused | ||||
| conventional zone is allocated and assigned to the chunk being | ||||
| accessed. Writing a block to the buffer zone of a chunk will | ||||
| automatically invalidate the same block in the sequential zone mapping | ||||
| the chunk. If all blocks of the sequential zone become invalid, the zone | ||||
| is freed and the chunk buffer zone becomes the primary zone mapping the | ||||
| chunk, resulting in native random write performance similar to a regular | ||||
| block device. | ||||
|  | ||||
| Read operations are processed according to the block validity | ||||
| information provided by the bitmaps. Valid blocks are read either from | ||||
| the sequential zone mapping a chunk, or if the chunk is buffered, from | ||||
| the buffer zone assigned. If the accessed chunk has no mapping, or the | ||||
| accessed blocks are invalid, the read buffer is zeroed and the read | ||||
| operation terminated. | ||||
|  | ||||
| After some time, the limited number of convnetional zones available may | ||||
| be exhausted (all used to map chunks or buffer sequential zones) and | ||||
| unaligned writes to unbuffered chunks become impossible. To avoid this | ||||
| situation, a reclaim process regularly scans used conventional zones and | ||||
| tries to reclaim the least recently used zones by copying the valid | ||||
| blocks of the buffer zone to a free sequential zone. Once the copy | ||||
| completes, the chunk mapping is updated to point to the sequential zone | ||||
| and the buffer zone freed for reuse. | ||||
|  | ||||
| Metadata Protection | ||||
| =================== | ||||
|  | ||||
| To protect metadata against corruption in case of sudden power loss or | ||||
| system crash, 2 sets of metadata zones are used. One set, the primary | ||||
| set, is used as the main metadata region, while the secondary set is | ||||
| used as a staging area. Modified metadata is first written to the | ||||
| secondary set and validated by updating the super block in the secondary | ||||
| set, a generation counter is used to indicate that this set contains the | ||||
| newest metadata. Once this operation completes, in place of metadata | ||||
| block updates can be done in the primary metadata set. This ensures that | ||||
| one of the set is always consistent (all modifications committed or none | ||||
| at all). Flush operations are used as a commit point. Upon reception of | ||||
| a flush request, metadata modification activity is temporarily blocked | ||||
| (for both incoming BIO processing and reclaim process) and all dirty | ||||
| metadata blocks are staged and updated. Normal operation is then | ||||
| resumed. Flushing metadata thus only temporarily delays write and | ||||
| discard requests. Read requests can be processed concurrently while | ||||
| metadata flush is being executed. | ||||
|  | ||||
| Usage | ||||
| ===== | ||||
|  | ||||
| A zoned block device must first be formatted using the dmzadm tool. This | ||||
| will analyze the device zone configuration, determine where to place the | ||||
| metadata sets on the device and initialize the metadata sets. | ||||
|  | ||||
| Ex: | ||||
|  | ||||
| dmzadm --format /dev/sdxx | ||||
|  | ||||
| For a formatted device, the target can be created normally with the | ||||
| dmsetup utility. The only parameter that dm-zoned requires is the | ||||
| underlying zoned block device name. Ex: | ||||
|  | ||||
| echo "0 `blockdev --getsize ${dev}` zoned ${dev}" | dmsetup create dmz-`basename ${dev}` | ||||
| @@ -127,9 +127,6 @@ | ||||
| /* Path to dmeventd pidfile. */ | ||||
| #undef DMEVENTD_PIDFILE | ||||
|  | ||||
| /* Define to 1 to enable the device-mapper filemap daemon. */ | ||||
| #undef DMFILEMAPD | ||||
|  | ||||
| /* Define to enable compat protocol */ | ||||
| #undef DM_COMPAT | ||||
|  | ||||
| @@ -148,9 +145,6 @@ | ||||
| /* Library version */ | ||||
| #undef DM_LIB_VERSION | ||||
|  | ||||
| /* Path to fsadm binary. */ | ||||
| #undef FSADM_PATH | ||||
|  | ||||
| /* Define to 1 if you have the `alarm' function. */ | ||||
| #undef HAVE_ALARM | ||||
|  | ||||
| @@ -494,9 +488,6 @@ | ||||
| /* Define to 1 if you have the <sys/file.h> header file. */ | ||||
| #undef HAVE_SYS_FILE_H | ||||
|  | ||||
| /* Define to 1 if you have the <sys/inotify.h> header file. */ | ||||
| #undef HAVE_SYS_INOTIFY_H | ||||
|  | ||||
| /* Define to 1 if you have the <sys/ioctl.h> header file. */ | ||||
| #undef HAVE_SYS_IOCTL_H | ||||
|  | ||||
| @@ -632,9 +623,6 @@ | ||||
| /* Define to 1 to include code that uses lvmpolld. */ | ||||
| #undef LVMPOLLD_SUPPORT | ||||
|  | ||||
| /* configure command line used */ | ||||
| #undef LVM_CONFIGURE_LINE | ||||
|  | ||||
| /* Path to lvm binary. */ | ||||
| #undef LVM_PATH | ||||
|  | ||||
| @@ -685,6 +673,9 @@ | ||||
| /* Define to 1 to include the LVM readline shell. */ | ||||
| #undef READLINE_SUPPORT | ||||
|  | ||||
| /* Define to 1 to include built-in support for replicators. */ | ||||
| #undef REPLICATOR_INTERNAL | ||||
|  | ||||
| /* Define as the return type of signal handlers (`int' or `void'). */ | ||||
| #undef RETSIGTYPE | ||||
|  | ||||
|   | ||||
| @@ -36,6 +36,10 @@ ifeq ("@RAID@", "shared") | ||||
|   SUBDIRS += raid | ||||
| endif | ||||
|  | ||||
| ifeq ("@REPLICATORS@", "shared") | ||||
|   SUBDIRS += replicator | ||||
| endif | ||||
|  | ||||
| ifeq ("@THIN@", "shared") | ||||
|   SUBDIRS += thin | ||||
| endif | ||||
| @@ -44,10 +48,6 @@ ifeq ("@CACHE@", "shared") | ||||
|   SUBDIRS += cache_segtype | ||||
| endif | ||||
|  | ||||
| ifeq ("@CLUSTER@", "shared") | ||||
|   SUBDIRS += locking | ||||
| endif | ||||
|  | ||||
| SOURCES =\ | ||||
| 	activate/activate.c \ | ||||
| 	cache/lvmcache.c \ | ||||
| @@ -96,13 +96,13 @@ SOURCES =\ | ||||
| 	metadata/lv_manip.c \ | ||||
| 	metadata/merge.c \ | ||||
| 	metadata/metadata.c \ | ||||
| 	metadata/metadata-liblvm.c \ | ||||
| 	metadata/mirror.c \ | ||||
| 	metadata/pool_manip.c \ | ||||
| 	metadata/pv.c \ | ||||
| 	metadata/pv_manip.c \ | ||||
| 	metadata/pv_map.c \ | ||||
| 	metadata/raid_manip.c \ | ||||
| 	metadata/replicator_manip.c \ | ||||
| 	metadata/segtype.c \ | ||||
| 	metadata/snapshot_manip.c \ | ||||
| 	metadata/thin_manip.c \ | ||||
| @@ -149,6 +149,10 @@ ifeq ("@CLUSTER@", "internal") | ||||
|   SOURCES += locking/cluster_locking.c | ||||
| endif | ||||
|  | ||||
| ifeq ("@CLUSTER@", "shared") | ||||
|   SUBDIRS += locking | ||||
| endif | ||||
|  | ||||
| ifeq ("@SNAPSHOTS@", "internal") | ||||
|   SOURCES += snapshot/snapshot.c | ||||
| endif | ||||
| @@ -161,6 +165,10 @@ ifeq ("@RAID@", "internal") | ||||
|   SOURCES += raid/raid.c | ||||
| endif | ||||
|  | ||||
| ifeq ("@REPLICATORS@", "internal") | ||||
|   SOURCES += replicator/replicator.c | ||||
| endif | ||||
|  | ||||
| ifeq ("@THIN@", "internal") | ||||
|   SOURCES += thin/thin.c | ||||
| endif | ||||
| @@ -196,6 +204,11 @@ ifeq ("@BUILD_LVMLOCKD@", "yes") | ||||
| 	locking/lvmlockd.c | ||||
| endif | ||||
|  | ||||
| ifeq ("@DMEVENTD@", "yes") | ||||
|   CLDFLAGS += -L$(top_builddir)/daemons/dmeventd | ||||
|   LIBS += -ldevmapper-event | ||||
| endif | ||||
|  | ||||
| LIB_NAME = liblvm-internal | ||||
| LIB_STATIC = $(LIB_NAME).a | ||||
|  | ||||
| @@ -207,6 +220,7 @@ ifeq ($(MAKECMDGOALS),distclean) | ||||
| 	mirror \ | ||||
| 	notify \ | ||||
| 	raid \ | ||||
| 	replicator \ | ||||
| 	thin \ | ||||
| 	cache_segtype \ | ||||
| 	locking | ||||
| @@ -215,10 +229,10 @@ endif | ||||
| CFLOW_LIST = $(SOURCES) | ||||
| CFLOW_LIST_TARGET = $(LIB_NAME).cflow | ||||
|  | ||||
| PROGS_CFLAGS = $(BLKID_CFLAGS) $(UDEV_CFLAGS) | ||||
|  | ||||
| include $(top_builddir)/make.tmpl | ||||
|  | ||||
| CFLAGS += $(BLKID_CFLAGS) $(UDEV_CFLAGS) $(VALGRIND_CFLAGS) | ||||
|  | ||||
| $(SUBDIRS): $(LIB_STATIC) | ||||
|  | ||||
| CLEAN_TARGETS += misc/configure.h misc/lvm-version.h | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| /* | ||||
|  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. | ||||
|  * Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -150,15 +150,15 @@ static int _lv_passes_volumes_filter(struct cmd_context *cmd, const struct logic | ||||
| 				    || str_list_match_list(&cmd->tags, | ||||
| 							   &lv->vg->tags, NULL)) | ||||
| 					    return 1; | ||||
|  | ||||
| 				continue; | ||||
| 				else | ||||
| 					continue; | ||||
| 			} | ||||
| 			/* If supplied tag matches LV or VG tag, activate */ | ||||
| 			if (str_list_match_item(&lv->tags, str) || | ||||
| 			    str_list_match_item(&lv->vg->tags, str)) | ||||
| 				return 1; | ||||
|  | ||||
| 			continue; | ||||
| 			else | ||||
| 				continue; | ||||
| 		} | ||||
|  | ||||
| 		/* If supplied name is vgname[/lvname] */ | ||||
| @@ -272,18 +272,10 @@ int lv_raid_percent(const struct logical_volume *lv, dm_percent_t *percent) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| int lv_raid_data_offset(const struct logical_volume *lv, uint64_t *data_offset) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| int lv_raid_dev_health(const struct logical_volume *lv, char **dev_health) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| int lv_raid_dev_count(const struct logical_volume *lv, uint32_t *dev_cnt) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| int lv_raid_mismatch_count(const struct logical_volume *lv, uint64_t *cnt) | ||||
| { | ||||
| 	return 0; | ||||
| @@ -323,6 +315,12 @@ int lvs_in_vg_opened(const struct volume_group *vg) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| /****** | ||||
| int lv_suspend(struct cmd_context *cmd, const char *lvid_s) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
| *******/ | ||||
| int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only, unsigned exclusive, | ||||
| 			 const struct logical_volume *lv, const struct logical_volume *lv_pre) | ||||
| { | ||||
| @@ -360,10 +358,6 @@ int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
| int lv_deactivate_any_missing_subdevs(const struct logical_volume *lv) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
| int pv_uses_vg(struct physical_volume *pv, | ||||
| 	       struct volume_group *vg) | ||||
| { | ||||
| @@ -775,8 +769,7 @@ int lv_info_with_seg_status(struct cmd_context *cmd, | ||||
| 	if (lv_is_used_cache_pool(lv)) { | ||||
| 		/* INFO is not set as cache-pool cannot be active. | ||||
| 		 * STATUS is collected from cache LV */ | ||||
| 		if (!(lv_seg = get_only_segment_using_this_lv(lv))) | ||||
| 			return_0; | ||||
| 		lv_seg = get_only_segment_using_this_lv(lv); | ||||
| 		(void) _lv_info(cmd, lv_seg->lv, 1, NULL, lv_seg, &status->seg_status, 0, 0); | ||||
| 		return 1; | ||||
| 	} | ||||
| @@ -791,18 +784,14 @@ int lv_info_with_seg_status(struct cmd_context *cmd, | ||||
| 				status->info.exists = 0; /* So pool LV is not active */ | ||||
| 		} | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	if (lv_is_external_origin(lv)) { | ||||
| 	} else if (lv_is_external_origin(lv)) { | ||||
| 		if (!_lv_info(cmd, lv, 0, &status->info, NULL, NULL, | ||||
| 			      with_open_count, with_read_ahead)) | ||||
| 			return_0; | ||||
|  | ||||
| 		(void) _lv_info(cmd, lv, 1, NULL, lv_seg, &status->seg_status, 0, 0); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	if (lv_is_origin(lv)) { | ||||
| 	} else if (lv_is_origin(lv)) { | ||||
| 		/* Query segment status for 'layered' (-real) device most of the time, | ||||
| 		 * only for merging snapshot, query its progress. | ||||
| 		 * TODO: single LV may need couple status to be exposed at once.... | ||||
| @@ -819,9 +808,7 @@ int lv_info_with_seg_status(struct cmd_context *cmd, | ||||
| 			/* Grab STATUS from layered -real */ | ||||
| 			(void) _lv_info(cmd, lv, 1, NULL, lv_seg, &status->seg_status, 0, 0); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	if (lv_is_cow(lv)) { | ||||
| 	} else if (lv_is_cow(lv)) { | ||||
| 		if (lv_is_merging_cow(lv)) { | ||||
| 			olv = origin_from_cow(lv); | ||||
|  | ||||
| @@ -836,6 +823,7 @@ int lv_info_with_seg_status(struct cmd_context *cmd, | ||||
| 				 * When merge is in progress, query merging origin LV instead. | ||||
| 				 * COW volume is already mapped as error target in this case. | ||||
| 				 */ | ||||
| 				status->lv = olv; | ||||
| 				return 1; | ||||
| 			} | ||||
|  | ||||
| @@ -992,30 +980,6 @@ int lv_raid_percent(const struct logical_volume *lv, dm_percent_t *percent) | ||||
| 	return lv_mirror_percent(lv->vg->cmd, lv, 0, percent, NULL); | ||||
| } | ||||
|  | ||||
| int lv_raid_data_offset(const struct logical_volume *lv, uint64_t *data_offset) | ||||
| { | ||||
| 	int r; | ||||
| 	struct dev_manager *dm; | ||||
| 	struct dm_status_raid *status; | ||||
|  | ||||
| 	if (!lv_info(lv->vg->cmd, lv, 0, NULL, 0, 0)) | ||||
| 		return 0; | ||||
|  | ||||
| 	log_debug_activation("Checking raid data offset and dev sectors for LV %s/%s", | ||||
| 			     lv->vg->name, lv->name); | ||||
| 	if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name, 1))) | ||||
| 		return_0; | ||||
|  | ||||
| 	if (!(r = dev_manager_raid_status(dm, lv, &status))) | ||||
| 		stack; | ||||
|  | ||||
| 	*data_offset = status->data_offset; | ||||
|  | ||||
| 	dev_manager_destroy(dm); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| int lv_raid_dev_health(const struct logical_volume *lv, char **dev_health) | ||||
| { | ||||
| 	int r; | ||||
| @@ -1045,32 +1009,6 @@ int lv_raid_dev_health(const struct logical_volume *lv, char **dev_health) | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| int lv_raid_dev_count(const struct logical_volume *lv, uint32_t *dev_cnt) | ||||
| { | ||||
| 	struct dev_manager *dm; | ||||
| 	struct dm_status_raid *status; | ||||
|  | ||||
| 	*dev_cnt = 0; | ||||
|  | ||||
| 	if (!lv_info(lv->vg->cmd, lv, 0, NULL, 0, 0)) | ||||
| 		return 0; | ||||
|  | ||||
| 	log_debug_activation("Checking raid device count for LV %s/%s", | ||||
| 			     lv->vg->name, lv->name); | ||||
| 	if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name, 1))) | ||||
| 		return_0; | ||||
|  | ||||
| 	if (!dev_manager_raid_status(dm, lv, &status)) { | ||||
| 		dev_manager_destroy(dm); | ||||
| 		return_0; | ||||
| 	} | ||||
| 	*dev_cnt = status->dev_count; | ||||
|  | ||||
| 	dev_manager_destroy(dm); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int lv_raid_mismatch_count(const struct logical_volume *lv, uint64_t *cnt) | ||||
| { | ||||
| 	struct dev_manager *dm; | ||||
| @@ -1701,7 +1639,7 @@ static char *_build_target_uuid(struct cmd_context *cmd, const struct logical_vo | ||||
|  | ||||
| 	if (lv_is_thin_pool(lv)) | ||||
| 		layer = "tpool"; /* Monitor "tpool" for the "thin pool". */ | ||||
| 	else if (lv_is_origin(lv) || lv_is_external_origin(lv)) | ||||
| 	else if (lv_is_origin(lv)) | ||||
| 		layer = "real"; /* Monitor "real" for "snapshot-origin". */ | ||||
| 	else | ||||
| 		layer = NULL; | ||||
| @@ -1849,15 +1787,12 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume | ||||
| 	 *  However in case command would have crashed, such LV is | ||||
| 	 *  left unmonitored and may potentially require dmeventd. | ||||
| 	 */ | ||||
| 	if (lv_is_cache_pool_data(lv) || lv_is_cache_pool_metadata(lv)) { | ||||
| 		if (!(seg = find_pool_seg(first_seg(lv)))) | ||||
| 			return_0; | ||||
| 		if (!lv_is_used_cache_pool(seg->lv)) { | ||||
| 			log_debug_activation("Skipping %smonitor of %s.%s", | ||||
| 					     (monitor) ? "" : "un", display_lvname(lv), | ||||
| 					     (monitor) ? " Cache pool activation for clearing only." : ""); | ||||
| 			return 1; | ||||
| 		} | ||||
| 	if ((lv_is_cache_pool_data(lv) || lv_is_cache_pool_metadata(lv)) && | ||||
| 	    !lv_is_used_cache_pool((find_pool_seg(first_seg(lv))->lv))) { | ||||
| 		log_debug_activation("Skipping %smonitor of %s.%s", | ||||
| 				     (monitor) ? "" : "un", display_lvname(lv), | ||||
| 				     (monitor) ? " Cache pool activation for clearing only." : ""); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| @@ -1943,13 +1878,6 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume | ||||
| 			r = 0; | ||||
| 		} | ||||
|  | ||||
| 		if (seg->external_lv && | ||||
| 		    !monitor_dev_for_events(cmd, seg->external_lv, | ||||
| 					    (!monitor) ? laopts : NULL, monitor)) { | ||||
| 			stack; | ||||
| 			r = 0; | ||||
| 		} | ||||
|  | ||||
| 		if (seg->metadata_lv && | ||||
| 		    !monitor_dev_for_events(cmd, seg->metadata_lv, NULL, monitor)) { | ||||
| 			stack; | ||||
| @@ -2008,7 +1936,7 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume | ||||
| 			/* FIXME specify events */ | ||||
| 			if (!monitor_fn(seg, 0)) { | ||||
| 				log_error("%s: %s segment monitoring function failed.", | ||||
| 					  display_lvname(lv), lvseg_name(seg)); | ||||
| 					  display_lvname(lv), seg->segtype->name); | ||||
| 				return 0; | ||||
| 			} | ||||
| 		} else | ||||
| @@ -2016,13 +1944,16 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume | ||||
|  | ||||
| 		/* Check [un]monitor results */ | ||||
| 		/* Try a couple times if pending, but not forever... */ | ||||
| 		for (i = 0;; i++) { | ||||
| 		for (i = 0; i < 40; i++) { | ||||
| 			pending = 0; | ||||
| 			monitored = seg->segtype->ops->target_monitored(seg, &pending); | ||||
| 			if (!pending || i >= 40) | ||||
| 			if (pending || | ||||
| 			    (!monitored && monitor) || | ||||
| 			    (monitored && !monitor)) | ||||
| 				log_very_verbose("%s %smonitoring still pending: waiting...", | ||||
| 						 display_lvname(lv), monitor ? "" : "un"); | ||||
| 			else | ||||
| 				break; | ||||
| 			log_very_verbose("%s %smonitoring still pending: waiting...", | ||||
| 					 display_lvname(lv), monitor ? "" : "un"); | ||||
| 			usleep(10000 * i); | ||||
| 		} | ||||
|  | ||||
| @@ -2083,16 +2014,12 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s, | ||||
| 	const struct logical_volume *pvmove_lv = NULL; | ||||
| 	const struct logical_volume *lv_to_free = NULL; | ||||
| 	const struct logical_volume *lv_pre_to_free = NULL; | ||||
| 	struct logical_volume *lv_pre_tmp, *lv_tmp; | ||||
| 	struct logical_volume *lv_pre_tmp; | ||||
| 	struct seg_list *sl; | ||||
| 	struct lv_segment *snap_seg; | ||||
| 	struct lvinfo info; | ||||
| 	int r = 0, lockfs = 0, flush_required = 0; | ||||
| 	struct detached_lv_data detached; | ||||
| 	struct dm_pool *mem = NULL; | ||||
| 	struct dm_list suspend_lvs; | ||||
| 	struct lv_list *lvl; | ||||
| 	int found; | ||||
|  | ||||
| 	if (!activation()) | ||||
| 		return 1; | ||||
| @@ -2130,6 +2057,9 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s, | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!lv_read_replicator_vgs(lv)) | ||||
| 		goto_out; | ||||
|  | ||||
| 	lv_calculate_readahead(lv, NULL); | ||||
|  | ||||
| 	/* | ||||
| @@ -2159,12 +2089,6 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s, | ||||
| 		} | ||||
| 		if (!_lv_preload(lv_pre_tmp, laopts, &flush_required)) | ||||
| 			goto_out; | ||||
|  | ||||
| 		/* Suspending 1st. LV above PVMOVE suspends whole tree */ | ||||
| 		dm_list_iterate_items(sl, &pvmove_lv->segs_using_this_lv) { | ||||
| 			lv = sl->seg->lv; | ||||
| 			break; | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (!_lv_preload(lv_pre, laopts, &flush_required)) | ||||
| 			/* FIXME Revert preloading */ | ||||
| @@ -2202,7 +2126,7 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s, | ||||
| 	 * NOTE: Mirror repair requires noflush for proper repair! | ||||
| 	 * TODO: Relax this limiting condition further */ | ||||
| 	if (!flush_required && | ||||
| 	    (lv_is_pvmove(lv) || pvmove_lv || | ||||
| 	    (lv_is_pvmove(lv) || | ||||
| 	     (!lv_is_mirror(lv) && !lv_is_thin_pool(lv) && !lv_is_thin_volume(lv)))) { | ||||
| 		log_debug("Requiring flush for LV %s.", display_lvname(lv)); | ||||
| 		flush_required = 1; | ||||
| @@ -2212,6 +2136,10 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s, | ||||
| 		/* FIXME Consider aborting here */ | ||||
| 		stack; | ||||
|  | ||||
| 	critical_section_inc(cmd, "suspending"); | ||||
| 	if (pvmove_lv) | ||||
| 		critical_section_inc(cmd, "suspending pvmove LV"); | ||||
|  | ||||
| 	if (!laopts->origin_only && | ||||
| 	    (lv_is_origin(lv_pre) || lv_is_cow(lv_pre))) | ||||
| 		lockfs = 1; | ||||
| @@ -2223,68 +2151,40 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s, | ||||
| 	if (laopts->origin_only && lv_is_thin_volume(lv) && lv_is_thin_volume(lv_pre)) | ||||
| 		lockfs = 1; | ||||
|  | ||||
| 	critical_section_inc(cmd, "suspending"); | ||||
|  | ||||
| 	if (!lv_is_locked(lv) && lv_is_locked(lv_pre) && | ||||
| 	    (pvmove_lv = find_pvmove_lv_in_lv(lv_pre))) { | ||||
| 		/* | ||||
| 		 * When starting PVMOVE, suspend participating LVs first | ||||
| 		 * with committed metadata by looking at precommited pvmove list. | ||||
| 		 * In committed metadata these LVs are not connected in any way. | ||||
| 		 * | ||||
| 		 * TODO: prepare list of LVs needed to be suspended and pass them | ||||
| 		 *       via 'struct laopts' directly to _lv_suspend_lv() and handle this | ||||
| 		 *       with a single 'dmtree' call. | ||||
| 		 */ | ||||
| 		if (!(mem = dm_pool_create("suspend_lvs", 128))) | ||||
| 	/* | ||||
| 	 * Suspending an LV directly above a PVMOVE LV also | ||||
|  	 * suspends other LVs using that same PVMOVE LV. | ||||
| 	 * FIXME Remove this and delay the 'clear node' until | ||||
|  	 * after the code knows whether there's a different | ||||
|  	 * inactive table to load or not instead so lv_suspend | ||||
|  	 * can be called separately for each LV safely. | ||||
|  	 */ | ||||
| 	if ((lv_pre->vg->status & PRECOMMITTED) && | ||||
| 	    lv_is_locked(lv_pre) && find_pvmove_lv_in_lv(lv_pre)) { | ||||
| 		if (!_lv_suspend_lv(lv_pre, laopts, lockfs, flush_required)) { | ||||
| 			critical_section_dec(cmd, "failed precommitted suspend"); | ||||
| 			if (pvmove_lv) | ||||
| 				critical_section_dec(cmd, "failed precommitted suspend (pvmove)"); | ||||
| 			goto_out; | ||||
|  | ||||
| 		/* Prepare list of all LVs for suspend ahead */ | ||||
| 		dm_list_init(&suspend_lvs); | ||||
| 		dm_list_iterate_items(sl, &pvmove_lv->segs_using_this_lv) { | ||||
| 			lv_tmp = sl->seg->lv; | ||||
| 			if (lv_is_cow(lv_tmp)) | ||||
| 				/* Never suspend COW, always has to be origin */ | ||||
| 				lv_tmp = origin_from_cow(lv_tmp); | ||||
| 			found = 0; | ||||
| 			dm_list_iterate_items(lvl, &suspend_lvs) | ||||
| 				if (strcmp(lvl->lv->name, lv_tmp->name) == 0) { | ||||
| 					found = 1; | ||||
| 					break; | ||||
| 				} | ||||
| 			if (found) | ||||
| 				continue; /* LV is already in the list */ | ||||
| 			if (!(lvl = dm_pool_alloc(mem, sizeof(*lvl)))) { | ||||
| 				log_error("lv_list alloc failed."); | ||||
| 				goto out; | ||||
| 			} | ||||
| 			/* Look for precommitted LV name in commmitted VG */ | ||||
| 			if (!(lvl->lv = find_lv(lv->vg, lv_tmp->name))) { | ||||
| 				log_error(INTERNAL_ERROR "LV %s missing from preload metadata.", | ||||
| 					  display_lvname(lv_tmp)); | ||||
| 				goto out; | ||||
| 			} | ||||
| 			dm_list_add(&suspend_lvs, &lvl->list); | ||||
| 		} | ||||
| 		dm_list_iterate_items(lvl, &suspend_lvs) | ||||
| 			if (!_lv_suspend_lv(lvl->lv, laopts, lockfs, 1)) { | ||||
| 				critical_section_dec(cmd, "failed suspend"); | ||||
| 				goto_out; /* FIXME: resume on recovery path? */ | ||||
| 			} | ||||
| 	} else  /* Standard suspend */ | ||||
| 	} else { | ||||
| 		/* Normal suspend */ | ||||
| 		if (!_lv_suspend_lv(lv, laopts, lockfs, flush_required)) { | ||||
| 			critical_section_dec(cmd, "failed suspend"); | ||||
| 			if (pvmove_lv) | ||||
| 				critical_section_dec(cmd, "failed suspend (pvmove)"); | ||||
| 			goto_out; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	r = 1; | ||||
| out: | ||||
| 	if (mem) | ||||
| 		dm_pool_destroy(mem); | ||||
| 	if (lv_pre_to_free) | ||||
| 		release_vg(lv_pre_to_free->vg); | ||||
| 	if (lv_to_free) | ||||
| 	if (lv_to_free) { | ||||
| 		lv_release_replicator_vgs(lv_to_free); | ||||
| 		release_vg(lv_to_free->vg); | ||||
| 	} | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
| @@ -2306,29 +2206,12 @@ int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned o | ||||
| 	return _lv_suspend(cmd, lvid_s, &laopts, 0, lv, lv_pre); | ||||
| } | ||||
|  | ||||
| static int _check_suspended_lv(struct logical_volume *lv, void *data) | ||||
| { | ||||
| 	struct lvinfo info; | ||||
|  | ||||
| 	if (lv_info(lv->vg->cmd, lv, 0, &info, 0, 0) && info.exists && info.suspended) { | ||||
| 		log_debug("Found suspended LV %s in critical section().", display_lvname(lv)); | ||||
| 		return 0; /* There is suspended subLV in the tree */ | ||||
| 	} | ||||
|  | ||||
| 	if (lv_layer(lv) && lv_info(lv->vg->cmd, lv, 1, &info, 0, 0) && info.exists && info.suspended) { | ||||
| 		log_debug("Found suspended layered LV %s in critical section().", display_lvname(lv)); | ||||
| 		return 0; /* There is suspended subLV in the tree */ | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _lv_resume(struct cmd_context *cmd, const char *lvid_s, | ||||
| 		      struct lv_activate_opts *laopts, int error_if_not_active, | ||||
| 	              const struct logical_volume *lv) | ||||
| { | ||||
| 	const struct logical_volume *lv_to_free = NULL; | ||||
| 	struct dm_list *snh; | ||||
| 	struct lvinfo info; | ||||
| 	int r = 0; | ||||
|  | ||||
| @@ -2362,28 +2245,12 @@ static int _lv_resume(struct cmd_context *cmd, const char *lvid_s, | ||||
| 	if (!info.exists || !info.suspended) { | ||||
| 		if (error_if_not_active) | ||||
| 			goto_out; | ||||
|  | ||||
| 		/* ATM only thin-pool with origin-only suspend does not really suspend anything | ||||
| 		 * it's used only for message passing to thin-pool */ | ||||
| 		if (laopts->origin_only && lv_is_thin_pool(lv)) | ||||
| 			critical_section_dec(cmd, "resumed"); | ||||
|  | ||||
| 		if (!info.suspended && critical_section()) { | ||||
| 			/* Validation check if any subLV is suspended */ | ||||
| 			if (!laopts->origin_only && lv_is_origin(lv)) { | ||||
| 				/* Check all snapshots for this origin LV */ | ||||
| 				dm_list_iterate(snh, &lv->snapshot_segs) | ||||
| 					if (!_check_suspended_lv(dm_list_struct_base(snh, struct lv_segment, origin_list)->cow, NULL)) | ||||
| 						goto needs_resume; /* Found suspended snapshot */ | ||||
| 			} | ||||
| 			if ((r = for_each_sub_lv((struct logical_volume *)lv, &_check_suspended_lv, NULL))) | ||||
| 				goto out; /* Nothing was found suspended */ | ||||
| 		} else { | ||||
| 			r = 1; | ||||
| 			goto out; | ||||
| 		} | ||||
| 		r = 1; | ||||
| 		if (!info.suspended) | ||||
| 			critical_section_dec(cmd, "already resumed"); | ||||
| 		goto out; | ||||
| 	} | ||||
| needs_resume: | ||||
|  | ||||
| 	laopts->read_only = _passes_readonly_filter(cmd, lv); | ||||
| 	laopts->resuming = 1; | ||||
|  | ||||
| @@ -2501,21 +2368,14 @@ int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logi | ||||
| 			goto_out; | ||||
| 	} | ||||
|  | ||||
| 	if (!lv_read_replicator_vgs(lv)) | ||||
| 		goto_out; | ||||
|  | ||||
| 	if (!monitor_dev_for_events(cmd, lv, &laopts, 0)) | ||||
| 		stack; | ||||
|  | ||||
| 	critical_section_inc(cmd, "deactivating"); | ||||
| 	r = _lv_deactivate(lv); | ||||
|  | ||||
| 	/* | ||||
| 	 * Remove any transiently activated error | ||||
| 	 * devices which arean't used any more. | ||||
| 	 */ | ||||
| 	if (r && lv_is_raid(lv) && !lv_deactivate_any_missing_subdevs(lv)) { | ||||
| 		log_error("Failed to remove temporary SubLVs from %s", | ||||
| 			  display_lvname(lv)); | ||||
| 		r = 0; | ||||
| 	} | ||||
| 	critical_section_dec(cmd, "deactivated"); | ||||
|  | ||||
| 	if (!lv_info(cmd, lv, 0, &info, 0, 0) || info.exists) { | ||||
| @@ -2525,8 +2385,10 @@ int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logi | ||||
| 		r = 0; | ||||
| 	} | ||||
| out: | ||||
| 	if (lv_to_free) | ||||
| 	if (lv_to_free) { | ||||
| 		lv_release_replicator_vgs(lv_to_free); | ||||
| 		release_vg(lv_to_free->vg); | ||||
| 	} | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
| @@ -2641,6 +2503,9 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s, | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (!lv_read_replicator_vgs(lv)) | ||||
| 		goto_out; | ||||
|  | ||||
| 	lv_calculate_readahead(lv, NULL); | ||||
|  | ||||
| 	critical_section_inc(cmd, "activating"); | ||||
| @@ -2652,8 +2517,10 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s, | ||||
| 		stack; | ||||
|  | ||||
| out: | ||||
| 	if (lv_to_free) | ||||
| 	if (lv_to_free) { | ||||
| 		lv_release_replicator_vgs(lv_to_free); | ||||
| 		release_vg(lv_to_free->vg); | ||||
| 	} | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
| @@ -2706,75 +2573,6 @@ int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv) | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* Remove any existing, closed mapped device by @name */ | ||||
| static int _remove_dm_dev_by_name(const char *name) | ||||
| { | ||||
| 	int r = 0; | ||||
| 	struct dm_task *dmt; | ||||
| 	struct dm_info info; | ||||
|  | ||||
| 	if (!(dmt = dm_task_create(DM_DEVICE_INFO))) | ||||
| 		return_0; | ||||
|  | ||||
| 	/* Check, if the device exists. */ | ||||
| 	if (dm_task_set_name(dmt, name) && dm_task_run(dmt) && dm_task_get_info(dmt, &info)) { | ||||
| 		dm_task_destroy(dmt); | ||||
|  | ||||
| 		/* Ignore non-existing or open dm devices */ | ||||
| 		if (!info.exists || info.open_count) | ||||
| 			return 1; | ||||
|  | ||||
| 		if (!(dmt = dm_task_create(DM_DEVICE_REMOVE))) | ||||
| 			return_0; | ||||
|  | ||||
| 		if (dm_task_set_name(dmt, name)) | ||||
| 			r = dm_task_run(dmt); | ||||
| 	} | ||||
|  | ||||
| 	dm_task_destroy(dmt); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* Work all segments of @lv removing any existing, closed "*-missing_N_0" sub devices. */ | ||||
| static int _lv_remove_any_missing_subdevs(struct logical_volume *lv) | ||||
| { | ||||
| 	if (lv) { | ||||
| 		uint32_t seg_no = 0; | ||||
| 		char name[257]; | ||||
| 		struct lv_segment *seg; | ||||
|  | ||||
| 		dm_list_iterate_items(seg, &lv->segments) { | ||||
| 			if (dm_snprintf(name, sizeof(name), "%s-%s-missing_%u_0", seg->lv->vg->name, seg->lv->name, seg_no) < 0) | ||||
| 				return_0; | ||||
| 			if (!_remove_dm_dev_by_name(name)) | ||||
| 				return 0; | ||||
|  | ||||
| 			seg_no++; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* Remove any "*-missing_*" sub devices added by the activation layer for an rmate/rimage missing PV mapping */ | ||||
| int lv_deactivate_any_missing_subdevs(const struct logical_volume *lv) | ||||
| { | ||||
| 	uint32_t s; | ||||
| 	struct lv_segment *seg = first_seg(lv); | ||||
|  | ||||
| 	for (s = 0; s < seg->area_count; s++) { | ||||
| 		if (seg_type(seg, s) == AREA_LV && | ||||
| 		    !_lv_remove_any_missing_subdevs(seg_lv(seg, s))) | ||||
| 			return 0; | ||||
| 		if (seg->meta_areas && seg_metatype(seg, s) == AREA_LV && | ||||
| 		    !_lv_remove_any_missing_subdevs(seg_metalv(seg, s))) | ||||
| 			return 0; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Does PV use VG somewhere in its construction? | ||||
|  * Returns 1 on failure. | ||||
|   | ||||
| @@ -124,8 +124,6 @@ int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logi | ||||
|  | ||||
| int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv); | ||||
|  | ||||
| int lv_deactivate_any_missing_subdevs(const struct logical_volume *lv); | ||||
|  | ||||
| /* | ||||
|  * Returns 1 if info structure has been populated, else 0 on failure. | ||||
|  * When lvinfo* is NULL, it returns 1 if the device is locally active, 0 otherwise. | ||||
| @@ -168,13 +166,11 @@ int lv_snapshot_percent(const struct logical_volume *lv, dm_percent_t *percent); | ||||
| int lv_mirror_percent(struct cmd_context *cmd, const struct logical_volume *lv, | ||||
| 		      int wait, dm_percent_t *percent, uint32_t *event_nr); | ||||
| int lv_raid_percent(const struct logical_volume *lv, dm_percent_t *percent); | ||||
| int lv_raid_dev_count(const struct logical_volume *lv, uint32_t *dev_cnt); | ||||
| int lv_raid_data_offset(const struct logical_volume *lv, uint64_t *data_offset); | ||||
| int lv_raid_dev_health(const struct logical_volume *lv, char **dev_health); | ||||
| int lv_raid_mismatch_count(const struct logical_volume *lv, uint64_t *cnt); | ||||
| int lv_raid_sync_action(const struct logical_volume *lv, char **sync_action); | ||||
| int lv_raid_message(const struct logical_volume *lv, const char *msg); | ||||
| int lv_cache_status(const struct logical_volume *cache_lv, | ||||
| int lv_cache_status(const struct logical_volume *lv, | ||||
| 		    struct lv_status_cache **status); | ||||
| int lv_thin_pool_percent(const struct logical_volume *lv, int metadata, | ||||
| 			 dm_percent_t *percent); | ||||
| @@ -202,12 +198,12 @@ int lv_has_target_type(struct dm_pool *mem, const struct logical_volume *lv, | ||||
| 		       const char *layer, const char *target_type); | ||||
|  | ||||
| int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume *lv, | ||||
| 			   const struct lv_activate_opts *laopts, int monitor); | ||||
| 			   const struct lv_activate_opts *laopts, int do_reg); | ||||
|  | ||||
| #ifdef DMEVENTD | ||||
| #  include "libdevmapper-event.h" | ||||
| char *get_monitor_dso_path(struct cmd_context *cmd, const char *libpath); | ||||
| int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso, | ||||
| int target_registered_with_dmeventd(struct cmd_context *cmd, const char *libpath, | ||||
| 				    const struct logical_volume *lv, int *pending); | ||||
| int target_register_events(struct cmd_context *cmd, const char *dso, const struct logical_volume *lv, | ||||
| 			    int evmask __attribute__((unused)), int set, int timeout); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| /* | ||||
|  * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved. | ||||
|  * Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved. | ||||
|  * Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved. | ||||
|  * | ||||
|  * This file is part of LVM2. | ||||
|  * | ||||
| @@ -61,7 +61,7 @@ struct dev_manager { | ||||
| 	int flush_required; | ||||
| 	int activation;                 /* building activation tree */ | ||||
| 	int suspend;			/* building suspend tree */ | ||||
| 	unsigned track_external_lv_deps; | ||||
| 	int skip_external_lv; | ||||
| 	struct dm_list pending_delete;	/* str_list of dlid(s) with pending delete */ | ||||
| 	unsigned track_pending_delete; | ||||
| 	unsigned track_pvmove_deps; | ||||
| @@ -214,14 +214,6 @@ typedef enum { | ||||
| 	STATUS, /* DM_DEVICE_STATUS ioctl */ | ||||
| } info_type_t; | ||||
|  | ||||
| /* Return length of segment depending on type and reshape_len */ | ||||
| static uint32_t _seg_len(const struct lv_segment *seg) | ||||
| { | ||||
| 	uint32_t reshape_len = seg_is_raid(seg) ? ((seg->area_count - seg->segtype->parity_devs) * seg->reshape_len) : 0; | ||||
|  | ||||
| 	return seg->len - reshape_len; | ||||
| } | ||||
|  | ||||
| static int _info_run(const char *dlid, struct dm_info *dminfo, | ||||
| 		     uint32_t *read_ahead, | ||||
| 		     struct lv_seg_status *seg_status, | ||||
| @@ -258,12 +250,7 @@ static int _info_run(const char *dlid, struct dm_info *dminfo, | ||||
| 	if (seg_status && dminfo->exists) { | ||||
| 		start = length = seg_status->seg->lv->vg->extent_size; | ||||
| 		start *= seg_status->seg->le; | ||||
| 		length *= _seg_len(seg_status->seg); | ||||
|  | ||||
| 		/* Uses max DM_THIN_MAX_METADATA_SIZE sectors for metadata device */ | ||||
| 		if (lv_is_thin_pool_metadata(seg_status->seg->lv) && | ||||
| 		    (length > DM_THIN_MAX_METADATA_SIZE)) | ||||
| 			length = DM_THIN_MAX_METADATA_SIZE; | ||||
| 		length *= seg_status->seg->len; | ||||
|  | ||||
| 		do { | ||||
| 			target = dm_get_next_target(dmt, target, &target_start, | ||||
| @@ -275,8 +262,7 @@ static int _info_run(const char *dlid, struct dm_info *dminfo, | ||||
| 			target_params = NULL; /* Marking this target_params unusable */ | ||||
| 		} while (target); | ||||
|  | ||||
| 		if (!target_name || | ||||
| 		    !_get_segment_status_from_target_params(target_name, target_params, seg_status)) | ||||
| 		if (!_get_segment_status_from_target_params(target_name, target_params, seg_status)) | ||||
| 			stack; | ||||
| 	} | ||||
|  | ||||
| @@ -1038,8 +1024,7 @@ static int _percent_run(struct dev_manager *dm, const char *name, | ||||
| 			goto_out; | ||||
| 	} | ||||
|  | ||||
| 	log_debug_activation("LV percent: %s", | ||||
| 			     display_percent(dm->cmd, *overall_percent)); | ||||
| 	log_debug_activation("LV percent: %.2f", dm_percent_to_float(*overall_percent)); | ||||
| 	r = 1; | ||||
|  | ||||
|       out: | ||||
| @@ -1056,11 +1041,10 @@ static int _percent(struct dev_manager *dm, const char *name, const char *dlid, | ||||
| 		if (_percent_run(dm, NULL, dlid, target_type, wait, lv, percent, | ||||
| 				 event_nr, fail_if_percent_unsupported)) | ||||
| 			return 1; | ||||
|  | ||||
| 		if (_original_uuid_format_check_required(dm->cmd) && | ||||
| 		    _percent_run(dm, NULL, dlid + sizeof(UUID_PREFIX) - 1, | ||||
| 				 target_type, wait, lv, percent, | ||||
| 				 event_nr, fail_if_percent_unsupported)) | ||||
| 		else if (_original_uuid_format_check_required(dm->cmd) && | ||||
| 			 _percent_run(dm, NULL, dlid + sizeof(UUID_PREFIX) - 1, | ||||
| 				      target_type, wait, lv, percent, | ||||
| 				      event_nr, fail_if_percent_unsupported)) | ||||
| 			return 1; | ||||
| 	} | ||||
|  | ||||
| @@ -1324,13 +1308,14 @@ int dev_manager_raid_message(struct dev_manager *dm, | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* These are the supported RAID messages for dm-raid v1.9.0 */ | ||||
| 	/* These are the supported RAID messages for dm-raid v1.5.0 */ | ||||
| 	if (strcmp(msg, "idle") && | ||||
| 	    strcmp(msg, "frozen") && | ||||
| 	    strcmp(msg, "resync") && | ||||
| 	    strcmp(msg, "recover") && | ||||
| 	    strcmp(msg, "check") && | ||||
| 	    strcmp(msg, "repair")) { | ||||
| 	    strcmp(msg, "repair") && | ||||
| 	    strcmp(msg, "reshape")) { | ||||
| 		log_error(INTERNAL_ERROR "Unknown RAID message: %s.", msg); | ||||
| 		return 0; | ||||
| 	} | ||||
| @@ -1717,114 +1702,6 @@ static uint16_t _get_udev_flags(struct dev_manager *dm, const struct logical_vol | ||||
| 	return udev_flags; | ||||
| } | ||||
|  | ||||
| static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, | ||||
| 			    const struct logical_volume *lv, int origin_only); | ||||
|  | ||||
| static int _check_holder(struct dev_manager *dm, struct dm_tree *dtree, | ||||
| 			 const struct logical_volume *lv, uint32_t major, | ||||
| 			 const char *d_name) | ||||
| { | ||||
| 	const char *default_uuid_prefix = dm_uuid_prefix(); | ||||
| 	const size_t default_uuid_prefix_len = strlen(default_uuid_prefix); | ||||
| 	const char *name; | ||||
| 	const char *uuid; | ||||
| 	struct dm_info info; | ||||
| 	struct dm_task *dmt; | ||||
| 	struct logical_volume *lv_det; | ||||
| 	union lvid id; | ||||
| 	int dev, r = 0; | ||||
|  | ||||
| 	errno = 0; | ||||
| 	dev = strtoll(d_name + 3, NULL, 10); | ||||
| 	if (errno) { | ||||
| 		log_error("Failed to parse dm device minor number from %s.", d_name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(dmt = _setup_task_run(DM_DEVICE_INFO, &info, NULL, NULL, NULL, | ||||
| 				    major, dev, 0, 0, 0))) | ||||
| 		return_0; | ||||
|  | ||||
| 	if (info.exists) { | ||||
| 		uuid = dm_task_get_uuid(dmt); | ||||
| 		name = dm_task_get_name(dmt); | ||||
|  | ||||
| 		log_debug_activation("Checking holder of %s  %s (" FMTu32 ":" FMTu32 ") %s.", | ||||
| 				     display_lvname(lv), uuid, info.major, info.minor, | ||||
| 				     name); | ||||
|  | ||||
| 		/* Skip common uuid prefix */ | ||||
| 		if (!strncmp(default_uuid_prefix, uuid, default_uuid_prefix_len)) | ||||
| 			uuid += default_uuid_prefix_len; | ||||
|  | ||||
| 		if (!strncmp(uuid, (char*)&lv->vg->id, sizeof(lv->vg->id)) && | ||||
| 		    !dm_tree_find_node_by_uuid(dtree, uuid)) { | ||||
| 			dm_strncpy((char*)&id, uuid, 2 * sizeof(struct id) + 1); | ||||
|  | ||||
| 			/* If UUID is not yet in dtree, look for matching LV */ | ||||
| 			if (!(lv_det = find_lv_in_vg_by_lvid(lv->vg, &id))) { | ||||
| 				log_error("Cannot find holder with device name %s in VG %s.", | ||||
| 					  name, lv->vg->name); | ||||
| 				goto out; | ||||
| 			} | ||||
|  | ||||
| 			if (lv_is_cow(lv_det)) | ||||
| 				lv_det = origin_from_cow(lv_det); | ||||
| 			log_debug_activation("Found holder %s of %s.", | ||||
| 					     display_lvname(lv_det), | ||||
| 					     display_lvname(lv)); | ||||
| 			if (!_add_lv_to_dtree(dm, dtree, lv_det, 0)) | ||||
| 				goto_out; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|         r = 1; | ||||
| out: | ||||
| 	dm_task_destroy(dmt); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Add exiting devices which holds given LV device open. | ||||
|  * This is used in case when metadata already do not contain information | ||||
|  * i.e. PVMOVE is being finished and final table is going to be resumed. | ||||
|  */ | ||||
| static int _add_holders_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, | ||||
| 				 const struct logical_volume *lv, struct dm_info *info) | ||||
| { | ||||
| 	const char *sysfs_dir = dm_sysfs_dir(); | ||||
| 	char sysfs_path[PATH_MAX]; | ||||
| 	struct dirent *dirent; | ||||
| 	DIR *d; | ||||
| 	int r = 0; | ||||
|  | ||||
| 	/* Sysfs path of holders */ | ||||
| 	if (dm_snprintf(sysfs_path, sizeof(sysfs_path), "%sblock/dm-" FMTu32 | ||||
| 			"/holders", sysfs_dir, info->minor) < 0) { | ||||
| 		log_error("sysfs_path dm_snprintf failed."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(d = opendir(sysfs_path))) { | ||||
| 		log_sys_error("opendir", sysfs_path); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	while ((dirent = readdir(d))) | ||||
| 		/* Expects minor is added to 'dm-' prefix */ | ||||
| 		if (!strncmp(dirent->d_name, "dm-", 3) && | ||||
| 		    !_check_holder(dm, dtree, lv, info->major, dirent->d_name)) | ||||
| 			goto_out; | ||||
|  | ||||
| 	r = 1; | ||||
| out: | ||||
| 	if (closedir(d)) | ||||
| 		log_sys_debug("closedir", "holders"); | ||||
|  | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, | ||||
| 			     const struct logical_volume *lv, const char *layer) | ||||
| { | ||||
| @@ -1879,14 +1756,83 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, | ||||
| 			return_0; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Find holders of existing active LV where name starts with 'pvmove', | ||||
| 	 * but it's not anymore PVMOVE LV and also it's not PVMOVE _mimage | ||||
| 	 */ | ||||
| 	if (info.exists && !lv_is_pvmove(lv) && | ||||
| 	    !strchr(lv->name, '_') && !strncmp(lv->name, "pvmove", 6)) | ||||
| 		if (!_add_holders_to_dtree(dm, dtree, lv, &info)) | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Add replicator devices | ||||
|  * | ||||
|  * Using _add_dev_to_dtree() directly instead of _add_lv_to_dtree() | ||||
|  * to avoid extra checks with extensions. | ||||
|  */ | ||||
| static int _add_partial_replicator_to_dtree(struct dev_manager *dm, | ||||
| 					    struct dm_tree *dtree, | ||||
| 					    const struct logical_volume *lv) | ||||
| { | ||||
| 	struct logical_volume *rlv = first_seg(lv)->replicator; | ||||
| 	struct replicator_device *rdev; | ||||
| 	struct replicator_site *rsite; | ||||
| 	struct dm_tree_node *rep_node, *rdev_node; | ||||
| 	const char *uuid; | ||||
|  | ||||
| 	if (!lv_is_active_replicator_dev(lv)) { | ||||
| 		if (!_add_dev_to_dtree(dm, dtree, lv->rdevice->lv, | ||||
| 				      NULL)) | ||||
| 			return_0; | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	/* Add _rlog and replicator device */ | ||||
| 	if (!_add_dev_to_dtree(dm, dtree, first_seg(rlv)->rlog_lv, NULL)) | ||||
| 		return_0; | ||||
|  | ||||
| 	if (!_add_dev_to_dtree(dm, dtree, rlv, NULL)) | ||||
| 		return_0; | ||||
|  | ||||
| 	if (!(uuid = build_dm_uuid(dm->mem, rlv, NULL))) | ||||
| 		return_0; | ||||
|  | ||||
| 	rep_node = dm_tree_find_node_by_uuid(dtree, uuid); | ||||
|  | ||||
| 	/* Add all related devices for replicator */ | ||||
| 	dm_list_iterate_items(rsite, &rlv->rsites) | ||||
| 		dm_list_iterate_items(rdev, &rsite->rdevices) { | ||||
| 			if (rsite->state == REPLICATOR_STATE_ACTIVE) { | ||||
| 				/* Add _rimage LV */ | ||||
| 				if (!_add_dev_to_dtree(dm, dtree, rdev->lv, NULL)) | ||||
| 					return_0; | ||||
|  | ||||
| 				/* Add replicator-dev LV, except of the already added one */ | ||||
| 				if ((lv != rdev->replicator_dev->lv) && | ||||
| 				    !_add_dev_to_dtree(dm, dtree, | ||||
| 						       rdev->replicator_dev->lv, NULL)) | ||||
| 					return_0; | ||||
|  | ||||
| 				/* If replicator exists - try connect existing heads */ | ||||
| 				if (rep_node) { | ||||
| 					uuid = build_dm_uuid(dm->mem, | ||||
| 							     rdev->replicator_dev->lv, | ||||
| 							     NULL); | ||||
| 					if (!uuid) | ||||
| 						return_0; | ||||
|  | ||||
| 					rdev_node = dm_tree_find_node_by_uuid(dtree, uuid); | ||||
| 					if (rdev_node) | ||||
| 						dm_tree_node_set_presuspend_node(rdev_node, | ||||
| 										 rep_node); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if (!rdev->rsite->vg_name) | ||||
| 				continue; | ||||
|  | ||||
| 			if (!_add_dev_to_dtree(dm, dtree, rdev->lv, NULL)) | ||||
| 				return_0; | ||||
|  | ||||
| 			if (rdev->slog && | ||||
| 			    !_add_dev_to_dtree(dm, dtree, rdev->slog, NULL)) | ||||
| 				return_0; | ||||
| 		} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
| @@ -1904,7 +1850,7 @@ struct pool_cb_data { | ||||
| static int _pool_callback(struct dm_tree_node *node, | ||||
| 			  dm_node_callback_t type, void *cb_data) | ||||
| { | ||||
| 	int ret, status = 0, fd; | ||||
| 	int ret, status, fd; | ||||
| 	const struct dm_config_node *cn; | ||||
| 	const struct dm_config_value *cv; | ||||
| 	const struct pool_cb_data *data = cb_data; | ||||
| @@ -1912,45 +1858,12 @@ static int _pool_callback(struct dm_tree_node *node, | ||||
| 	const struct logical_volume *mlv = first_seg(pool_lv)->metadata_lv; | ||||
| 	long buf[64 / sizeof(long)]; /* buffer for short disk header (64B) */ | ||||
| 	int args = 0; | ||||
| 	char *mpath; | ||||
| 	const char *argv[19] = { /* Max supported 15 args */ | ||||
| 		find_config_tree_str_allow_empty(pool_lv->vg->cmd, data->exec, NULL) | ||||
| 		find_config_tree_str_allow_empty(pool_lv->vg->cmd, data->exec, NULL) /* argv[0] */ | ||||
| 	}; | ||||
|  | ||||
| 	if (!*argv[0]) /* *_check tool is unconfigured/disabled with "" setting */ | ||||
| 		return 1; | ||||
|  | ||||
| 	if (!(mpath = lv_dmpath_dup(data->dm->mem, mlv))) { | ||||
| 		log_error("Failed to build device path for checking pool metadata %s.", | ||||
| 			  display_lvname(mlv)); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (data->skip_zero) { | ||||
| 		if ((fd = open(mpath, O_RDONLY)) < 0) { | ||||
| 			log_sys_error("open", mpath); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		/* let's assume there is no problem to read 64 bytes */ | ||||
| 		if (read(fd, buf, sizeof(buf)) < (int)sizeof(buf)) { | ||||
| 			log_sys_error("read", mpath); | ||||
| 			if (close(fd)) | ||||
| 				log_sys_error("close", mpath); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		for (ret = 0; ret < (int) DM_ARRAY_SIZE(buf); ++ret) | ||||
| 			if (buf[ret]) | ||||
| 				break; | ||||
|  | ||||
| 		if (close(fd)) | ||||
| 			log_sys_error("close", mpath); | ||||
|  | ||||
| 		if (ret == (int) DM_ARRAY_SIZE(buf)) { | ||||
| 			log_debug_activation("Metadata checking skipped, detected empty disk header on %s.", | ||||
| 					     mpath); | ||||
| 			return 1; | ||||
| 		} | ||||
| 	} | ||||
| 	if (!*argv[0]) | ||||
| 		return 1; /* Checking disabled */ | ||||
|  | ||||
| 	if (!(cn = find_config_tree_array(mlv->vg->cmd, data->opts, NULL))) { | ||||
| 		log_error(INTERNAL_ERROR "Unable to find configuration for pool check options."); | ||||
| @@ -1972,7 +1885,36 @@ static int _pool_callback(struct dm_tree_node *node, | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	argv[++args] = mpath; | ||||
| 	if (!(argv[++args] = lv_dmpath_dup(data->dm->mem, mlv))) { | ||||
| 		log_error("Failed to build pool metadata path."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (data->skip_zero) { | ||||
| 		if ((fd = open(argv[args], O_RDONLY)) < 0) { | ||||
| 			log_sys_error("open", argv[args]); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		/* let's assume there is no problem to read 64 bytes */ | ||||
| 		if (read(fd, buf, sizeof(buf)) < (int)sizeof(buf)) { | ||||
| 			log_sys_error("read", argv[args]); | ||||
| 			if (close(fd)) | ||||
| 				log_sys_error("close", argv[args]); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		for (ret = 0; ret < (int) DM_ARRAY_SIZE(buf); ++ret) | ||||
| 			if (buf[ret]) | ||||
| 				break; | ||||
|  | ||||
| 		if (close(fd)) | ||||
| 			log_sys_error("close", argv[args]); | ||||
|  | ||||
| 		if (ret == (int) DM_ARRAY_SIZE(buf)) { | ||||
| 			log_debug_activation("%s skipped, detect empty disk header on %s.", | ||||
| 					     argv[0], argv[args]); | ||||
| 			return 1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!(ret = exec_cmd(pool_lv->vg->cmd, (const char * const *)argv, | ||||
| 			     &status, 0))) { | ||||
| @@ -2060,10 +2002,6 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, | ||||
| 	struct lv_segment *seg; | ||||
| 	struct dm_tree_node *node; | ||||
| 	const char *uuid; | ||||
| 	const struct logical_volume *plv; | ||||
|  | ||||
| 	if (lv_is_pvmove(lv) && (dm->track_pvmove_deps == 2)) | ||||
| 		return 1; /* Avoid rechecking of already seen pvmove LV */ | ||||
|  | ||||
| 	if (lv_is_cache_pool(lv)) { | ||||
| 		if (!dm_list_empty(&lv->segs_using_this_lv)) { | ||||
| @@ -2101,16 +2039,16 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, | ||||
| #endif | ||||
| 	} | ||||
|  | ||||
| 	if (origin_only && dm->activation && dm->track_external_lv_deps && | ||||
| 	if (origin_only && dm->activation && !dm->skip_external_lv && | ||||
| 	    lv_is_external_origin(lv)) { | ||||
| 		/* Find possible users of external origin lv */ | ||||
| 		dm->track_external_lv_deps = 0; /* avoid recursion */ | ||||
| 		dm->skip_external_lv = 1; /* avoid recursion */ | ||||
| 		dm_list_iterate_items(sl, &lv->segs_using_this_lv) | ||||
| 			/* Match only external_lv users */ | ||||
| 			if ((sl->seg->external_lv == lv) && | ||||
| 			    !_add_lv_to_dtree(dm, dtree, sl->seg->lv, 1)) | ||||
| 				return_0; | ||||
| 		dm->track_external_lv_deps = 1; | ||||
| 		dm->skip_external_lv = 0; | ||||
| 	} | ||||
|  | ||||
| 	if (lv_is_thin_pool(lv)) { | ||||
| @@ -2184,14 +2122,11 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, | ||||
| 		return_0; | ||||
|  | ||||
| 	/* Add any LVs referencing a PVMOVE LV unless told not to. */ | ||||
| 	if ((dm->track_pvmove_deps == 1) && lv_is_pvmove(lv)) { | ||||
| 		dm->track_pvmove_deps = 2; /* Mark as already seen */ | ||||
| 		dm_list_iterate_items(sl, &lv->segs_using_this_lv) { | ||||
| 			/* If LV is snapshot COW - whole snapshot needs reload */ | ||||
| 			plv = lv_is_cow(sl->seg->lv) ? origin_from_cow(sl->seg->lv) : sl->seg->lv; | ||||
| 			if (!_add_lv_to_dtree(dm, dtree, plv, 0)) | ||||
| 	if (dm->track_pvmove_deps && lv_is_pvmove(lv)) { | ||||
| 		dm->track_pvmove_deps = 0; | ||||
| 		dm_list_iterate_items(sl, &lv->segs_using_this_lv) | ||||
| 			if (!_add_lv_to_dtree(dm, dtree, sl->seg->lv, origin_only)) | ||||
| 				return_0; | ||||
| 		} | ||||
| 		dm->track_pvmove_deps = 1; | ||||
| 	} | ||||
|  | ||||
| @@ -2206,9 +2141,14 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	/* Adding LV head of replicator adds all other related devs */ | ||||
| 	if (lv_is_replicator_dev(lv) && | ||||
| 	    !_add_partial_replicator_to_dtree(dm, dtree, lv)) | ||||
| 		return_0; | ||||
|  | ||||
| 	/* Add any LVs used by segments in this LV */ | ||||
| 	dm_list_iterate_items(seg, &lv->segments) { | ||||
| 		if (seg->external_lv && dm->track_external_lv_deps && | ||||
| 		if (seg->external_lv && !dm->skip_external_lv && | ||||
| 		    !_add_lv_to_dtree(dm, dtree, seg->external_lv, 1)) /* stack */ | ||||
| 			return_0; | ||||
| 		if (seg->log_lv && | ||||
| @@ -2218,7 +2158,7 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, | ||||
| 		    !_add_lv_to_dtree(dm, dtree, seg->metadata_lv, 0)) | ||||
| 			return_0; | ||||
| 		if (seg->pool_lv && | ||||
| 		    (lv_is_cache_pool(seg->pool_lv) || dm->track_external_lv_deps) && | ||||
| 		    (lv_is_cache_pool(seg->pool_lv) || !dm->skip_external_lv) && | ||||
| 		    /* When activating and not origin_only detect linear 'overlay' over pool */ | ||||
| 		    !_add_lv_to_dtree(dm, dtree, seg->pool_lv, dm->activation ? origin_only : 1)) | ||||
| 			return_0; | ||||
| @@ -2274,7 +2214,7 @@ static char *_add_error_or_zero_device(struct dev_manager *dm, struct dm_tree *d | ||||
| 	struct lv_segment *seg_i; | ||||
| 	struct dm_info info; | ||||
| 	int segno = -1, i = 0; | ||||
| 	uint64_t size = (uint64_t) _seg_len(seg) * seg->lv->vg->extent_size; | ||||
| 	uint64_t size = (uint64_t) seg->len * seg->lv->vg->extent_size; | ||||
|  | ||||
| 	dm_list_iterate_items(seg_i, &seg->lv->segments) { | ||||
| 		if (seg == seg_i) { | ||||
| @@ -2553,14 +2493,14 @@ static int _add_target_to_dtree(struct dev_manager *dm, | ||||
|  | ||||
| 	if (!seg->segtype->ops->add_target_line) { | ||||
| 		log_error(INTERNAL_ERROR "_emit_target cannot handle " | ||||
| 			  "segment type %s.", lvseg_name(seg)); | ||||
| 			  "segment type %s.", seg->segtype->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return seg->segtype->ops->add_target_line(dm, dm->mem, dm->cmd, | ||||
| 						  &dm->target_state, seg, | ||||
| 						  laopts, dnode, | ||||
| 						  extent_size * _seg_len(seg), | ||||
| 						  extent_size * seg->len, | ||||
| 						  &dm->pvmove_mirror_count); | ||||
| } | ||||
|  | ||||
| @@ -2569,6 +2509,64 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, | ||||
| 				struct lv_activate_opts *laopts, | ||||
| 				const char *layer); | ||||
|  | ||||
| /* Add all replicators' LVs */ | ||||
| static int _add_replicator_dev_target_to_dtree(struct dev_manager *dm, | ||||
| 					       struct dm_tree *dtree, | ||||
| 					       struct lv_segment *seg, | ||||
| 					       struct lv_activate_opts *laopts) | ||||
| { | ||||
| 	struct replicator_device *rdev; | ||||
| 	struct replicator_site *rsite; | ||||
|  | ||||
| 	/* For inactive replicator add linear mapping */ | ||||
| 	if (!lv_is_active_replicator_dev(seg->lv)) { | ||||
| 		if (!_add_new_lv_to_dtree(dm, dtree, seg->lv->rdevice->lv, laopts, NULL)) | ||||
| 			return_0; | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	/* Add rlog and replicator nodes */ | ||||
| 	if (!seg->replicator || | ||||
| 	    !first_seg(seg->replicator)->rlog_lv || | ||||
| 	    !_add_new_lv_to_dtree(dm, dtree, | ||||
| 				  first_seg(seg->replicator)->rlog_lv, | ||||
| 				  laopts, NULL) || | ||||
| 	    !_add_new_lv_to_dtree(dm, dtree, seg->replicator, laopts, NULL)) | ||||
| 	    return_0; | ||||
|  | ||||
| 	/* Activation of one replicator_dev node activates all other nodes */ | ||||
| 	dm_list_iterate_items(rsite, &seg->replicator->rsites) { | ||||
| 		dm_list_iterate_items(rdev, &rsite->rdevices) { | ||||
| 			if (rdev->lv && | ||||
| 			    !_add_new_lv_to_dtree(dm, dtree, rdev->lv, | ||||
| 						  laopts, NULL)) | ||||
| 				return_0; | ||||
|  | ||||
| 			if (rdev->slog && | ||||
| 			    !_add_new_lv_to_dtree(dm, dtree, rdev->slog, | ||||
| 						  laopts, NULL)) | ||||
| 				return_0; | ||||
| 		} | ||||
| 	} | ||||
| 	/* Add remaining replicator-dev nodes in the second loop | ||||
| 	 * to avoid multiple retries for inserting all elements */ | ||||
| 	dm_list_iterate_items(rsite, &seg->replicator->rsites) { | ||||
| 		if (rsite->state != REPLICATOR_STATE_ACTIVE) | ||||
| 			continue; | ||||
| 		dm_list_iterate_items(rdev, &rsite->rdevices) { | ||||
| 			if (rdev->replicator_dev->lv == seg->lv) | ||||
| 				continue; | ||||
| 			if (!rdev->replicator_dev->lv || | ||||
| 			    !_add_new_lv_to_dtree(dm, dtree, | ||||
| 						  rdev->replicator_dev->lv, | ||||
| 						  laopts, NULL)) | ||||
| 				return_0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int _add_new_external_lv_to_dtree(struct dev_manager *dm, | ||||
| 					 struct dm_tree *dtree, | ||||
| 					 struct logical_volume *external_lv, | ||||
| @@ -2577,7 +2575,7 @@ static int _add_new_external_lv_to_dtree(struct dev_manager *dm, | ||||
| 	struct seg_list *sl; | ||||
|  | ||||
| 	/* Do not want to recursively add externals again */ | ||||
| 	if (!dm->track_external_lv_deps) | ||||
| 	if (dm->skip_external_lv) | ||||
| 		return 1; | ||||
|  | ||||
| 	/* | ||||
| @@ -2585,7 +2583,7 @@ static int _add_new_external_lv_to_dtree(struct dev_manager *dm, | ||||
| 	 * process all LVs related to this LV, and we want to | ||||
| 	 * skip repeated invocation of external lv processing | ||||
| 	 */ | ||||
| 	dm->track_external_lv_deps = 0; | ||||
| 	dm->skip_external_lv = 1; | ||||
|  | ||||
| 	log_debug_activation("Adding external origin LV %s and all active users.", | ||||
| 			     display_lvname(external_lv)); | ||||
| @@ -2611,7 +2609,7 @@ static int _add_new_external_lv_to_dtree(struct dev_manager *dm, | ||||
| 	log_debug_activation("Finished adding external origin LV %s and all active users.", | ||||
| 			     display_lvname(external_lv)); | ||||
|  | ||||
| 	dm->track_external_lv_deps = 1; | ||||
| 	dm->skip_external_lv = 0; | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
| @@ -2669,6 +2667,11 @@ static int _add_segment_to_dtree(struct dev_manager *dm, | ||||
| 				  lv_layer(seg->pool_lv))) | ||||
| 		return_0; | ||||
|  | ||||
| 	if (seg_is_replicator_dev(seg)) { | ||||
| 		if (!_add_replicator_dev_target_to_dtree(dm, dtree, seg, laopts)) | ||||
| 			return_0; | ||||
| 	} | ||||
|  | ||||
| 	/* Add any LVs used by this segment */ | ||||
| 	for (s = 0; s < seg->area_count; ++s) { | ||||
| 		if ((seg_type(seg, s) == AREA_LV) && | ||||
| @@ -2690,7 +2693,7 @@ static int _add_segment_to_dtree(struct dev_manager *dm, | ||||
| 		/* Replace target and all its used devs with error mapping */ | ||||
| 		log_debug_activation("Using error for pending delete %s.", | ||||
| 				     display_lvname(seg->lv)); | ||||
| 		if (!dm_tree_node_add_error_target(dnode, (uint64_t)seg->lv->vg->extent_size * _seg_len(seg))) | ||||
| 		if (!dm_tree_node_add_error_target(dnode, (uint64_t)seg->lv->vg->extent_size * seg->len)) | ||||
| 			return_0; | ||||
| 	} else if (!_add_target_to_dtree(dm, dnode, seg, laopts)) | ||||
| 		return_0; | ||||
| @@ -3072,7 +3075,7 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv, | ||||
| 				     (laopts->origin_only) ? " origin-only" : "", | ||||
| 				     display_lvname(lv)); | ||||
|  | ||||
| 	/* Some LV cannot be used for top level tree */ | ||||
| 	/* Some LV can be used for top level tree */ | ||||
| 	/* TODO: add more.... */ | ||||
| 	if (lv_is_cache_pool(lv) && !dm_list_empty(&lv->segs_using_this_lv)) { | ||||
| 		log_error(INTERNAL_ERROR "Cannot create tree for %s.", | ||||
| @@ -3082,7 +3085,6 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv, | ||||
| 	/* Some targets may build bigger tree for activation */ | ||||
| 	dm->activation = ((action == PRELOAD) || (action == ACTIVATE)); | ||||
| 	dm->suspend = (action == SUSPEND_WITH_LOCKFS) || (action == SUSPEND); | ||||
| 	dm->track_external_lv_deps = 1; | ||||
|  | ||||
| 	if (!(dtree = _create_partial_dtree(dm, lv, laopts->origin_only))) | ||||
| 		return_0; | ||||
| @@ -3138,6 +3140,8 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv, | ||||
| 		if (!dm_tree_preload_children(root, dlid, DLID_SIZE)) | ||||
| 			goto_out; | ||||
|  | ||||
| 		//if (action == PRELOAD) { log_debug("SLEEP"); sleep(7); } | ||||
|  | ||||
| 		if ((dm_tree_node_size_changed(root) < 0)) | ||||
| 			dm->flush_required = 1; | ||||
| 		/* Currently keep the code require flush for any | ||||
| @@ -3160,6 +3164,7 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv, | ||||
| 		log_error(INTERNAL_ERROR "_tree_action: Action %u not supported.", action); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	r = 1; | ||||
|  | ||||
| out: | ||||
|   | ||||
| @@ -186,11 +186,11 @@ static int _mk_link(const char *dev_dir, const char *vg_name, | ||||
| 			    !stat(lv_path, &buf)) { | ||||
| 				if (buf_lp.st_rdev == buf.st_rdev) | ||||
| 					return 1; | ||||
|  | ||||
| 				log_warn("Symlink %s that should have been " | ||||
| 					 "created by udev does not have " | ||||
| 					 "correct target. Falling back to " | ||||
| 					 "direct link creation", lv_path); | ||||
| 				else | ||||
| 					log_warn("Symlink %s that should have been " | ||||
| 						 "created by udev does not have " | ||||
| 						 "correct target. Falling back to " | ||||
| 						 "direct link creation", lv_path); | ||||
| 			} else | ||||
| 				log_warn("Symlink %s that should have been " | ||||
| 					 "created by udev could not be checked " | ||||
| @@ -239,9 +239,7 @@ static int _rm_link(const char *dev_dir, const char *vg_name, | ||||
| 			return 1; | ||||
| 		log_sys_error("lstat", lv_path); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (dm_udev_get_sync_support() && udev_checking() && check_udev) | ||||
| 	} else if (dm_udev_get_sync_support() && udev_checking() && check_udev) | ||||
| 		log_warn("The link %s should have been removed by udev " | ||||
| 			 "but it is still present. Falling back to " | ||||
| 			 "direct link removal.", lv_path); | ||||
| @@ -480,9 +478,9 @@ int fs_rename_lv(const struct logical_volume *lv, const char *dev, | ||||
| 			 _fs_op(FS_ADD, lv->vg->cmd->dev_dir, lv->vg->name, | ||||
| 				lv->name, dev, "", lv->vg->cmd->current_settings.udev_rules)); | ||||
| 	} | ||||
|  | ||||
| 	return _fs_op(FS_RENAME, lv->vg->cmd->dev_dir, lv->vg->name, lv->name, | ||||
| 		      dev, old_lvname, lv->vg->cmd->current_settings.udev_rules); | ||||
| 	else  | ||||
| 		return _fs_op(FS_RENAME, lv->vg->cmd->dev_dir, lv->vg->name, lv->name, | ||||
| 			      dev, old_lvname, lv->vg->cmd->current_settings.udev_rules); | ||||
| } | ||||
|  | ||||
| void fs_unlock(void) | ||||
|   | ||||
							
								
								
									
										21
									
								
								lib/cache/lvmcache.c
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								lib/cache/lvmcache.c
									
									
									
									
										vendored
									
									
								
							| @@ -154,7 +154,7 @@ static void _free_cached_vgmetadata(struct lvmcache_vginfo *vginfo) | ||||
| 		vginfo->cft = NULL; | ||||
| 	} | ||||
|  | ||||
| 	log_debug_cache("lvmcache: VG %s wiped.", vginfo->vgname); | ||||
| 	log_debug_cache("Metadata cache: VG %s wiped.", vginfo->vgname); | ||||
|  | ||||
| 	release_vg(vginfo->cached_vg); | ||||
| } | ||||
| @@ -197,7 +197,7 @@ static void _store_metadata(struct volume_group *vg, unsigned precommitted) | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	log_debug_cache("lvmcache: VG %s (%s) stored (%" PRIsize_t " bytes%s).", | ||||
| 	log_debug_cache("Metadata cache: VG %s (%s) stored (%" PRIsize_t " bytes%s).", | ||||
| 			vginfo->vgname, uuid, size, | ||||
| 			precommitted ? ", precommitted" : ""); | ||||
| } | ||||
| @@ -289,7 +289,7 @@ void lvmcache_commit_metadata(const char *vgname) | ||||
| 		return; | ||||
|  | ||||
| 	if (vginfo->precommitted) { | ||||
| 		log_debug_cache("lvmcache: Upgraded pre-committed VG %s metadata to committed.", | ||||
| 		log_debug_cache("Precommitted metadata cache: VG %s upgraded to committed.", | ||||
| 				vginfo->vgname); | ||||
| 		vginfo->precommitted = 0; | ||||
| 	} | ||||
| @@ -616,7 +616,7 @@ struct lvmcache_vginfo *lvmcache_vginfo_from_vgid(const char *vgid) | ||||
| 	id[ID_LEN] = '\0'; | ||||
|  | ||||
| 	if (!(vginfo = dm_hash_lookup(_vgid_hash, id))) { | ||||
| 		log_debug_cache("lvmcache has no info for vgid \"%s\"", id); | ||||
| 		log_debug_cache("Metadata cache has no info for vgid \"%s\"", id); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| @@ -1600,6 +1600,8 @@ void lvmcache_del(struct lvmcache_info *info) | ||||
| 	info->label->labeller->ops->destroy_label(info->label->labeller, | ||||
| 						  info->label); | ||||
| 	dm_free(info); | ||||
|  | ||||
| 	return; | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -1870,7 +1872,7 @@ static int _lvmcache_update_vgname(struct lvmcache_info *info, | ||||
| 				vginfo->vgid[0] ? vginfo->vgid : "", | ||||
| 				vginfo->vgid[0] ? ")" : "", mdabuf); | ||||
| 	} else | ||||
| 		log_debug_cache("lvmcache: Initialised VG %s.", vgname); | ||||
| 		log_debug_cache("lvmcache initialised VG %s.", vgname); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
| @@ -1896,7 +1898,8 @@ static int _lvmcache_update_vgstatus(struct lvmcache_info *info, uint32_t vgstat | ||||
| 						   info->vginfo->creation_host)) | ||||
| 		goto set_lock_type; | ||||
|  | ||||
| 	dm_free(info->vginfo->creation_host); | ||||
| 	if (info->vginfo->creation_host) | ||||
| 		dm_free(info->vginfo->creation_host); | ||||
|  | ||||
| 	if (!(info->vginfo->creation_host = dm_strdup(creation_host))) { | ||||
| 		log_error("cache creation host alloc failed for %s.", | ||||
| @@ -1915,7 +1918,8 @@ set_lock_type: | ||||
| 	if (info->vginfo->lock_type && !strcmp(lock_type, info->vginfo->lock_type)) | ||||
| 		goto set_system_id; | ||||
|  | ||||
| 	dm_free(info->vginfo->lock_type); | ||||
| 	if (info->vginfo->lock_type) | ||||
| 		dm_free(info->vginfo->lock_type); | ||||
|  | ||||
| 	if (!(info->vginfo->lock_type = dm_strdup(lock_type))) { | ||||
| 		log_error("cache lock_type alloc failed for %s", lock_type); | ||||
| @@ -1933,7 +1937,8 @@ set_system_id: | ||||
| 	if (info->vginfo->system_id && !strcmp(system_id, info->vginfo->system_id)) | ||||
| 		goto out; | ||||
|  | ||||
| 	dm_free(info->vginfo->system_id); | ||||
| 	if (info->vginfo->system_id) | ||||
| 		dm_free(info->vginfo->system_id); | ||||
|  | ||||
| 	if (!(info->vginfo->system_id = dm_strdup(system_id))) { | ||||
| 		log_error("cache system_id alloc failed for %s", system_id); | ||||
|   | ||||
							
								
								
									
										2
									
								
								lib/cache/lvmcache.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								lib/cache/lvmcache.h
									
									
									
									
										vendored
									
									
								
							| @@ -181,7 +181,7 @@ int lvmcache_foreach_ba(struct lvmcache_info *info, | ||||
| 			int (*fun)(struct disk_locn *, void *), | ||||
| 			void *baton); | ||||
|  | ||||
| int lvmcache_foreach_pv(struct lvmcache_vginfo *vginfo, | ||||
| int lvmcache_foreach_pv(struct lvmcache_vginfo *vg, | ||||
| 			int (*fun)(struct lvmcache_info *, void *), void * baton); | ||||
|  | ||||
| uint64_t lvmcache_device_size(struct lvmcache_info *info); | ||||
|   | ||||
							
								
								
									
										67
									
								
								lib/cache/lvmetad.c
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										67
									
								
								lib/cache/lvmetad.c
									
									
									
									
										vendored
									
									
								
							| @@ -39,7 +39,7 @@ static int64_t _lvmetad_update_timeout; | ||||
|  | ||||
| static int _found_lvm1_metadata = 0; | ||||
|  | ||||
| static struct volume_group *_lvmetad_pvscan_vg(struct cmd_context *cmd, struct volume_group *vg); | ||||
| static struct volume_group *lvmetad_pvscan_vg(struct cmd_context *cmd, struct volume_group *vg); | ||||
|  | ||||
| static uint64_t _monotonic_seconds(void) | ||||
| { | ||||
| @@ -66,7 +66,7 @@ static int _log_debug_inequality(const char *name, struct dm_config_node *a, str | ||||
| 					log_debug_lvmetad("VG %s metadata inequality at %s / %s: %s / %s", | ||||
| 							  name, a->key, b->key, av->v.str, bv->v.str); | ||||
| 				else if (a->v->type == DM_CFG_INT && b->v->type == DM_CFG_INT) | ||||
| 					log_debug_lvmetad("VG %s metadata inequality at %s / %s: " FMTd64 " / " FMTd64, | ||||
| 					log_debug_lvmetad("VG %s metadata inequality at %s / %s: " FMTi64 " / " FMTi64, | ||||
| 							  name, a->key, b->key, av->v.i, bv->v.i); | ||||
| 				else | ||||
| 					log_debug_lvmetad("VG %s metadata inequality at %s / %s: type %d / type %d", | ||||
| @@ -145,14 +145,13 @@ int lvmetad_connect(struct cmd_context *cmd) | ||||
| 		_lvmetad_use = 1; | ||||
| 		_lvmetad_cmd = cmd; | ||||
| 		return 1; | ||||
| 	} else { | ||||
| 		log_debug_lvmetad("Failed to connect to lvmetad: %s", strerror(_lvmetad.error)); | ||||
| 		_lvmetad_connected = 0; | ||||
| 		_lvmetad_use = 0; | ||||
| 		_lvmetad_cmd = NULL; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	log_debug_lvmetad("Failed to connect to lvmetad: %s", strerror(_lvmetad.error)); | ||||
| 	_lvmetad_connected = 0; | ||||
| 	_lvmetad_use = 0; | ||||
| 	_lvmetad_cmd = NULL; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int lvmetad_used(void) | ||||
| @@ -551,7 +550,6 @@ static int _token_update(int *replaced_update) | ||||
| 	daemon_reply reply; | ||||
| 	const char *token_expected; | ||||
| 	const char *prev_token; | ||||
| 	const char *reply_str; | ||||
| 	int update_pid; | ||||
| 	int ending_our_update; | ||||
|  | ||||
| @@ -568,14 +566,13 @@ static int _token_update(int *replaced_update) | ||||
| 	} | ||||
|  | ||||
| 	update_pid = (int)daemon_reply_int(reply, "update_pid", 0); | ||||
| 	reply_str = daemon_reply_str(reply, "response", ""); | ||||
|  | ||||
| 	/* | ||||
| 	 * A mismatch can only happen when this command attempts to set the | ||||
| 	 * token to filter:<hash> at the end of its update, but the update has | ||||
| 	 * been preempted in lvmetad by a new one (from update_pid). | ||||
| 	 */ | ||||
| 	if (!strcmp(reply_str, "token_mismatch")) { | ||||
| 	if (!strcmp(daemon_reply_str(reply, "response", ""), "token_mismatch")) { | ||||
| 		token_expected = daemon_reply_str(reply, "expected", ""); | ||||
|  | ||||
| 		ending_our_update = strcmp(_lvmetad_token, LVMETAD_TOKEN_UPDATE_IN_PROGRESS); | ||||
| @@ -601,7 +598,7 @@ static int _token_update(int *replaced_update) | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (strcmp(reply_str, "OK")) { | ||||
| 	if (strcmp(daemon_reply_str(reply, "response", ""), "OK")) { | ||||
| 		log_error("Failed response from lvmetad for token update."); | ||||
| 		daemon_reply_destroy(reply); | ||||
| 		return 0; | ||||
| @@ -628,7 +625,6 @@ static int _lvmetad_handle_reply(daemon_reply reply, const char *id, const char | ||||
| { | ||||
| 	const char *token_expected; | ||||
| 	const char *action; | ||||
| 	const char *reply_str; | ||||
| 	int action_modifies = 0; | ||||
| 	int daemon_in_update; | ||||
| 	int we_are_in_update; | ||||
| @@ -666,15 +662,15 @@ static int _lvmetad_handle_reply(daemon_reply reply, const char *id, const char | ||||
| 	} | ||||
|  | ||||
| 	if (reply.error) { | ||||
| 		log_error("lvmetad cannot be used due to error: %s", strerror(reply.error)); | ||||
| 		log_warn("WARNING: lvmetad cannot be used due to error: %s", strerror(reply.error)); | ||||
| 		goto fail; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Errors related to token mismatch. | ||||
| 	 */ | ||||
| 	reply_str = daemon_reply_str(reply, "response", ""); | ||||
| 	if (!strcmp(reply_str, "token_mismatch")) { | ||||
|  | ||||
| 	if (!strcmp(daemon_reply_str(reply, "response", ""), "token_mismatch")) { | ||||
|  | ||||
| 		token_expected = daemon_reply_str(reply, "expected", ""); | ||||
| 		update_pid = (int)daemon_reply_int(reply, "update_pid", 0); | ||||
| @@ -772,14 +768,14 @@ static int _lvmetad_handle_reply(daemon_reply reply, const char *id, const char | ||||
| 	 */ | ||||
|  | ||||
| 	/* All OK? */ | ||||
| 	if (!strcmp(reply_str, "OK")) { | ||||
| 	if (!strcmp(daemon_reply_str(reply, "response", ""), "OK")) { | ||||
| 		if (found) | ||||
| 			*found = 1; | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	/* Unknown device permitted? */ | ||||
| 	if (found && !strcmp(reply_str, "unknown")) { | ||||
| 	if (found && !strcmp(daemon_reply_str(reply, "response", ""), "unknown")) { | ||||
| 		log_very_verbose("Request to %s %s%sin lvmetad did not find any matching object.", | ||||
| 				 action, object, *object ? " " : ""); | ||||
| 		*found = 0; | ||||
| @@ -787,7 +783,7 @@ static int _lvmetad_handle_reply(daemon_reply reply, const char *id, const char | ||||
| 	} | ||||
|  | ||||
| 	/* Multiple VGs with the same name were found. */ | ||||
| 	if (found && !strcmp(reply_str, "multiple")) { | ||||
| 	if (found && !strcmp(daemon_reply_str(reply, "response", ""), "multiple")) { | ||||
| 		log_very_verbose("Request to %s %s%sin lvmetad found multiple matching objects.", | ||||
| 				 action, object, *object ? " " : ""); | ||||
| 		if (found) | ||||
| @@ -1093,7 +1089,7 @@ struct volume_group *lvmetad_vg_lookup(struct cmd_context *cmd, const char *vgna | ||||
| 		 * invalidated the cached vg. | ||||
| 		 */ | ||||
| 		if (rescan) { | ||||
| 			if (!(vg2 = _lvmetad_pvscan_vg(cmd, vg))) { | ||||
| 			if (!(vg2 = lvmetad_pvscan_vg(cmd, vg))) { | ||||
| 				log_debug_lvmetad("VG %s from lvmetad not found during rescan.", vgname); | ||||
| 				fid = NULL; | ||||
| 				release_vg(vg); | ||||
| @@ -1284,7 +1280,7 @@ int lvmetad_vg_update_finish(struct volume_group *vg) | ||||
| 		if (pvl->pv->dev && !lvmetad_pv_found(vg->cmd, &pvl->pv->id, pvl->pv->dev, | ||||
| 						      vgu->fid ? vgu->fid->fmt : pvl->pv->fmt, | ||||
| 						      pvl->pv->label_sector, NULL, NULL, NULL)) | ||||
| 			return_0; | ||||
| 			return 0; | ||||
| 	} | ||||
|  | ||||
| 	vg->lvmetad_update_pending = 0; | ||||
| @@ -1308,7 +1304,7 @@ int lvmetad_vg_remove_pending(struct volume_group *vg) | ||||
| 	reply = _lvmetad_send(vg->cmd, "set_vg_info", | ||||
| 			      "name = %s", vg->name, | ||||
| 			      "uuid = %s", uuid, | ||||
| 			      "version = %"PRId64, (int64_t)0, | ||||
| 			      "version = %d", 0, | ||||
| 			      NULL); | ||||
|  | ||||
| 	if (!_lvmetad_handle_reply(reply, "set_vg_info", vg->name, NULL)) { | ||||
| @@ -1519,7 +1515,7 @@ int lvmetad_vg_list_to_lvmcache(struct cmd_context *cmd) | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| struct extract_dl_baton { | ||||
| struct _extract_dl_baton { | ||||
| 	int i; | ||||
| 	struct dm_config_tree *cft; | ||||
| 	struct dm_config_node *pre_sib; | ||||
| @@ -1527,7 +1523,7 @@ struct extract_dl_baton { | ||||
|  | ||||
| static int _extract_mda(struct metadata_area *mda, void *baton) | ||||
| { | ||||
| 	struct extract_dl_baton *b = baton; | ||||
| 	struct _extract_dl_baton *b = baton; | ||||
| 	struct dm_config_node *cn; | ||||
| 	char id[32]; | ||||
|  | ||||
| @@ -1548,7 +1544,7 @@ static int _extract_mda(struct metadata_area *mda, void *baton) | ||||
|  | ||||
| static int _extract_disk_location(const char *name, struct disk_locn *dl, void *baton) | ||||
| { | ||||
| 	struct extract_dl_baton *b = baton; | ||||
| 	struct _extract_dl_baton *b = baton; | ||||
| 	struct dm_config_node *cn; | ||||
| 	char id[32]; | ||||
|  | ||||
| @@ -1583,7 +1579,7 @@ static int _extract_ba(struct disk_locn *ba, void *baton) | ||||
| static int _extract_mdas(struct lvmcache_info *info, struct dm_config_tree *cft, | ||||
| 			 struct dm_config_node *pre_sib) | ||||
| { | ||||
| 	struct extract_dl_baton baton = { .cft = cft }; | ||||
| 	struct _extract_dl_baton baton = { .cft = cft }; | ||||
|  | ||||
| 	if (!lvmcache_foreach_mda(info, &_extract_mda, &baton)) | ||||
| 		return 0; | ||||
| @@ -1610,7 +1606,7 @@ int lvmetad_pv_found(struct cmd_context *cmd, const struct id *pvid, struct devi | ||||
| 	struct dm_config_tree *pvmeta, *vgmeta; | ||||
| 	const char *status = NULL, *vgname = NULL; | ||||
| 	int64_t changed = 0; | ||||
| 	int result, seqno_after; | ||||
| 	int result; | ||||
|  | ||||
| 	if (!lvmetad_used() || test_mode()) | ||||
| 		return 1; | ||||
| @@ -1675,12 +1671,10 @@ int lvmetad_pv_found(struct cmd_context *cmd, const struct id *pvid, struct devi | ||||
|  | ||||
| 	result = _lvmetad_handle_reply(reply, "pv_found", uuid, NULL); | ||||
|  | ||||
| 	if (vg && result) { | ||||
| 		seqno_after = daemon_reply_int(reply, "seqno_after", -1); | ||||
| 		if ((seqno_after != vg->seqno) || | ||||
| 		    (seqno_after != daemon_reply_int(reply, "seqno_before", -1))) | ||||
| 			log_warn("WARNING: Inconsistent metadata found for VG %s", vg->name); | ||||
| 	} | ||||
| 	if (vg && result && | ||||
| 	    (daemon_reply_int(reply, "seqno_after", -1) != vg->seqno || | ||||
| 	     daemon_reply_int(reply, "seqno_after", -1) != daemon_reply_int(reply, "seqno_before", -1))) | ||||
| 		log_warn("WARNING: Inconsistent metadata found for VG %s", vg->name); | ||||
|  | ||||
| 	if (result && found_vgnames) { | ||||
| 		status = daemon_reply_str(reply, "status", NULL); | ||||
| @@ -1792,7 +1786,7 @@ static int _lvmetad_pvscan_single(struct metadata_area *mda, void *baton) | ||||
|  * the VG, and that PV may have been reused for another VG. | ||||
|  */ | ||||
|  | ||||
| static struct volume_group *_lvmetad_pvscan_vg(struct cmd_context *cmd, struct volume_group *vg) | ||||
| static struct volume_group *lvmetad_pvscan_vg(struct cmd_context *cmd, struct volume_group *vg) | ||||
| { | ||||
| 	char pvid_s[ID_LEN + 1] __attribute__((aligned(8))); | ||||
| 	char uuid[64] __attribute__((aligned(8))); | ||||
| @@ -2880,9 +2874,6 @@ int lvmetad_is_disabled(struct cmd_context *cmd, const char **reason) | ||||
| 		} else if (strstr(reply_reason, LVMETAD_DISABLE_REASON_DIRECT)) { | ||||
| 			*reason = "the disable flag was set directly"; | ||||
|  | ||||
| 		} else if (strstr(reply_reason, LVMETAD_DISABLE_REASON_REPAIR)) { | ||||
| 			*reason = "a repair command was run"; | ||||
|  | ||||
| 		} else if (strstr(reply_reason, LVMETAD_DISABLE_REASON_LVM1)) { | ||||
| 			*reason = "LVM1 metadata was found"; | ||||
|  | ||||
|   | ||||
| @@ -36,38 +36,6 @@ static unsigned _feature_mask; | ||||
|         log_error(t " segment %s of logical volume %s.", ## p,	\ | ||||
|                   dm_config_parent_name(sn), seg->lv->name), 0; | ||||
|  | ||||
| static int _cache_out_line(const char *line, void *_f) | ||||
| { | ||||
| 	log_print("    Setting\t\t%s", line); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void _cache_display(const struct lv_segment *seg) | ||||
| { | ||||
| 	const struct dm_config_node *n; | ||||
| 	const struct lv_segment *pool_seg = | ||||
| 		seg_is_cache_pool(seg) ? seg : first_seg(seg->pool_lv); | ||||
|  | ||||
| 	log_print("  Chunk size\t\t%s", | ||||
| 		  display_size(seg->lv->vg->cmd, pool_seg->chunk_size)); | ||||
|  | ||||
| 	if (pool_seg->cache_metadata_format != CACHE_METADATA_FORMAT_UNSELECTED) | ||||
| 		log_print("  Metadata format\t%u", pool_seg->cache_metadata_format); | ||||
|  | ||||
| 	if (pool_seg->cache_mode != CACHE_MODE_UNSELECTED) | ||||
| 		log_print("  Mode\t\t%s", get_cache_mode_name(pool_seg)); | ||||
|  | ||||
| 	if (pool_seg->policy_name) | ||||
| 		log_print("  Policy\t\t%s", pool_seg->policy_name); | ||||
|  | ||||
| 	if (pool_seg->policy_settings && | ||||
| 	    (n = pool_seg->policy_settings->child)) | ||||
| 		dm_config_write_node(n, _cache_out_line, NULL); | ||||
|  | ||||
| 	log_print(" "); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * When older metadata are loaded without newer settings, | ||||
|  * set then to default settings (the one that could have been | ||||
| @@ -84,13 +52,7 @@ static void _fix_missing_defaults(struct lv_segment *cpool_seg) | ||||
| 			    cpool_seg->policy_name); | ||||
| 	} | ||||
|  | ||||
| 	if (cpool_seg->cache_metadata_format == CACHE_METADATA_FORMAT_UNSELECTED) { | ||||
| 		cpool_seg->cache_metadata_format = CACHE_METADATA_FORMAT_1; | ||||
| 		log_verbose("Cache pool %s uses implicit metadata format %u.", | ||||
| 			    display_lvname(cpool_seg->lv), cpool_seg->cache_metadata_format); | ||||
| 	} | ||||
|  | ||||
| 	if (cpool_seg->cache_mode == CACHE_MODE_UNSELECTED) { | ||||
| 	if (cpool_seg->cache_mode == CACHE_MODE_UNDEFINED) { | ||||
| 		cpool_seg->cache_mode = CACHE_MODE_WHEN_MISSING; | ||||
| 		log_verbose("Cache pool %s is missing cache mode, using %s.", | ||||
| 			    display_lvname(cpool_seg->lv), | ||||
| @@ -145,16 +107,6 @@ static int _cache_pool_text_import(struct lv_segment *seg, | ||||
| 			return SEG_LOG_ERROR("Failed to duplicate policy in"); | ||||
| 	} | ||||
|  | ||||
| 	if (dm_config_has_node(sn, "metadata_format")) { | ||||
| 		if (!dm_config_get_uint32(sn, "metadata_format", &seg->cache_metadata_format) || | ||||
| 		    ((seg->cache_metadata_format != CACHE_METADATA_FORMAT_1) && | ||||
| 		     (seg->cache_metadata_format != CACHE_METADATA_FORMAT_2))) | ||||
| 			return SEG_LOG_ERROR("Unknown cache metadata format %u number in", | ||||
| 					     seg->cache_metadata_format); | ||||
| 		if (seg->cache_metadata_format == CACHE_METADATA_FORMAT_2) | ||||
| 			seg->lv->status |= LV_METADATA_FORMAT; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Read in policy args: | ||||
| 	 *   policy_settings { | ||||
| @@ -212,31 +164,12 @@ static int _cache_pool_text_export(const struct lv_segment *seg, | ||||
| 	outf(f, "metadata = \"%s\"", seg->metadata_lv->name); | ||||
| 	outf(f, "chunk_size = %" PRIu32, seg->chunk_size); | ||||
|  | ||||
| 	switch (seg->cache_metadata_format) { | ||||
| 	case CACHE_METADATA_FORMAT_UNSELECTED: | ||||
| 		/* Unselected format is not printed */ | ||||
| 		break; | ||||
| 	case CACHE_METADATA_FORMAT_1: | ||||
| 		/* If format 1 was already specified with cache pool, store it, | ||||
| 		 * otherwise format gets stored when LV is cached. | ||||
| 		 * NB: format 1 could be lost anytime, it's a default format. | ||||
| 		 * Older lvm2 tool can easily drop it. | ||||
| 		 */ | ||||
| 	case CACHE_METADATA_FORMAT_2: /* more in future ? */ | ||||
| 		outf(f, "metadata_format = " FMTu32, seg->cache_metadata_format); | ||||
| 		break; | ||||
| 	default: | ||||
| 		log_error(INTERNAL_ERROR "LV %s is using unknown cache metadada format %u.", | ||||
| 			  display_lvname(seg->lv), seg->cache_metadata_format); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Cache pool used by a cache LV holds data. Not ideal, | ||||
| 	 * but not worth to break backward compatibility, by shifting | ||||
| 	 * content to cache segment | ||||
| 	 */ | ||||
| 	if (seg->cache_mode != CACHE_MODE_UNSELECTED) { | ||||
| 	if (seg->cache_mode != CACHE_MODE_UNDEFINED) { | ||||
| 		if (!(cache_mode = get_cache_mode_name(seg))) | ||||
| 			return_0; | ||||
| 		outf(f, "cache_mode = \"%s\"", cache_mode); | ||||
| @@ -265,39 +198,6 @@ static void _destroy(struct segment_type *segtype) | ||||
| } | ||||
|  | ||||
| #ifdef DEVMAPPER_SUPPORT | ||||
| /* | ||||
|  * Parse and look for kernel symbol in /proc/kallsyms | ||||
|  * this could be our only change to figure out there is | ||||
|  * cache policy symbol already in the monolithic kernel | ||||
|  * where 'modprobe dm-cache-smq' will simply not work | ||||
|  */ | ||||
| static int _lookup_kallsyms(const char *symbol) | ||||
| { | ||||
| 	static const char _syms[] = "/proc/kallsyms"; | ||||
| 	int ret = 0; | ||||
| 	char *line = NULL; | ||||
| 	size_t len; | ||||
| 	FILE *s; | ||||
|  | ||||
| 	if (!(s = fopen(_syms, "r"))) | ||||
| 		log_sys_debug("fopen", _syms); | ||||
| 	else { | ||||
| 		while (getline(&line, &len, s) != -1) | ||||
| 			if (strstr(line, symbol)) { | ||||
| 				ret = 1; /* Found symbol */ | ||||
| 				log_debug("Found kernel symbol%s.", symbol); /* space is in symbol */ | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 		free(line); | ||||
| 		if (fclose(s)) | ||||
| 			log_sys_debug("fclose", _syms); | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| static int _target_present(struct cmd_context *cmd, | ||||
| 			   const struct lv_segment *seg __attribute__((unused)), | ||||
| 			   unsigned *attributes __attribute__((unused))) | ||||
| @@ -310,15 +210,13 @@ static int _target_present(struct cmd_context *cmd, | ||||
| 		unsigned cache_alias; | ||||
| 		const char feature[12]; | ||||
| 		const char module[12]; /* check dm-%s */ | ||||
| 		const char ksymbol[12]; /* check for kernel symbol */ | ||||
| 		const char *aliasing; | ||||
| 	} _features[] = { | ||||
| 		{ 1, 10, CACHE_FEATURE_METADATA2, 0, "metadata2" }, | ||||
| 		/* Assumption: cache >=1.9 always aliases MQ policy */ | ||||
| 		{ 1, 9, CACHE_FEATURE_POLICY_SMQ, CACHE_FEATURE_POLICY_MQ, "policy_smq", "cache-smq", | ||||
| 		 " smq_exit", " and aliases cache-mq" }, | ||||
| 		{ 1, 8, CACHE_FEATURE_POLICY_SMQ, 0, "policy_smq", "cache-smq", " smq_exit" }, | ||||
| 		{ 1, 3, CACHE_FEATURE_POLICY_MQ, 0, "policy_mq", "cache-mq", " mq_init" }, | ||||
| 		" and aliases cache-mq" }, | ||||
| 		{ 1, 8, CACHE_FEATURE_POLICY_SMQ, 0, "policy_smq", "cache-smq" }, | ||||
| 		{ 1, 3, CACHE_FEATURE_POLICY_MQ, 0, "policy_mq", "cache-mq" }, | ||||
| 	}; | ||||
| 	static const char _lvmconf[] = "global/cache_disabled_features"; | ||||
| 	static unsigned _attrs = 0; | ||||
| @@ -352,20 +250,9 @@ static int _target_present(struct cmd_context *cmd, | ||||
| 		for (i = 0; i < DM_ARRAY_SIZE(_features); ++i) { | ||||
| 			if (_attrs & _features[i].cache_feature) | ||||
| 				continue; /* already present */ | ||||
|  | ||||
| 			if (!_features[i].module[0]) { | ||||
| 				if ((maj > _features[i].maj) || | ||||
| 				    (maj == _features[i].maj && min >= _features[i].min)) { | ||||
| 					log_debug_activation("Cache supports %s.", | ||||
| 							     _features[i].feature); | ||||
| 					_attrs |= _features[i].cache_feature; | ||||
| 				} | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (((maj > _features[i].maj) || | ||||
| 			     (maj == _features[i].maj && min >= _features[i].min)) && | ||||
| 			    ((_features[i].ksymbol[0] && _lookup_kallsyms(_features[i].ksymbol)) || | ||||
| 			     module_present(cmd, _features[i].module))) { | ||||
| 			    module_present(cmd, _features[i].module)) { | ||||
| 				log_debug_activation("Cache policy %s is available%s.", | ||||
| 						     _features[i].module, | ||||
| 						     _features[i].aliasing ? : ""); | ||||
| @@ -423,7 +310,6 @@ static int _modules_needed(struct dm_pool *mem, | ||||
| #endif /* DEVMAPPER_SUPPORT */ | ||||
|  | ||||
| static struct segtype_handler _cache_pool_ops = { | ||||
| 	.display = _cache_display, | ||||
| 	.text_import = _cache_pool_text_import, | ||||
| 	.text_import_area_count = _cache_pool_text_import_area_count, | ||||
| 	.text_export = _cache_pool_text_export, | ||||
| @@ -513,7 +399,6 @@ static int _cache_add_target_line(struct dev_manager *dm, | ||||
| 	struct lv_segment *cache_pool_seg; | ||||
| 	char *metadata_uuid, *data_uuid, *origin_uuid; | ||||
| 	uint64_t feature_flags = 0; | ||||
| 	unsigned attr; | ||||
|  | ||||
| 	if (!seg->pool_lv || !seg_is_cache(seg)) { | ||||
| 		log_error(INTERNAL_ERROR "Passed segment is not cache."); | ||||
| @@ -541,26 +426,6 @@ static int _cache_add_target_line(struct dev_manager *dm, | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 	switch (cache_pool_seg->cache_metadata_format) { | ||||
| 	case CACHE_METADATA_FORMAT_1: break; | ||||
| 	case CACHE_METADATA_FORMAT_2: | ||||
| 		if (!_target_present(cmd, NULL, &attr)) | ||||
| 			return_0; | ||||
|  | ||||
| 		if (!(attr & CACHE_FEATURE_METADATA2)) { | ||||
| 			log_error("LV %s has metadata format %u unsuported by kernel.", | ||||
| 				  display_lvname(seg->lv), cache_pool_seg->cache_metadata_format); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		feature_flags |= DM_CACHE_FEATURE_METADATA2; | ||||
| 		log_debug_activation("Using metadata2 format for %s.", display_lvname(seg->lv)); | ||||
| 		break; | ||||
| 	default: | ||||
| 		log_error(INTERNAL_ERROR "LV %s has unknown metadata format %u.", | ||||
| 			  display_lvname(seg->lv), cache_pool_seg->cache_metadata_format); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!(metadata_uuid = build_dm_uuid(mem, cache_pool_seg->metadata_lv, NULL))) | ||||
| 		return_0; | ||||
|  | ||||
| @@ -587,7 +452,6 @@ static int _cache_add_target_line(struct dev_manager *dm, | ||||
| #endif /* DEVMAPPER_SUPPORT */ | ||||
|  | ||||
| static struct segtype_handler _cache_ops = { | ||||
| 	.display = _cache_display, | ||||
| 	.text_import = _cache_text_import, | ||||
| 	.text_import_area_count = _cache_text_import_area_count, | ||||
| 	.text_export = _cache_text_export, | ||||
|   | ||||
| @@ -54,7 +54,7 @@ | ||||
| #  include <malloc.h> | ||||
| #endif | ||||
|  | ||||
| static const size_t _linebuffer_size = 4096; | ||||
| static const size_t linebuffer_size = 4096; | ||||
|  | ||||
| /* | ||||
|  * Copy the input string, removing invalid characters. | ||||
| @@ -192,9 +192,6 @@ static int _get_env_vars(struct cmd_context *cmd) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (strcmp((getenv("LVM_RUN_BY_DMEVENTD") ? : "0"), "1") == 0) | ||||
| 		init_run_by_dmeventd(cmd); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| @@ -283,8 +280,6 @@ static int _parse_debug_classes(struct cmd_context *cmd) | ||||
| 			debug_classes |= LOG_CLASS_LVMPOLLD; | ||||
| 		else if (!strcasecmp(cv->v.str, "dbus")) | ||||
| 			debug_classes |= LOG_CLASS_DBUS; | ||||
| 		else if (!strcasecmp(cv->v.str, "io")) | ||||
| 			debug_classes |= LOG_CLASS_IO; | ||||
| 		else | ||||
| 			log_verbose("Unrecognised value for log/debug_classes: %s", cv->v.str); | ||||
| 	} | ||||
| @@ -476,12 +471,10 @@ bad: | ||||
|  | ||||
| int process_profilable_config(struct cmd_context *cmd) | ||||
| { | ||||
| 	const char *units; | ||||
|  | ||||
| 	if (!(cmd->default_settings.unit_factor = | ||||
| 	      dm_units_to_factor(units = find_config_tree_str(cmd, global_units_CFG, NULL), | ||||
| 	      dm_units_to_factor(find_config_tree_str(cmd, global_units_CFG, NULL), | ||||
| 				 &cmd->default_settings.unit_type, 1, NULL))) { | ||||
| 		log_error("Unrecognised configuration setting for global/units: %s", units); | ||||
| 		log_error("Invalid units specification"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| @@ -566,7 +559,7 @@ static int _process_config(struct cmd_context *cmd) | ||||
| #ifdef DEVMAPPER_SUPPORT | ||||
| 	dm_set_dev_dir(cmd->dev_dir); | ||||
|  | ||||
| 	if (!dm_set_uuid_prefix(UUID_PREFIX)) | ||||
| 	if (!dm_set_uuid_prefix("LVM-")) | ||||
| 		return_0; | ||||
| #endif | ||||
|  | ||||
| @@ -1503,6 +1496,11 @@ static int _init_segtypes(struct cmd_context *cmd) | ||||
| 		dm_list_add(&cmd->segtypes, &segtype->list); | ||||
| 	} | ||||
|  | ||||
| #ifdef REPLICATOR_INTERNAL | ||||
| 	if (!init_replicator_segtype(cmd, &seglib)) | ||||
| 		return 0; | ||||
| #endif | ||||
|  | ||||
| #ifdef RAID_INTERNAL | ||||
| 	if (!init_raid_segtypes(cmd, &seglib)) | ||||
| 		return 0; | ||||
| @@ -1757,15 +1755,6 @@ bad: | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int init_run_by_dmeventd(struct cmd_context *cmd) | ||||
| { | ||||
| 	init_dmeventd_monitor(DMEVENTD_MONITOR_IGNORE); | ||||
| 	init_ignore_suspended_devices(1); | ||||
| 	init_disable_dmeventd_monitoring(1); /* Lock settings */ | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void destroy_config_context(struct cmd_context *cmd) | ||||
| { | ||||
| 	_destroy_config(cmd); | ||||
| @@ -1875,7 +1864,7 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived, | ||||
| 	/* Set in/out stream buffering before glibc */ | ||||
| 	if (set_buffering) { | ||||
| 		/* Allocate 2 buffers */ | ||||
| 		if (!(cmd->linebuffer = dm_malloc(2 * _linebuffer_size))) { | ||||
| 		if (!(cmd->linebuffer = dm_malloc(2 * linebuffer_size))) { | ||||
| 			log_error("Failed to allocate line buffer."); | ||||
| 			goto out; | ||||
| 		} | ||||
| @@ -1886,7 +1875,7 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived, | ||||
| 		    (flags & O_ACCMODE) != O_WRONLY) { | ||||
| 			if (!reopen_standard_stream(&stdin, "r")) | ||||
| 				goto_out; | ||||
| 			if (setvbuf(stdin, cmd->linebuffer, _IOLBF, _linebuffer_size)) { | ||||
| 			if (setvbuf(stdin, cmd->linebuffer, _IOLBF, linebuffer_size)) { | ||||
| 				log_sys_error("setvbuf", ""); | ||||
| 				goto out; | ||||
| 			} | ||||
| @@ -1897,8 +1886,8 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived, | ||||
| 		    (flags & O_ACCMODE) != O_RDONLY) { | ||||
| 			if (!reopen_standard_stream(&stdout, "w")) | ||||
| 				goto_out; | ||||
| 			if (setvbuf(stdout, cmd->linebuffer + _linebuffer_size, | ||||
| 				     _IOLBF, _linebuffer_size)) { | ||||
| 			if (setvbuf(stdout, cmd->linebuffer + linebuffer_size, | ||||
| 				     _IOLBF, linebuffer_size)) { | ||||
| 				log_sys_error("setvbuf", ""); | ||||
| 				goto out; | ||||
| 			} | ||||
| @@ -2008,6 +1997,7 @@ out: | ||||
| 		cmd = NULL; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	return cmd; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -89,7 +89,6 @@ struct cmd_context { | ||||
| 	 */ | ||||
| 	const char *cmd_line; | ||||
| 	const char *name; /* needed before cmd->command is set */ | ||||
| 	struct command_name *cname; | ||||
| 	struct command *command; | ||||
| 	char **argv; | ||||
| 	struct arg_values *opt_arg_values; | ||||
| @@ -160,7 +159,6 @@ struct cmd_context { | ||||
| 	unsigned lockd_vg_rescan:1; | ||||
| 	unsigned lockd_vg_default_sh:1; | ||||
| 	unsigned lockd_vg_enforce_sh:1; | ||||
| 	unsigned lockd_lv_sh:1; | ||||
| 	unsigned vg_notify:1; | ||||
| 	unsigned lv_notify:1; | ||||
| 	unsigned pv_notify:1; | ||||
| @@ -243,7 +241,6 @@ int config_files_changed(struct cmd_context *cmd); | ||||
| int init_lvmcache_orphans(struct cmd_context *cmd); | ||||
| int init_filters(struct cmd_context *cmd, unsigned load_persistent_cache); | ||||
| int init_connections(struct cmd_context *cmd); | ||||
| int init_run_by_dmeventd(struct cmd_context *cmd); | ||||
|  | ||||
| /* | ||||
|  * A config context is a very light weight cmd struct that | ||||
|   | ||||
| @@ -65,11 +65,11 @@ struct config_source { | ||||
|  * Map each ID to respective definition of the configuration item. | ||||
|  */ | ||||
| static struct cfg_def_item _cfg_def_items[CFG_COUNT + 1] = { | ||||
| #define cfg_section(id, name, parent, flags, since_version, deprecated_since_version, deprecation_comment, comment) {id, parent, name, CFG_TYPE_SECTION, {0}, (flags), since_version, {0}, deprecated_since_version, deprecation_comment, comment}, | ||||
| #define cfg(id, name, parent, flags, type, default_value, since_version, unconfigured_value, deprecated_since_version, deprecation_comment, comment) {id, parent, name, type, {.v_##type = (default_value)}, (flags), since_version, {.v_UNCONFIGURED = (unconfigured_value)}, deprecated_since_version, deprecation_comment, comment}, | ||||
| #define cfg_runtime(id, name, parent, flags, type, since_version, deprecated_since_version, deprecation_comment, comment) {id, parent, name, type, {.fn_##type = get_default_##id}, (flags) | CFG_DEFAULT_RUN_TIME, since_version, {.fn_UNCONFIGURED = get_default_unconfigured_##id}, deprecated_since_version, (deprecation_comment), comment}, | ||||
| #define cfg_array(id, name, parent, flags, types, default_value, since_version, unconfigured_value, deprecated_since_version, deprecation_comment, comment) {id, parent, name, CFG_TYPE_ARRAY | (types), {.v_CFG_TYPE_STRING = (default_value)}, (flags), (since_version), {.v_UNCONFIGURED = (unconfigured_value)}, deprecated_since_version, deprecation_comment, comment}, | ||||
| #define cfg_array_runtime(id, name, parent, flags, types, since_version, deprecated_since_version, deprecation_comment, comment) {id, parent, name, CFG_TYPE_ARRAY | (types), {.fn_CFG_TYPE_STRING = get_default_##id}, (flags) | CFG_DEFAULT_RUN_TIME, (since_version), {.fn_UNCONFIGURED = get_default_unconfigured_##id}, deprecated_since_version, deprecation_comment, comment}, | ||||
| #define cfg_section(id, name, parent, flags, since_version, deprecated_since_version, deprecation_comment, comment) {id, parent, name, CFG_TYPE_SECTION, {0}, flags, since_version, {0}, deprecated_since_version, deprecation_comment, comment}, | ||||
| #define cfg(id, name, parent, flags, type, default_value, since_version, unconfigured_value, deprecated_since_version, deprecation_comment, comment) {id, parent, name, type, {.v_##type = default_value}, flags, since_version, {.v_UNCONFIGURED = unconfigured_value}, deprecated_since_version, deprecation_comment, comment}, | ||||
| #define cfg_runtime(id, name, parent, flags, type, since_version, deprecated_since_version, deprecation_comment, comment) {id, parent, name, type, {.fn_##type = get_default_##id}, flags | CFG_DEFAULT_RUN_TIME, since_version, {.fn_UNCONFIGURED = get_default_unconfigured_##id}, deprecated_since_version, deprecation_comment, comment}, | ||||
| #define cfg_array(id, name, parent, flags, types, default_value, since_version, unconfigured_value, deprecated_since_version, deprecation_comment, comment) {id, parent, name, CFG_TYPE_ARRAY | types, {.v_CFG_TYPE_STRING = default_value}, flags, since_version, {.v_UNCONFIGURED = unconfigured_value}, deprecated_since_version, deprecation_comment, comment}, | ||||
| #define cfg_array_runtime(id, name, parent, flags, types, since_version, deprecated_since_version, deprecation_comment, comment) {id, parent, name, CFG_TYPE_ARRAY | types, {.fn_CFG_TYPE_STRING = get_default_##id}, flags | CFG_DEFAULT_RUN_TIME, since_version, {.fn_UNCONFIGURED = get_default_unconfigured_##id}, deprecated_since_version, deprecation_comment, comment}, | ||||
| #include "config_settings.h" | ||||
| #undef cfg_section | ||||
| #undef cfg | ||||
| @@ -481,8 +481,7 @@ int override_config_tree_from_profile(struct cmd_context *cmd, | ||||
|  | ||||
| 	if (profile->source == CONFIG_PROFILE_COMMAND) | ||||
| 		return _override_config_tree_from_command_profile(cmd, profile); | ||||
|  | ||||
| 	if (profile->source == CONFIG_PROFILE_METADATA) | ||||
| 	else if (profile->source == CONFIG_PROFILE_METADATA) | ||||
| 		return _override_config_tree_from_metadata_profile(cmd, profile); | ||||
|  | ||||
| 	log_error(INTERNAL_ERROR "override_config_tree_from_profile: incorrect profile source type"); | ||||
| @@ -494,7 +493,7 @@ int override_config_tree_from_profile(struct cmd_context *cmd, | ||||
|  * and function avoids parsing of mda into config tree which | ||||
|  * remains unmodified and should not be used. | ||||
|  */ | ||||
| int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, dev_io_reason_t reason, | ||||
| int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, | ||||
| 			off_t offset, size_t size, off_t offset2, size_t size2, | ||||
| 			checksum_fn_t checksum_fn, uint32_t checksum, | ||||
| 			int checksum_only, int no_dup_node_check) | ||||
| @@ -533,7 +532,7 @@ int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, dev_io_r | ||||
| 			return 0; | ||||
| 		} | ||||
| 		if (!dev_read_circular(dev, (uint64_t) offset, size, | ||||
| 				       (uint64_t) offset2, size2, reason, buf)) { | ||||
| 				       (uint64_t) offset2, size2, buf)) { | ||||
| 			goto out; | ||||
| 		} | ||||
| 		fb = buf; | ||||
| @@ -542,7 +541,7 @@ int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, dev_io_r | ||||
| 	if (checksum_fn && checksum != | ||||
| 	    (checksum_fn(checksum_fn(INITIAL_CRC, (const uint8_t *)fb, size), | ||||
| 			 (const uint8_t *)(fb + size), size2))) { | ||||
| 		log_error("%s: Checksum error at offset %" PRIu64, dev_name(dev), (uint64_t) offset); | ||||
| 		log_error("%s: Checksum error", dev_name(dev)); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| @@ -601,7 +600,7 @@ int config_file_read(struct dm_config_tree *cft) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	r = config_file_read_fd(cft, cf->dev, DEV_IO_MDA_CONTENT, 0, (size_t) info.st_size, 0, 0, | ||||
| 	r = config_file_read_fd(cft, cf->dev, 0, (size_t) info.st_size, 0, 0, | ||||
| 				(checksum_fn_t) NULL, 0, 0, 0); | ||||
|  | ||||
| 	if (!cf->keep_open) { | ||||
| @@ -620,9 +619,9 @@ struct timespec config_file_timestamp(struct dm_config_tree *cft) | ||||
| } | ||||
|  | ||||
| #define cfg_def_get_item_p(id) (&_cfg_def_items[id]) | ||||
| #define cfg_def_get_default_unconfigured_value_hint(cmd,item) (((item)->flags & CFG_DEFAULT_RUN_TIME) ? (item)->default_unconfigured_value.fn_UNCONFIGURED(cmd) : (item)->default_unconfigured_value.v_UNCONFIGURED) | ||||
| #define cfg_def_get_default_value_hint(cmd,item,type,profile) (((item)->flags & CFG_DEFAULT_RUN_TIME) ? (item)->default_value.fn_##type(cmd,profile) : (item)->default_value.v_##type) | ||||
| #define cfg_def_get_default_value(cmd,item,type,profile) ((item)->flags & CFG_DEFAULT_UNDEFINED ? 0 : cfg_def_get_default_value_hint(cmd,item,type,profile)) | ||||
| #define cfg_def_get_default_unconfigured_value_hint(cmd,item) ((item->flags & CFG_DEFAULT_RUN_TIME) ? item->default_unconfigured_value.fn_UNCONFIGURED(cmd) : item->default_unconfigured_value.v_UNCONFIGURED) | ||||
| #define cfg_def_get_default_value_hint(cmd,item,type,profile) ((item->flags & CFG_DEFAULT_RUN_TIME) ? item->default_value.fn_##type(cmd,profile) : item->default_value.v_##type) | ||||
| #define cfg_def_get_default_value(cmd,item,type,profile) (item->flags & CFG_DEFAULT_UNDEFINED ? 0 : cfg_def_get_default_value_hint(cmd,item,type,profile)) | ||||
|  | ||||
| static int _cfg_def_make_path(char *buf, size_t buf_size, int id, cfg_def_item_t *item, int xlate) | ||||
| { | ||||
| @@ -743,16 +742,14 @@ static struct dm_config_value *_get_def_array_values(struct cmd_context *cmd, | ||||
| 		switch (toupper(token[0])) { | ||||
| 			case 'I': | ||||
| 			case 'B': | ||||
| 				errno = 0; | ||||
| 				v->v.i = strtoll(token + 1, &r, 10); | ||||
| 				if (errno || *r) | ||||
| 				if (*r) | ||||
| 					goto bad; | ||||
| 				v->type = DM_CFG_INT; | ||||
| 				break; | ||||
| 			case 'F': | ||||
| 				errno = 0; | ||||
| 				v->v.f = strtod(token + 1, &r); | ||||
| 				if (errno || *r) | ||||
| 				if (*r) | ||||
| 					goto bad; | ||||
| 				v->type = DM_CFG_FLOAT; | ||||
| 				break; | ||||
| @@ -1880,12 +1877,6 @@ int config_write(struct dm_config_tree *cft, | ||||
| 	} | ||||
|  | ||||
| 	log_verbose("Dumping configuration to %s", file); | ||||
|  | ||||
| 	if (tree_spec->withgeneralpreamble) | ||||
| 		fprintf(baton.fp, CFG_PREAMBLE_GENERAL); | ||||
| 	if (tree_spec->withlocalpreamble) | ||||
| 		fprintf(baton.fp, CFG_PREAMBLE_LOCAL); | ||||
|  | ||||
| 	if (!argc) { | ||||
| 		if (!dm_config_write_node_out(cft->root, &_out_spec, &baton)) { | ||||
| 			log_error("Failure while writing to %s", file); | ||||
| @@ -2451,13 +2442,21 @@ const char *get_default_activation_mirror_image_fault_policy_CFG(struct cmd_cont | ||||
|  | ||||
| int get_default_allocation_thin_pool_chunk_size_CFG(struct cmd_context *cmd, struct profile *profile) | ||||
| { | ||||
| 	const char *str; | ||||
| 	uint32_t chunk_size; | ||||
| 	int chunk_size_calc_method; | ||||
|  | ||||
| 	if (!get_default_allocation_thin_pool_chunk_size(cmd, profile, &chunk_size, | ||||
| 							 &chunk_size_calc_method)) { | ||||
| 		stack; /* Ignore this error, never happens... */ | ||||
| 	if (!(str = find_config_tree_str(cmd, allocation_thin_pool_chunk_size_policy_CFG, profile))) { | ||||
| 		log_error(INTERNAL_ERROR "Cannot find configuration."); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (!strcasecmp(str, "generic")) | ||||
| 		chunk_size = DEFAULT_THIN_POOL_CHUNK_SIZE * 2; | ||||
| 	else if (!strcasecmp(str, "performance")) | ||||
| 		chunk_size = DEFAULT_THIN_POOL_CHUNK_SIZE_PERFORMANCE * 2; | ||||
| 	else { | ||||
| 		log_error("Thin pool chunk size calculation policy \"%s\" is unrecognised.", str); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	return (int) chunk_size; | ||||
|   | ||||
| @@ -17,12 +17,12 @@ | ||||
| #define _LVM_CONFIG_H | ||||
|  | ||||
| #include "libdevmapper.h" | ||||
| #include "device.h" | ||||
|  | ||||
| /* 16 bits: 3 bits for major, 4 bits for minor, 9 bits for patchlevel */ | ||||
| /* FIXME Max LVM version supported: 7.15.511. Extend bits when needed. */ | ||||
| #define vsn(major, minor, patchlevel) (major << 13 | minor << 9 | patchlevel) | ||||
|  | ||||
| struct device; | ||||
| struct cmd_context; | ||||
|  | ||||
| typedef enum { | ||||
| @@ -141,7 +141,6 @@ typedef struct cfg_def_item { | ||||
| 	uint16_t deprecated_since_version;				/* version since this item is deprecated */ | ||||
| 	const char *deprecation_comment;				/* comment about reasons for deprecation and settings that supersede this one */ | ||||
| 	const char *comment;						/* comment */ | ||||
| 	const char *file_premable;					/* comment text to use at the start of the file */ | ||||
| } cfg_def_item_t; | ||||
|  | ||||
| /* configuration definition tree types */ | ||||
| @@ -174,8 +173,6 @@ struct config_def_tree_spec { | ||||
| 	unsigned withversions:1;		/* include versions */ | ||||
| 	unsigned withspaces:1;			/* add more spaces in output for better readability */ | ||||
| 	unsigned unconfigured:1;		/* use unconfigured path strings */ | ||||
| 	unsigned withgeneralpreamble:1;		/* include preamble for a general config file */ | ||||
| 	unsigned withlocalpreamble:1;		/* include preamble for a local config file */ | ||||
| 	uint8_t *check_status;			/* status of last tree check (currently needed for CFG_DEF_TREE_MISSING only) */ | ||||
| }; | ||||
|  | ||||
| @@ -239,7 +236,7 @@ config_source_t config_get_source_type(struct dm_config_tree *cft); | ||||
| typedef uint32_t (*checksum_fn_t) (uint32_t initial, const uint8_t *buf, uint32_t size); | ||||
|  | ||||
| struct dm_config_tree *config_open(config_source_t source, const char *filename, int keep_open); | ||||
| int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, dev_io_reason_t reason, | ||||
| int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, | ||||
| 			off_t offset, size_t size, off_t offset2, size_t size2, | ||||
| 			checksum_fn_t checksum_fn, uint32_t checksum, | ||||
| 			int skip_parse, int no_dup_node_check); | ||||
|   | ||||
| @@ -121,30 +121,6 @@ | ||||
|  | ||||
| cfg_section(root_CFG_SECTION, "(root)", root_CFG_SECTION, 0, vsn(0, 0, 0), 0, NULL, NULL) | ||||
|  | ||||
| #define CFG_PREAMBLE_GENERAL \ | ||||
| 	"# This is an example configuration file for the LVM2 system.\n" \ | ||||
| 	"# It contains the default settings that would be used if there was no\n" \ | ||||
| 	"# @DEFAULT_SYS_DIR@/lvm.conf file.\n" \ | ||||
| 	"#\n" \ | ||||
| 	"# Refer to 'man lvm.conf' for further information including the file layout.\n" \ | ||||
| 	"#\n" \ | ||||
| 	"# Refer to 'man lvm.conf' for information about how settings configured in\n" \ | ||||
| 	"# this file are combined with built-in values and command line options to\n" \ | ||||
| 	"# arrive at the final values used by LVM.\n" \ | ||||
| 	"#\n" \ | ||||
| 	"# Refer to 'man lvmconfig' for information about displaying the built-in\n" \ | ||||
| 	"# and configured values used by LVM.\n" \ | ||||
| 	"#\n" \ | ||||
| 	"# If a default value is set in this file (not commented out), then a\n" \ | ||||
| 	"# new version of LVM using this file will continue using that value,\n" \ | ||||
| 	"# even if the new version of LVM changes the built-in default value.\n" \ | ||||
| 	"#\n" \ | ||||
| 	"# To put this file in a different directory and override @DEFAULT_SYS_DIR@ set\n" \ | ||||
| 	"# the environment variable LVM_SYSTEM_DIR before running the tools.\n" \ | ||||
| 	"#\n" \ | ||||
| 	"# N.B. Take care that each setting only appears once if uncommenting\n" \ | ||||
| 	"# example settings in this file.\n\n" | ||||
|  | ||||
| cfg_section(config_CFG_SECTION, "config", root_CFG_SECTION, 0, vsn(2, 2, 99), 0, NULL, | ||||
| 	"How LVM configuration settings are handled.\n") | ||||
|  | ||||
| @@ -185,26 +161,6 @@ cfg_section(tags_CFG_SECTION, "tags", root_CFG_SECTION, CFG_DEFAULT_COMMENTED, v | ||||
| cfg_section(local_CFG_SECTION, "local", root_CFG_SECTION, 0, vsn(2, 2, 117), 0, NULL, | ||||
| 	"LVM settings that are specific to the local host.\n") | ||||
|  | ||||
| #define CFG_PREAMBLE_LOCAL \ | ||||
| 	"# This is a local configuration file template for the LVM2 system\n" \ | ||||
| 	"# which should be installed as @DEFAULT_SYS_DIR@/lvmlocal.conf .\n" \ | ||||
| 	"#\n" \ | ||||
| 	"# Refer to 'man lvm.conf' for information about the file layout.\n" \ | ||||
| 	"#\n" \ | ||||
| 	"# To put this file in a different directory and override\n" \ | ||||
| 	"# @DEFAULT_SYS_DIR@ set the environment variable LVM_SYSTEM_DIR before\n" \ | ||||
| 	"# running the tools.\n" \ | ||||
| 	"#\n" \ | ||||
| 	"# The lvmlocal.conf file is normally expected to contain only the\n" \ | ||||
| 	"# \"local\" section which contains settings that should not be shared or\n" \ | ||||
| 	"# repeated among different hosts.  (But if other sections are present,\n" \ | ||||
| 	"# they *will* get processed.  Settings in this file override equivalent\n" \ | ||||
| 	"# ones in lvm.conf and are in turn overridden by ones in any enabled\n" \ | ||||
| 	"# lvm_<tag>.conf files.)\n" \ | ||||
| 	"#\n" \ | ||||
| 	"# Please take care that each setting only appears once if uncommenting\n" \ | ||||
| 	"# example settings in this file and never copy this file between hosts.\n\n" | ||||
|  | ||||
| cfg(config_checks_CFG, "checks", config_CFG_SECTION, 0, CFG_TYPE_BOOL, 1, vsn(2, 2, 99), NULL, 0, NULL, | ||||
| 	"If enabled, any LVM configuration mismatch is reported.\n" | ||||
| 	"This implies checking that the configuration key is understood by\n" | ||||
| @@ -513,28 +469,18 @@ cfg(allocation_mirror_logs_require_separate_pvs_CFG, "mirror_logs_require_separa | ||||
|  | ||||
| cfg(allocation_raid_stripe_all_devices_CFG, "raid_stripe_all_devices", allocation_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_ALLOCATION_STRIPE_ALL_DEVICES, vsn(2, 2, 162), NULL, 0, NULL, | ||||
| 	"Stripe across all PVs when RAID stripes are not specified.\n" | ||||
| 	"If enabled, all PVs in the VG or on the command line are used for\n" | ||||
| 	"raid0/4/5/6/10 when the command does not specify the number of\n" | ||||
| 	"stripes to use.\n" | ||||
| 	"If enabled, all PVs in the VG or on the command line are used for raid0/4/5/6/10\n" | ||||
| 	"when the command does not specify the number of stripes to use.\n" | ||||
| 	"This was the default behaviour until release 2.02.162.\n") | ||||
|  | ||||
| cfg(allocation_cache_pool_metadata_require_separate_pvs_CFG, "cache_pool_metadata_require_separate_pvs", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA, CFG_TYPE_BOOL, DEFAULT_CACHE_POOL_METADATA_REQUIRE_SEPARATE_PVS, vsn(2, 2, 106), NULL, 0, NULL, | ||||
| cfg(allocation_cache_pool_metadata_require_separate_pvs_CFG, "cache_pool_metadata_require_separate_pvs", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_CACHE_POOL_METADATA_REQUIRE_SEPARATE_PVS, vsn(2, 2, 106), NULL, 0, NULL, | ||||
| 	"Cache pool metadata and data will always use different PVs.\n") | ||||
|  | ||||
| cfg(allocation_cache_pool_cachemode_CFG, "cache_pool_cachemode", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_CACHE_MODE, vsn(2, 2, 113), NULL, vsn(2, 2, 128), | ||||
| cfg(allocation_cache_pool_cachemode_CFG, "cache_pool_cachemode", allocation_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_CACHE_MODE, vsn(2, 2, 113), NULL, vsn(2, 2, 128), | ||||
| 	"This has been replaced by the allocation/cache_mode setting.\n", | ||||
| 	"Cache mode.\n") | ||||
|  | ||||
| cfg(allocation_cache_metadata_format_CFG, "cache_metadata_format", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA | CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_CACHE_METADATA_FORMAT, vsn(2, 2, 169), NULL, 0, NULL, | ||||
| 	"Sets default metadata format for new cache.\n" | ||||
| 	"#\n" | ||||
| 	"Accepted values:\n" | ||||
| 	"  0  Automatically detected best available format\n" | ||||
| 	"  1  Original format\n" | ||||
| 	"  2  Improved 2nd. generation format\n" | ||||
| 	"#\n") | ||||
|  | ||||
| cfg(allocation_cache_mode_CFG, "cache_mode", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_CACHE_MODE, vsn(2, 2, 128), NULL, 0, NULL, | ||||
| cfg(allocation_cache_mode_CFG, "cache_mode", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_CACHE_MODE, vsn(2, 2, 128), NULL, 0, NULL, | ||||
| 	"The default cache mode used for new cache.\n" | ||||
| 	"#\n" | ||||
| 	"Accepted values:\n" | ||||
| @@ -546,20 +492,20 @@ cfg(allocation_cache_mode_CFG, "cache_mode", allocation_CFG_SECTION, CFG_PROFILA | ||||
| 	"#\n" | ||||
| 	"This setting replaces allocation/cache_pool_cachemode.\n") | ||||
|  | ||||
| cfg(allocation_cache_policy_CFG, "cache_policy", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, 0, vsn(2, 2, 128), NULL, 0, NULL, | ||||
| cfg(allocation_cache_policy_CFG, "cache_policy", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, 0, vsn(2, 2, 128), NULL, 0, NULL, | ||||
| 	"The default cache policy used for new cache volume.\n" | ||||
| 	"Since kernel 4.2 the default policy is smq (Stochastic multiqueue),\n" | ||||
| 	"Since kernel 4.2 the default policy is smq (Stochastic multique),\n" | ||||
| 	"otherwise the older mq (Multiqueue) policy is selected.\n") | ||||
|  | ||||
| cfg_section(allocation_cache_settings_CFG_SECTION, "cache_settings", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA | CFG_DEFAULT_COMMENTED, vsn(2, 2, 128), 0, NULL, | ||||
| cfg_section(allocation_cache_settings_CFG_SECTION, "cache_settings", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, vsn(2, 2, 128), 0, NULL, | ||||
| 	"Settings for the cache policy.\n" | ||||
| 	"See documentation for individual cache policies for more info.\n") | ||||
|  | ||||
| cfg_section(policy_settings_CFG_SUBSECTION, "policy_settings", allocation_cache_settings_CFG_SECTION, CFG_NAME_VARIABLE | CFG_SECTION_NO_CHECK | CFG_PROFILABLE | CFG_PROFILABLE_METADATA | CFG_DEFAULT_COMMENTED, vsn(2, 2, 128), 0, NULL, | ||||
| cfg_section(policy_settings_CFG_SUBSECTION, "policy_settings", allocation_cache_settings_CFG_SECTION, CFG_NAME_VARIABLE | CFG_SECTION_NO_CHECK | CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, vsn(2, 2, 128), 0, NULL, | ||||
| 	"Replace this subsection name with a policy name.\n" | ||||
| 	"Multiple subsections for different policies can be created.\n") | ||||
|  | ||||
| cfg_runtime(allocation_cache_pool_chunk_size_CFG, "cache_pool_chunk_size", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA | CFG_DEFAULT_UNDEFINED, CFG_TYPE_INT, vsn(2, 2, 106), 0, NULL, | ||||
| cfg_runtime(allocation_cache_pool_chunk_size_CFG, "cache_pool_chunk_size", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_UNDEFINED, CFG_TYPE_INT, vsn(2, 2, 106), 0, NULL, | ||||
| 	"The minimal chunk size in KiB for cache pool volumes.\n" | ||||
| 	"Using a chunk_size that is too large can result in wasteful use of\n" | ||||
| 	"the cache, where small reads and writes can cause large sections of\n" | ||||
| @@ -570,7 +516,7 @@ cfg_runtime(allocation_cache_pool_chunk_size_CFG, "cache_pool_chunk_size", alloc | ||||
| 	"on the smaller end of the spectrum. Supported values range from\n" | ||||
| 	"32KiB to 1GiB in multiples of 32.\n") | ||||
|  | ||||
| cfg(allocation_cache_pool_max_chunks_CFG, "cache_pool_max_chunks", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_PROFILABLE_METADATA | CFG_DEFAULT_UNDEFINED, CFG_TYPE_INT, 0, vsn(2, 2, 165), NULL, 0, NULL, | ||||
| cfg(allocation_cache_pool_max_chunks_CFG, "cache_pool_max_chunks", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_UNDEFINED, CFG_TYPE_INT, 0, vsn(2, 2, 165), NULL, 0, NULL, | ||||
| 	"The maximum number of chunks in a cache pool.\n" | ||||
| 	"For cache target v1.9 the recommended maximumm is 1000000 chunks.\n" | ||||
| 	"Using cache pool with more chunks may degrade cache performance.\n") | ||||
| @@ -705,11 +651,11 @@ cfg(log_activation_CFG, "activation", log_CFG_SECTION, 0, CFG_TYPE_BOOL, 0, vsn( | ||||
|  | ||||
| cfg(log_activate_file_CFG, "activate_file", log_CFG_SECTION, CFG_DEFAULT_UNDEFINED | CFG_UNSUPPORTED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL, 0, NULL, NULL) | ||||
|  | ||||
| cfg_array(log_debug_classes_CFG, "debug_classes", log_CFG_SECTION, CFG_ALLOW_EMPTY, CFG_TYPE_STRING, "#Smemory#Sdevices#Sio#Sactivation#Sallocation#Slvmetad#Smetadata#Scache#Slocking#Slvmpolld#Sdbus", vsn(2, 2, 99), NULL, 0, NULL, | ||||
| cfg_array(log_debug_classes_CFG, "debug_classes", log_CFG_SECTION, CFG_ALLOW_EMPTY, CFG_TYPE_STRING, "#Smemory#Sdevices#Sactivation#Sallocation#Slvmetad#Smetadata#Scache#Slocking#Slvmpolld#Sdbus", vsn(2, 2, 99), NULL, 0, NULL, | ||||
| 	"Select log messages by class.\n" | ||||
| 	"Some debugging messages are assigned to a class and only appear in\n" | ||||
| 	"debug output if the class is listed here. Classes currently\n" | ||||
| 	"available: memory, devices, io, activation, allocation, lvmetad,\n" | ||||
| 	"available: memory, devices, activation, allocation, lvmetad,\n" | ||||
| 	"metadata, cache, locking, lvmpolld. Use \"all\" to see everything.\n") | ||||
|  | ||||
| cfg(backup_backup_CFG, "backup", backup_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_BACKUP_ENABLED, vsn(1, 0, 0), NULL, 0, NULL, | ||||
| @@ -979,7 +925,7 @@ cfg(global_use_lvmetad_CFG, "use_lvmetad", global_CFG_SECTION, 0, CFG_TYPE_BOOL, | ||||
| 	"devices/global_filter.\n") | ||||
|  | ||||
| cfg(global_lvmetad_update_wait_time_CFG, "lvmetad_update_wait_time", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_LVMETAD_UPDATE_WAIT_TIME, vsn(2, 2, 151), NULL, 0, NULL, | ||||
| 	"Number of seconds a command will wait for lvmetad update to finish.\n" | ||||
| 	"The number of seconds a command will wait for lvmetad update to finish.\n" | ||||
| 	"After waiting for this period, a command will not use lvmetad, and\n" | ||||
| 	"will revert to disk scanning.\n") | ||||
|  | ||||
| @@ -1046,7 +992,7 @@ cfg_array(global_thin_disabled_features_CFG, "thin_disabled_features", global_CF | ||||
| cfg_array(global_cache_disabled_features_CFG, "cache_disabled_features", global_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 128), NULL, 0, NULL, | ||||
| 	"Features to not use in the cache driver.\n" | ||||
| 	"This can be helpful for testing, or to avoid using a feature that is\n" | ||||
| 	"causing problems. Features include: policy_mq, policy_smq, metadata2.\n" | ||||
| 	"causing problems. Features include: policy_mq, policy_smq.\n" | ||||
| 	"#\n" | ||||
| 	"Example\n" | ||||
| 	"cache_disabled_features = [ \"policy_smq\" ]\n" | ||||
| @@ -1080,10 +1026,6 @@ cfg_array(global_cache_check_options_CFG, "cache_check_options", global_CFG_SECT | ||||
| cfg_array(global_cache_repair_options_CFG, "cache_repair_options", global_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_CACHE_REPAIR_OPTIONS_CONFIG, vsn(2, 2, 108), NULL, 0, NULL, | ||||
| 	"List of options passed to the cache_repair command.\n") | ||||
|  | ||||
| cfg(global_fsadm_executable_CFG, "fsadm_executable", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_FSADM_PATH, vsn(2, 2, 170), "@FSADM_PATH@", 0, NULL, | ||||
| 	"The full path to the fsadm command.\n" | ||||
| 	"LVM uses this command to help with lvresize -r operations.\n") | ||||
|  | ||||
| cfg(global_system_id_source_CFG, "system_id_source", global_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_SYSTEM_ID_SOURCE, vsn(2, 2, 117), NULL, 0, NULL, | ||||
| 	"The method LVM uses to set the local system ID.\n" | ||||
| 	"Volume Groups can also be given a system ID (by vgcreate, vgchange,\n" | ||||
| @@ -1279,15 +1221,14 @@ cfg_array(activation_read_only_volume_list_CFG, "read_only_volume_list", activat | ||||
| 	"read_only_volume_list = [ \"vg1\", \"vg2/lvol1\", \"@tag1\", \"@*\" ]\n" | ||||
| 	"#\n") | ||||
|  | ||||
|  cfg(activation_mirror_region_size_CFG, "mirror_region_size", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_RAID_REGION_SIZE, vsn(1, 0, 0), NULL, vsn(2, 2, 99), | ||||
| cfg(activation_mirror_region_size_CFG, "mirror_region_size", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_RAID_REGION_SIZE, vsn(1, 0, 0), NULL, vsn(2, 2, 99), | ||||
| 	"This has been replaced by the activation/raid_region_size setting.\n", | ||||
| 	"Size in KiB of each raid or mirror synchronization region.\n") | ||||
|         "Size in KiB of each copy operation when mirroring.\n") | ||||
|  | ||||
| cfg(activation_raid_region_size_CFG, "raid_region_size", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_RAID_REGION_SIZE, vsn(2, 2, 99), NULL, 0, NULL, | ||||
| 	"Size in KiB of each raid or mirror synchronization region.\n" | ||||
| 	"The clean/dirty state of data is tracked for each region.\n" | ||||
| 	"The value is rounded down to a power of two if necessary, and\n" | ||||
| 	"is ignored if it is not a multiple of the machine memory page size.\n") | ||||
| 	"For raid or mirror segment types, this is the amount of data that is\n" | ||||
| 	"copied at once when initializing, or moved at once by pvmove.\n") | ||||
|  | ||||
| cfg(activation_error_when_full_CFG, "error_when_full", activation_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_ERROR_WHEN_FULL, vsn(2, 2, 115), NULL, 0, NULL, | ||||
| 	"Return errors if a thin pool runs out of space.\n" | ||||
| @@ -1918,14 +1859,6 @@ cfg(dmeventd_thin_library_CFG, "thin_library", dmeventd_CFG_SECTION, 0, CFG_TYPE | ||||
| 	"and emits a warning through syslog when the usage exceeds 80%. The\n" | ||||
| 	"warning is repeated when 85%, 90% and 95% of the pool is filled.\n") | ||||
|  | ||||
| cfg(dmeventd_thin_command_CFG, "thin_command", dmeventd_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_DMEVENTD_THIN_COMMAND, vsn(2, 2, 169), NULL, 0, NULL, | ||||
| 	"The plugin runs command with each 5% increment when thin-pool data volume\n" | ||||
| 	"or metadata volume gets above 50%.\n" | ||||
| 	"Command which starts with 'lvm ' prefix is internal lvm command.\n" | ||||
| 	"You can write your own handler to customise behaviour in more details.\n" | ||||
| 	"User handler is specified with the full path starting with '/'.\n") | ||||
| 	/* TODO: systemd service handler */ | ||||
|  | ||||
| cfg(dmeventd_executable_CFG, "executable", dmeventd_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_DMEVENTD_PATH, vsn(2, 2, 73), "@DMEVENTD_PATH@", 0, NULL, | ||||
| 	"The full path to the dmeventd binary.\n") | ||||
|  | ||||
|   | ||||
| @@ -71,7 +71,7 @@ | ||||
|  * FIXME: Increase these to 64 and further to the MD maximum | ||||
|  *	  once the SubLVs split and name shift got enhanced | ||||
|  */ | ||||
| #define DEFAULT_RAID1_MAX_IMAGES 64 | ||||
| #define DEFAULT_RAID1_MAX_IMAGES 10 | ||||
| #define DEFAULT_RAID_MAX_IMAGES 64 | ||||
| #define DEFAULT_ALLOCATION_STRIPE_ALL_DEVICES 0 /* Don't stripe across all devices if not -i/--stripes given */ | ||||
|  | ||||
| @@ -81,7 +81,6 @@ | ||||
| #define DEFAULT_DMEVENTD_MIRROR_LIB "libdevmapper-event-lvm2mirror.so" | ||||
| #define DEFAULT_DMEVENTD_SNAPSHOT_LIB "libdevmapper-event-lvm2snapshot.so" | ||||
| #define DEFAULT_DMEVENTD_THIN_LIB "libdevmapper-event-lvm2thin.so" | ||||
| #define DEFAULT_DMEVENTD_THIN_COMMAND "lvm lvextend --use-policies" | ||||
| #define DEFAULT_DMEVENTD_MONITOR 1 | ||||
| #define DEFAULT_BACKGROUND_POLLING 1 | ||||
|  | ||||
| @@ -104,9 +103,9 @@ | ||||
| #define DEFAULT_THIN_REPAIR_OPTION1 "" | ||||
| #define DEFAULT_THIN_REPAIR_OPTIONS_CONFIG "#S" DEFAULT_THIN_REPAIR_OPTION1 | ||||
| #define DEFAULT_THIN_POOL_METADATA_REQUIRE_SEPARATE_PVS 0 | ||||
| #define DEFAULT_THIN_POOL_MAX_METADATA_SIZE (DM_THIN_MAX_METADATA_SIZE / 2)  /* KB */ | ||||
| #define DEFAULT_THIN_POOL_MAX_METADATA_SIZE (16 * 1024 * 1024)  /* KB */ | ||||
| #define DEFAULT_THIN_POOL_MIN_METADATA_SIZE 2048  /* KB */ | ||||
| #define DEFAULT_THIN_POOL_OPTIMAL_METADATA_SIZE (128 * 1024) /* KB */ | ||||
| #define DEFAULT_THIN_POOL_OPTIMAL_SIZE     (128 * 1024 * 1024)	/* KB */ | ||||
| #define DEFAULT_THIN_POOL_CHUNK_SIZE_POLICY "generic" | ||||
| #define DEFAULT_THIN_POOL_CHUNK_SIZE	    64	  /* KB */ | ||||
| #define DEFAULT_THIN_POOL_CHUNK_SIZE_PERFORMANCE 512 /* KB */ | ||||
| @@ -132,11 +131,8 @@ | ||||
| #define DEFAULT_CACHE_POOL_MIN_METADATA_SIZE 2048  /* KB */ | ||||
| #define DEFAULT_CACHE_POOL_MAX_METADATA_SIZE (16 * 1024 * 1024)  /* KB */ | ||||
| #define DEFAULT_CACHE_POLICY "mq" | ||||
| #define DEFAULT_CACHE_METADATA_FORMAT CACHE_METADATA_FORMAT_UNSELECTED /* Autodetect */ | ||||
| #define DEFAULT_CACHE_MODE "writethrough" | ||||
|  | ||||
| #define DEFAULT_FSADM_PATH FSADM_PATH | ||||
|  | ||||
| #define DEFAULT_UMASK 0077 | ||||
|  | ||||
| #define DEFAULT_FORMAT "lvm2" | ||||
| @@ -180,7 +176,7 @@ | ||||
| #define DEFAULT_INDENT 1 | ||||
| #define DEFAULT_ABORT_ON_INTERNAL_ERRORS 0 | ||||
| #define DEFAULT_DETECT_INTERNAL_VG_CACHE_CORRUPTION 0 | ||||
| #define DEFAULT_UNITS "r" | ||||
| #define DEFAULT_UNITS "h" | ||||
| #define DEFAULT_SUFFIX 1 | ||||
| #define DEFAULT_HOSTTAGS 0 | ||||
|  | ||||
| @@ -202,7 +198,7 @@ | ||||
| #define DEFAULT_ACTIVATION_MODE "degraded" | ||||
| #define DEFAULT_USE_LINEAR_TARGET 1 | ||||
| #define DEFAULT_STRIPE_FILLER "error" | ||||
| #define DEFAULT_RAID_REGION_SIZE   2048	/* KB */ | ||||
| #define DEFAULT_RAID_REGION_SIZE   512	/* KB */ | ||||
| #define DEFAULT_INTERVAL 15 | ||||
|  | ||||
| #define DEFAULT_MAX_HISTORY 100 | ||||
|   | ||||
| @@ -320,8 +320,8 @@ static int _compare_paths(const char *path0, const char *path1) | ||||
| 	/* ASCII comparison */ | ||||
| 	if (strcmp(path0, path1) < 0) | ||||
| 		return 0; | ||||
|  | ||||
| 	return 1; | ||||
| 	else | ||||
| 		return 1; | ||||
| } | ||||
|  | ||||
| static int _add_alias(struct device *dev, const char *path) | ||||
| @@ -706,12 +706,6 @@ static int _insert_dev(const char *path, dev_t d) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (dm_hash_lookup(_cache.names, path) == dev) { | ||||
| 		/* Hash already has matching entry present */ | ||||
| 		log_debug("%s: Path already cached.", path); | ||||
| 		return 1; | ||||
| 	} | ||||
|  | ||||
| 	if (!(path_copy = dm_pool_strdup(_cache.mem, path))) { | ||||
| 		log_error("Failed to duplicate path string."); | ||||
| 		return 0; | ||||
| @@ -898,10 +892,10 @@ int dev_cache_index_devs(void) | ||||
| 			if (errno == ENOENT) { | ||||
| 				sysfs_has_dev_block = 0; | ||||
| 				return 1; | ||||
| 			} else { | ||||
| 				log_sys_error("stat", path); | ||||
| 				return 0; | ||||
| 			} | ||||
|  | ||||
| 			log_sys_error("stat", path); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} else if (!sysfs_has_dev_block) | ||||
| 		return 1; | ||||
| @@ -939,20 +933,12 @@ static int _insert_udev_dir(struct udev *udev, const char *dir) | ||||
| 	struct udev_device *device; | ||||
| 	int r = 1; | ||||
|  | ||||
| 	if (!(udev_enum = udev_enumerate_new(udev))) { | ||||
| 		log_error("Failed to udev_enumerate_new."); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	if (!(udev_enum = udev_enumerate_new(udev))) | ||||
| 		goto bad; | ||||
|  | ||||
| 	if (udev_enumerate_add_match_subsystem(udev_enum, "block")) { | ||||
| 		log_error("Failed to udev_enumerate_add_match_subsystem."); | ||||
| 		goto out; | ||||
| 	} | ||||
|  | ||||
| 	if (udev_enumerate_scan_devices(udev_enum)) { | ||||
| 		log_error("Failed to udev_enumerate_scan_devices."); | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (udev_enumerate_add_match_subsystem(udev_enum, "block") || | ||||
| 	    udev_enumerate_scan_devices(udev_enum)) | ||||
| 		goto bad; | ||||
|  | ||||
| 	/* | ||||
| 	 * Report any missing information as "log_very_verbose" only, do not | ||||
| @@ -989,10 +975,13 @@ static int _insert_udev_dir(struct udev *udev, const char *dir) | ||||
| 		udev_device_unref(device); | ||||
| 	} | ||||
|  | ||||
| out: | ||||
| 	udev_enumerate_unref(udev_enum); | ||||
|  | ||||
| 	return r; | ||||
|  | ||||
| bad: | ||||
| 	log_error("Failed to enumerate udev device list."); | ||||
| 	udev_enumerate_unref(udev_enum); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void _insert_dirs(struct dm_list *dirs) | ||||
| @@ -1375,19 +1364,6 @@ const char *dev_name_confirmed(struct device *dev, int quiet) | ||||
| 	return dev_name(dev); | ||||
| } | ||||
|  | ||||
| /* Provide a custom reason when a device is ignored */ | ||||
| const char *dev_cache_filtered_reason(const char *name) | ||||
| { | ||||
| 	const char *reason = "not found"; | ||||
| 	struct device *d = (struct device *) dm_hash_lookup(_cache.names, name); | ||||
|  | ||||
| 	if (d) | ||||
| 		/* FIXME Record which filter caused the exclusion */ | ||||
| 		reason = "excluded by a filter"; | ||||
|  | ||||
| 	return reason; | ||||
| } | ||||
|  | ||||
| struct device *dev_cache_get(const char *name, struct dev_filter *f) | ||||
| { | ||||
| 	struct stat buf; | ||||
| @@ -1423,7 +1399,7 @@ struct device *dev_cache_get(const char *name, struct dev_filter *f) | ||||
| 	if (!d || (f && !(d->flags & DEV_REGULAR) && !(f->passes_filter(f, d)))) | ||||
| 		return NULL; | ||||
|  | ||||
| 	log_debug_devs("%s: Using device (%d:%d)", dev_name(d), (int) MAJOR(d->dev), (int) MINOR(d->dev)); | ||||
| 	log_debug_devs("Using %s", dev_name(d)); | ||||
| 	return d; | ||||
| } | ||||
|  | ||||
| @@ -1533,7 +1509,7 @@ struct device *dev_iter_get(struct dev_iter *iter) | ||||
| 		struct device *d = _iter_next(iter); | ||||
| 		if (!iter->filter || (d->flags & DEV_REGULAR) || | ||||
| 		    iter->filter->passes_filter(iter->filter, d)) { | ||||
| 			log_debug_devs("%s: Using device (%d:%d)", dev_name(d), (int) MAJOR(d->dev), (int) MINOR(d->dev)); | ||||
| 			log_debug_devs("Using %s", dev_name(d)); | ||||
| 			return d; | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -55,7 +55,6 @@ int dev_cache_add_dir(const char *path); | ||||
| int dev_cache_add_loopfile(const char *path); | ||||
| __attribute__((nonnull(1))) | ||||
| struct device *dev_cache_get(const char *name, struct dev_filter *f); | ||||
| const char *dev_cache_filtered_reason(const char *name); | ||||
|  | ||||
| // TODO | ||||
| struct device *dev_cache_get_by_devt(dev_t device, struct dev_filter *f); | ||||
|   | ||||
| @@ -100,6 +100,8 @@ const char *dev_ext_name(struct device *dev) | ||||
| 	return _ext_registry[dev->ext.src].name; | ||||
| } | ||||
|  | ||||
| static const char *_ext_attached_msg = "External handle attached to device"; | ||||
|  | ||||
| struct dev_ext *dev_ext_get(struct device *dev) | ||||
| { | ||||
| 	struct dev_ext *ext; | ||||
| @@ -108,10 +110,10 @@ struct dev_ext *dev_ext_get(struct device *dev) | ||||
| 	handle_ptr = dev->ext.handle; | ||||
|  | ||||
| 	if (!(ext = _ext_registry[dev->ext.src].dev_ext_get(dev))) | ||||
| 		log_error("%s: Failed to get external handle [%s].", | ||||
| 		log_error("Failed to get external handle for device %s [%s].", | ||||
| 			   dev_name(dev), dev_ext_name(dev)); | ||||
| 	else if (handle_ptr != dev->ext.handle) | ||||
| 		log_debug_devs("%s: External handle [%s:%p] attached", dev_name(dev), | ||||
| 		log_debug_devs("%s %s [%s:%p]", _ext_attached_msg, dev_name(dev), | ||||
| 				dev_ext_name(dev), dev->ext.handle); | ||||
|  | ||||
| 	return ext; | ||||
| @@ -129,10 +131,10 @@ int dev_ext_release(struct device *dev) | ||||
| 	handle_ptr = dev->ext.handle; | ||||
|  | ||||
| 	if (!(r = _ext_registry[dev->ext.src].dev_ext_release(dev))) | ||||
| 		log_error("%s: Failed to release external handle [%s:%p]", | ||||
| 		log_error("Failed to release external handle for device %s [%s:%p].", | ||||
| 			  dev_name(dev), dev_ext_name(dev), dev->ext.handle); | ||||
| 	else | ||||
| 		log_debug_devs("%s: External handle [%s:%p] detached", | ||||
| 		log_debug_devs("External handle detached from device %s [%s:%p]", | ||||
| 				dev_name(dev), dev_ext_name(dev), handle_ptr); | ||||
|  | ||||
| 	return r; | ||||
| @@ -141,7 +143,7 @@ int dev_ext_release(struct device *dev) | ||||
| int dev_ext_enable(struct device *dev, dev_ext_t src) | ||||
| { | ||||
| 	if (dev->ext.enabled && (dev->ext.src != src) && !dev_ext_release(dev)) { | ||||
| 		log_error("%s: Failed to enable external handle [%s].", | ||||
| 		log_error("Failed to enable external handle for device %s [%s].", | ||||
| 			   dev_name(dev), _ext_registry[src].name);  | ||||
| 		return 0; | ||||
| 	} | ||||
| @@ -158,7 +160,7 @@ int dev_ext_disable(struct device *dev) | ||||
| 		return 1; | ||||
|  | ||||
| 	if (!dev_ext_release(dev)) { | ||||
| 		log_error("%s: Failed to disable external handle [%s].", | ||||
| 		log_error("Failed to disable external handle for device %s [%s].", | ||||
| 			   dev_name(dev), dev_ext_name(dev)); | ||||
| 		return 0; | ||||
| 	} | ||||
|   | ||||
| @@ -56,29 +56,11 @@ | ||||
| static DM_LIST_INIT(_open_devices); | ||||
| static unsigned _dev_size_seqno = 1; | ||||
|  | ||||
| static const char *_reasons[] = { | ||||
| 	"dev signatures", | ||||
| 	"PV labels", | ||||
| 	"VG metadata header", | ||||
| 	"VG metadata content", | ||||
| 	"extra VG metadata header", | ||||
| 	"extra VG metadata content", | ||||
| 	"LVM1 metadata", | ||||
| 	"pool metadata", | ||||
| 	"LV content", | ||||
| 	"logging", | ||||
| }; | ||||
|  | ||||
| static const char *_reason_text(dev_io_reason_t reason) | ||||
| { | ||||
| 	return _reasons[(unsigned) reason]; | ||||
| } | ||||
|  | ||||
| /*----------------------------------------------------------------- | ||||
|  * The standard io loop that keeps submitting an io until it's | ||||
|  * all gone. | ||||
|  *---------------------------------------------------------------*/ | ||||
| static int _io(struct device_area *where, char *buffer, int should_write, dev_io_reason_t reason) | ||||
| static int _io(struct device_area *where, char *buffer, int should_write) | ||||
| { | ||||
| 	int fd = dev_fd(where->dev); | ||||
| 	ssize_t n = 0; | ||||
| @@ -90,11 +72,6 @@ static int _io(struct device_area *where, char *buffer, int should_write, dev_io | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	log_debug_io("%s %s:%8" PRIu64 " bytes (sync) at %" PRIu64 "%s (for %s)", | ||||
| 		     should_write ? "Write" : "Read ", dev_name(where->dev), | ||||
| 		     where->size, (uint64_t) where->start, | ||||
| 		     (should_write && test_mode()) ? " (test mode - suppressed)" : "", _reason_text(reason)); | ||||
|  | ||||
| 	/* | ||||
| 	 * Skip all writes in test mode. | ||||
| 	 */ | ||||
| @@ -165,7 +142,7 @@ int dev_get_block_size(struct device *dev, unsigned int *physical_block_size, un | ||||
| 			r = 0; | ||||
| 			goto out; | ||||
| 		} | ||||
| 		log_debug_devs("%s: Block size is %u bytes", name, dev->block_size); | ||||
| 		log_debug_devs("%s: block size is %u bytes", name, dev->block_size); | ||||
| 	} | ||||
|  | ||||
| #ifdef BLKPBSZGET | ||||
| @@ -176,7 +153,7 @@ int dev_get_block_size(struct device *dev, unsigned int *physical_block_size, un | ||||
| 			r = 0; | ||||
| 			goto out; | ||||
| 		} | ||||
| 		log_debug_devs("%s: Physical block size is %u bytes", name, dev->phys_block_size); | ||||
| 		log_debug_devs("%s: physical block size is %u bytes", name, dev->phys_block_size); | ||||
| 	} | ||||
| #elif defined (BLKSSZGET) | ||||
| 	/* if we can't get physical block size, just use logical block size instead */ | ||||
| @@ -186,13 +163,15 @@ int dev_get_block_size(struct device *dev, unsigned int *physical_block_size, un | ||||
| 			r = 0; | ||||
| 			goto out; | ||||
| 		} | ||||
| 		log_debug_devs("%s: Physical block size can't be determined: Using logical block size of %u bytes", name, dev->phys_block_size); | ||||
| 		log_debug_devs("%s: physical block size can't be determined, using logical " | ||||
| 			       "block size of %u bytes", name, dev->phys_block_size); | ||||
| 	} | ||||
| #else | ||||
| 	/* if even BLKSSZGET is not available, use default 512b */ | ||||
| 	if (dev->phys_block_size == -1) { | ||||
| 		dev->phys_block_size = 512; | ||||
| 		log_debug_devs("%s: Physical block size can't be determined: Using block size of %u bytes instead", name, dev->phys_block_size); | ||||
| 		log_debug_devs("%s: physical block size can't be determined, using block " | ||||
| 			       "size of %u bytes instead", name, dev->phys_block_size); | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| @@ -228,12 +207,11 @@ static void _widen_region(unsigned int block_size, struct device_area *region, | ||||
| } | ||||
|  | ||||
| static int _aligned_io(struct device_area *where, char *buffer, | ||||
| 		       int should_write, dev_io_reason_t reason) | ||||
| 		       int should_write) | ||||
| { | ||||
| 	char *bounce, *bounce_buf; | ||||
| 	unsigned int physical_block_size = 0; | ||||
| 	unsigned int block_size = 0; | ||||
| 	unsigned buffer_was_widened = 0; | ||||
| 	uintptr_t mask; | ||||
| 	struct device_area widened; | ||||
| 	int r = 0; | ||||
| @@ -244,18 +222,14 @@ static int _aligned_io(struct device_area *where, char *buffer, | ||||
|  | ||||
| 	if (!block_size) | ||||
| 		block_size = lvm_getpagesize(); | ||||
| 	mask = block_size - 1; | ||||
|  | ||||
| 	_widen_region(block_size, where, &widened); | ||||
|  | ||||
| 	/* Did we widen the buffer?  When writing, this means means read-modify-write. */ | ||||
| 	if (where->size != widened.size || where->start != widened.start) { | ||||
| 		buffer_was_widened = 1; | ||||
| 		log_debug_io("Widening request for %" PRIu64 " bytes at %" PRIu64 " to %" PRIu64 " bytes at %" PRIu64 " on %s (for %s)", | ||||
| 			     where->size, (uint64_t) where->start, widened.size, (uint64_t) widened.start, dev_name(where->dev), _reason_text(reason)); | ||||
| 	} else if (!((uintptr_t) buffer & mask)) | ||||
| 		/* Perform the I/O directly. */ | ||||
| 		return _io(where, buffer, should_write, reason); | ||||
| 	/* Do we need to use a bounce buffer? */ | ||||
| 	mask = block_size - 1; | ||||
| 	if (!memcmp(where, &widened, sizeof(widened)) && | ||||
| 	    !((uintptr_t) buffer & mask)) | ||||
| 		return _io(where, buffer, should_write); | ||||
|  | ||||
| 	/* Allocate a bounce buffer with an extra block */ | ||||
| 	if (!(bounce_buf = bounce = dm_malloc((size_t) widened.size + block_size))) { | ||||
| @@ -269,12 +243,10 @@ static int _aligned_io(struct device_area *where, char *buffer, | ||||
| 	if (((uintptr_t) bounce) & mask) | ||||
| 		bounce = (char *) ((((uintptr_t) bounce) + mask) & ~mask); | ||||
|  | ||||
| 	/* Do we need to read into the bounce buffer? */ | ||||
| 	if ((!should_write || buffer_was_widened) && | ||||
| 	    !_io(&widened, bounce, 0, reason)) { | ||||
| 	/* channel the io through the bounce buffer */ | ||||
| 	if (!_io(&widened, bounce, 0)) { | ||||
| 		if (!should_write) | ||||
| 			goto_out; | ||||
| 		/* FIXME Handle errors properly! */ | ||||
| 		/* FIXME pre-extend the file */ | ||||
| 		memset(bounce, '\n', widened.size); | ||||
| 	} | ||||
| @@ -284,7 +256,7 @@ static int _aligned_io(struct device_area *where, char *buffer, | ||||
| 		       (size_t) where->size); | ||||
|  | ||||
| 		/* ... then we write */ | ||||
| 		if (!(r = _io(&widened, bounce, 1, reason))) | ||||
| 		if (!(r = _io(&widened, bounce, 1))) | ||||
| 			stack; | ||||
| 			 | ||||
| 		goto out; | ||||
| @@ -433,8 +405,8 @@ int dev_get_size(struct device *dev, uint64_t *size) | ||||
|  | ||||
| 	if ((dev->flags & DEV_REGULAR)) | ||||
| 		return _dev_get_size_file(dev, size); | ||||
|  | ||||
| 	return _dev_get_size_dev(dev, size); | ||||
| 	else | ||||
| 		return _dev_get_size_dev(dev, size); | ||||
| } | ||||
|  | ||||
| int dev_get_read_ahead(struct device *dev, uint32_t *read_ahead) | ||||
| @@ -491,12 +463,11 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet) | ||||
| 			return 1; | ||||
| 		} | ||||
|  | ||||
| 		if (dev->open_count && !need_excl) | ||||
| 			log_debug_devs("%s: Already opened read-only. Upgrading " | ||||
| 		if (dev->open_count && !need_excl) { | ||||
| 			log_debug_devs("%s already opened read-only. Upgrading " | ||||
| 				       "to read-write.", dev_name(dev)); | ||||
|  | ||||
| 		/* dev_close_immediate will decrement this */ | ||||
| 		dev->open_count++; | ||||
| 			dev->open_count++; | ||||
| 		} | ||||
|  | ||||
| 		dev_close_immediate(dev); | ||||
| 		// FIXME: dev with DEV_ALLOCED is released | ||||
| @@ -722,7 +693,7 @@ static void _dev_inc_error_count(struct device *dev) | ||||
| 			 dev->max_error_count, dev_name(dev)); | ||||
| } | ||||
|  | ||||
| int dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, void *buffer) | ||||
| int dev_read(struct device *dev, uint64_t offset, size_t len, void *buffer) | ||||
| { | ||||
| 	struct device_area where; | ||||
| 	int ret; | ||||
| @@ -737,7 +708,9 @@ int dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t re | ||||
| 	where.start = offset; | ||||
| 	where.size = len; | ||||
|  | ||||
| 	ret = _aligned_io(&where, buffer, 0, reason); | ||||
| 	// fprintf(stderr, "READ: %s, %lld, %d\n", dev_name(dev), offset, len); | ||||
|  | ||||
| 	ret = _aligned_io(&where, buffer, 0); | ||||
| 	if (!ret) | ||||
| 		_dev_inc_error_count(dev); | ||||
|  | ||||
| @@ -750,9 +723,9 @@ int dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t re | ||||
|  * 'buf' should be len+len2. | ||||
|  */ | ||||
| int dev_read_circular(struct device *dev, uint64_t offset, size_t len, | ||||
| 		      uint64_t offset2, size_t len2, dev_io_reason_t reason, char *buf) | ||||
| 		      uint64_t offset2, size_t len2, char *buf) | ||||
| { | ||||
| 	if (!dev_read(dev, offset, len, reason, buf)) { | ||||
| 	if (!dev_read(dev, offset, len, buf)) { | ||||
| 		log_error("Read from %s failed", dev_name(dev)); | ||||
| 		return 0; | ||||
| 	} | ||||
| @@ -764,7 +737,7 @@ int dev_read_circular(struct device *dev, uint64_t offset, size_t len, | ||||
| 	if (!len2) | ||||
| 		return 1; | ||||
|  | ||||
| 	if (!dev_read(dev, offset2, len2, reason, buf + len)) { | ||||
| 	if (!dev_read(dev, offset2, len2, buf + len)) { | ||||
| 		log_error("Circular read from %s failed", | ||||
| 			  dev_name(dev)); | ||||
| 		return 0; | ||||
| @@ -778,14 +751,14 @@ int dev_read_circular(struct device *dev, uint64_t offset, size_t len, | ||||
|  */ | ||||
|  | ||||
| /* FIXME pre-extend the file */ | ||||
| int dev_append(struct device *dev, size_t len, dev_io_reason_t reason, char *buffer) | ||||
| int dev_append(struct device *dev, size_t len, char *buffer) | ||||
| { | ||||
| 	int r; | ||||
|  | ||||
| 	if (!dev->open_count) | ||||
| 		return_0; | ||||
|  | ||||
| 	r = dev_write(dev, dev->end, len, reason, buffer); | ||||
| 	r = dev_write(dev, dev->end, len, buffer); | ||||
| 	dev->end += (uint64_t) len; | ||||
|  | ||||
| #ifndef O_DIRECT_SUPPORT | ||||
| @@ -794,7 +767,7 @@ int dev_append(struct device *dev, size_t len, dev_io_reason_t reason, char *buf | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| int dev_write(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, void *buffer) | ||||
| int dev_write(struct device *dev, uint64_t offset, size_t len, void *buffer) | ||||
| { | ||||
| 	struct device_area where; | ||||
| 	int ret; | ||||
| @@ -805,25 +778,20 @@ int dev_write(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t r | ||||
| 	if (!_dev_is_valid(dev)) | ||||
| 		return 0; | ||||
|  | ||||
| 	if (!len) { | ||||
| 		log_error(INTERNAL_ERROR "Attempted to write 0 bytes to %s at " FMTu64, dev_name(dev), offset); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	where.dev = dev; | ||||
| 	where.start = offset; | ||||
| 	where.size = len; | ||||
|  | ||||
| 	dev->flags |= DEV_ACCESSED_W; | ||||
|  | ||||
| 	ret = _aligned_io(&where, buffer, 1, reason); | ||||
| 	ret = _aligned_io(&where, buffer, 1); | ||||
| 	if (!ret) | ||||
| 		_dev_inc_error_count(dev); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| int dev_set(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, int value) | ||||
| int dev_set(struct device *dev, uint64_t offset, size_t len, int value) | ||||
| { | ||||
| 	size_t s; | ||||
| 	char buffer[4096] __attribute__((aligned(8))); | ||||
| @@ -842,7 +810,7 @@ int dev_set(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t rea | ||||
| 	memset(buffer, value, sizeof(buffer)); | ||||
| 	while (1) { | ||||
| 		s = len > sizeof(buffer) ? sizeof(buffer) : len; | ||||
| 		if (!dev_write(dev, offset, s, reason, buffer)) | ||||
| 		if (!dev_write(dev, offset, s, buffer)) | ||||
| 			break; | ||||
|  | ||||
| 		len -= s; | ||||
|   | ||||
| @@ -31,7 +31,7 @@ int dev_is_luks(struct device *dev, uint64_t *offset_found) | ||||
| 	if (offset_found) | ||||
| 		*offset_found = 0; | ||||
|  | ||||
| 	if (!dev_read(dev, 0, LUKS_SIGNATURE_SIZE, DEV_IO_SIGNATURES, buf)) | ||||
| 	if (!dev_read(dev, 0, LUKS_SIGNATURE_SIZE, buf)) | ||||
| 		goto_out; | ||||
|  | ||||
| 	ret = memcmp(buf, LUKS_SIGNATURE, LUKS_SIGNATURE_SIZE) ? 0 : 1; | ||||
|   | ||||
| @@ -28,7 +28,7 @@ | ||||
| #define MD_SB_MAGIC 0xa92b4efc | ||||
| #define MD_RESERVED_BYTES (64 * 1024ULL) | ||||
| #define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512) | ||||
| #define MD_NEW_SIZE_SECTORS(x) (((x) & ~(MD_RESERVED_SECTORS - 1)) \ | ||||
| #define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) \ | ||||
| 				- MD_RESERVED_SECTORS) | ||||
| #define MD_MAX_SYSFS_SIZE 64 | ||||
|  | ||||
| @@ -37,7 +37,7 @@ static int _dev_has_md_magic(struct device *dev, uint64_t sb_offset) | ||||
| 	uint32_t md_magic; | ||||
|  | ||||
| 	/* Version 1 is little endian; version 0.90.0 is machine endian */ | ||||
| 	if (dev_read(dev, sb_offset, sizeof(uint32_t), DEV_IO_SIGNATURES, &md_magic) && | ||||
| 	if (dev_read(dev, sb_offset, sizeof(uint32_t), &md_magic) && | ||||
| 	    ((md_magic == MD_SB_MAGIC) || | ||||
| 	     ((MD_SB_MAGIC != xlate32(MD_SB_MAGIC)) && (md_magic == xlate32(MD_SB_MAGIC))))) | ||||
| 		return 1; | ||||
| @@ -261,7 +261,8 @@ out: | ||||
| /* | ||||
|  * Retrieve chunk size from md device using sysfs. | ||||
|  */ | ||||
| static unsigned long _dev_md_chunk_size(struct dev_types *dt, struct device *dev) | ||||
| static unsigned long dev_md_chunk_size(struct dev_types *dt, | ||||
| 				       struct device *dev) | ||||
| { | ||||
| 	const char *attribute = "chunk_size"; | ||||
| 	unsigned long chunk_size_bytes = 0UL; | ||||
| @@ -279,7 +280,7 @@ static unsigned long _dev_md_chunk_size(struct dev_types *dt, struct device *dev | ||||
| /* | ||||
|  * Retrieve level from md device using sysfs. | ||||
|  */ | ||||
| static int _dev_md_level(struct dev_types *dt, struct device *dev) | ||||
| static int dev_md_level(struct dev_types *dt, struct device *dev) | ||||
| { | ||||
| 	char level_string[MD_MAX_SYSFS_SIZE]; | ||||
| 	const char *attribute = "level"; | ||||
| @@ -302,7 +303,7 @@ static int _dev_md_level(struct dev_types *dt, struct device *dev) | ||||
| /* | ||||
|  * Retrieve raid_disks from md device using sysfs. | ||||
|  */ | ||||
| static int _dev_md_raid_disks(struct dev_types *dt, struct device *dev) | ||||
| static int dev_md_raid_disks(struct dev_types *dt, struct device *dev) | ||||
| { | ||||
| 	const char *attribute = "raid_disks"; | ||||
| 	int raid_disks = 0; | ||||
| @@ -326,15 +327,15 @@ unsigned long dev_md_stripe_width(struct dev_types *dt, struct device *dev) | ||||
| 	unsigned long stripe_width_sectors = 0UL; | ||||
| 	int level, raid_disks, data_disks; | ||||
|  | ||||
| 	chunk_size_sectors = _dev_md_chunk_size(dt, dev); | ||||
| 	chunk_size_sectors = dev_md_chunk_size(dt, dev); | ||||
| 	if (!chunk_size_sectors) | ||||
| 		return 0; | ||||
|  | ||||
| 	level = _dev_md_level(dt, dev); | ||||
| 	level = dev_md_level(dt, dev); | ||||
| 	if (level < 0) | ||||
| 		return 0; | ||||
|  | ||||
| 	raid_disks = _dev_md_raid_disks(dt, dev); | ||||
| 	raid_disks = dev_md_raid_disks(dt, dev); | ||||
| 	if (!raid_disks) | ||||
| 		return 0; | ||||
|  | ||||
|   | ||||
| @@ -20,7 +20,8 @@ | ||||
| #define MAX_PAGESIZE	(64 * 1024) | ||||
| #define SIGNATURE_SIZE  10 | ||||
|  | ||||
| static int _swap_detect_signature(const char *buf) | ||||
| static int | ||||
| _swap_detect_signature(const char *buf) | ||||
| { | ||||
| 	if (memcmp(buf, "SWAP-SPACE", 10) == 0 || | ||||
|             memcmp(buf, "SWAPSPACE2", 10) == 0) | ||||
| @@ -61,7 +62,7 @@ int dev_is_swap(struct device *dev, uint64_t *offset_found) | ||||
| 		if (size < (page >> SECTOR_SHIFT)) | ||||
| 			break; | ||||
| 		if (!dev_read(dev, page - SIGNATURE_SIZE, | ||||
| 			      SIGNATURE_SIZE, DEV_IO_SIGNATURES, buf)) { | ||||
| 			      SIGNATURE_SIZE, buf)) { | ||||
| 			ret = -1; | ||||
| 			break; | ||||
| 		} | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user