master - man: update lvmsystemid wording
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=327f62a25533c195f4b...
Commit: 327f62a25533c195f4b49a29974f3c5a2f93c15b
Parent: b5f444d447e6c3f83fc1cc963b81501a760bbbeb
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Jun 14 12:34:00 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Thu Jun 14 12:35:00 2018 -0500
man: update lvmsystemid wording
to refer to "shared VG" instead of "lockd VG".
---
man/lvmsystemid.7_main | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/man/lvmsystemid.7_main b/man/lvmsystemid.7_main
index 9b36dc3..97c67c2 100644
--- a/man/lvmsystemid.7_main
+++ b/man/lvmsystemid.7_main
@@ -346,9 +346,9 @@ of the foreign VG to its own. See Overriding system ID above.
.SS shared VGs
-A shared/lockd VG has no system ID set, allowing multiple hosts to use it
-via lvmlockd. Changing a VG to a lockd type will clear the existing
-system ID. Applicable only if LVM is compiled with lockd support.
+A shared VG has no system ID set, allowing multiple hosts to use it
+via lvmlockd. Changing a VG to shared will clear the existing
+system ID. Applicable only if LVM is compiled with lvmlockd support.
.SS clustered VGs
5 years, 10 months
master - man: updates to lvmlockd
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=b5f444d447e6c3f83fc...
Commit: b5f444d447e6c3f83fc1cc963b81501a760bbbeb
Parent: e84e9cd115c37a64de6c0b67972a01044dffe843
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Jun 14 12:30:45 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Thu Jun 14 12:35:00 2018 -0500
man: updates to lvmlockd
The terminology has migrated toward using "shared VG"
rather than "lockd VG".
Also improve the wording in a number of places.
---
man/lvmlockd.8_main | 341 +++++++++++++++++++++-----------------------------
1 files changed, 143 insertions(+), 198 deletions(-)
diff --git a/man/lvmlockd.8_main b/man/lvmlockd.8_main
index 6411c01..cfb45b2 100644
--- a/man/lvmlockd.8_main
+++ b/man/lvmlockd.8_main
@@ -84,8 +84,8 @@ For default settings, see lvmlockd -h.
.SS Initial set up
-Using LVM with lvmlockd for the first time includes some one-time set up
-steps:
+Setting up LVM to use lvmlockd and a shared VG for the first time includes
+some one time set up steps:
.SS 1. choose a lock manager
@@ -94,7 +94,7 @@ steps:
If dlm (or corosync) are already being used by other cluster
software, then select dlm. dlm uses corosync which requires additional
configuration beyond the scope of this document. See corosync and dlm
-documentation for instructions on configuration, setup and usage.
+documentation for instructions on configuration, set up and usage.
.I sanlock
.br
@@ -117,7 +117,9 @@ Assign each host a unique host_id in the range 1-2000 by setting
.SS 3. start lvmlockd
-Use a unit/init file, or run the lvmlockd daemon directly:
+Start the lvmlockd daemon.
+.br
+Use systemctl, a cluster resource agent, or run directly, e.g.
.br
systemctl start lvm2-lvmlockd
@@ -125,14 +127,17 @@ systemctl start lvm2-lvmlockd
.I sanlock
.br
-Use unit/init files, or start wdmd and sanlock daemons directly:
+Start the sanlock and wdmd daemons.
+.br
+Use systemctl or run directly, e.g.
.br
systemctl start wdmd sanlock
.I dlm
.br
-Follow external clustering documentation when applicable, or use
-unit/init files:
+Start the dlm and corosync daemons.
+.br
+Use systemctl, a cluster resource agent, or run directly, e.g.
.br
systemctl start corosync dlm
@@ -141,18 +146,17 @@ systemctl start corosync dlm
vgcreate --shared <vgname> <devices>
The shared option sets the VG lock type to sanlock or dlm depending on
-which lock manager is running. LVM commands will perform locking for the
-VG using lvmlockd. lvmlockd will use the chosen lock manager.
+which lock manager is running. LVM commands acquire locks from lvmlockd,
+and lvmlockd uses the chosen lock manager.
.SS 6. start VG on all hosts
vgchange --lock-start
-lvmlockd requires shared VGs to be started before they are used. This is
-a lock manager operation to start (join) the VG lockspace, and it may take
-some time. Until the start completes, locks for the VG are not available.
-LVM commands are allowed to read the VG while start is in progress. (A
-unit/init file can also be used to start VGs.)
+Shared VGs must be started before they are used. Starting the VG performs
+lock manager initialization that is necessary to begin using locks (i.e.
+creating and joining a lockspace). Starting the VG may take some time,
+and until the start completes the VG may not be modified or activated.
.SS 7. create and activate LVs
@@ -168,9 +172,9 @@ multiple hosts.)
.SS Normal start up and shut down
-After initial set up, start up and shut down include the following general
-steps. They can be performed manually or using the system service
-manager.
+After initial set up, start up and shut down include the following steps.
+They can be performed directly or may be automated using systemd or a
+cluster resource manager/agents.
\[bu]
start lvmlockd
@@ -204,106 +208,64 @@ stop lvmlockd
.SH TOPICS
-.SS VG access control
+.SS Protecting VGs on shared devices
-The following terms are used to describe different forms of VG access
-control.
+The following terms are used to describe the different ways of accessing
+VGs on shared devices.
-.I "lockd VG"
+.I "shared VG"
-A "lockd VG" is a shared VG that has a "lock type" of dlm or sanlock.
-Using it requires lvmlockd. These VGs exist on shared storage that is
-visible to multiple hosts. LVM commands use lvmlockd to perform locking
-for these VGs when they are used.
+A shared VG exists on shared storage that is visible to multiple hosts.
+LVM acquires locks through lvmlockd to coordinate access to shared VGs.
+A shared VG has lock_type "dlm" or "sanlock", which specifies the lock
+manager lvmlockd will use.
-If the lock manager for the lock type is not available (e.g. not started
-or failed), lvmlockd is unable to acquire locks for LVM commands. LVM
-commands that only read the VG will generally be allowed to continue
-without locks in this case (with a warning). Commands to modify or
-activate the VG will fail without the necessary locks.
+When the lock manager for the lock type is not available (e.g. not started
+or failed), lvmlockd is unable to acquire locks for LVM commands. In this
+situation, LVM commands are only allowed to read and display the VG;
+changes and activation will fail.
.I "local VG"
-A "local VG" is meant to be used by a single host. It has no lock type or
-lock type "none". LVM commands and lvmlockd do not perform locking for
-these VGs. A local VG typically exists on local (non-shared) devices and
-cannot be used concurrently from different hosts.
+A local VG is meant to be used by a single host. It has no lock type or
+lock type "none". A local VG typically exists on local (non-shared)
+devices and cannot be used concurrently from different hosts.
If a local VG does exist on shared devices, it should be owned by a single
-host by having its system ID set, see
+host by having the system ID set, see
.BR lvmsystemid (7).
-Only the host with a matching system ID can use the local VG. A VG
-with no lock type and no system ID should be excluded from all but one
-host using lvm.conf filters. Without any of these protections, a local VG
-on shared devices can be easily damaged or destroyed.
+The host with a matching system ID can use the local VG and other hosts
+will ignore it. A VG with no lock type and no system ID should be
+excluded from all but one host using lvm.conf filters. Without any of
+these protections, a local VG on shared devices can be easily damaged or
+destroyed.
.I "clvm VG"
-A "clvm VG" is a VG on shared storage (like a lockd VG) that requires
-clvmd for clustering. See below for converting a clvm VG to a lockd VG.
+A clvm VG (or clustered VG) is a VG on shared storage (like a shared VG)
+that requires clvmd for clustering and locking. See below for converting
+a clvm/clustered VG to a shared VG.
-.SS lockd VGs from hosts not using lvmlockd
+.SS shared VGs from hosts not using lvmlockd
-Only hosts that use lockd VGs should be configured to run lvmlockd.
-However, shared devices in lockd VGs may be visible from hosts not
-using lvmlockd. From a host not using lvmlockd, lockd VGs are ignored
-in the same way as foreign VGs (see
+Hosts that do not use shared VGs will not be running lvmlockd. In this
+case, shared VGs that are still visible to the host will be ignored
+(like foreign VGs, see
.BR lvmsystemid (7).)
-The --shared option for reporting and display commands causes lockd VGs
+The --shared option for reporting and display commands causes shared VGs
to be displayed on a host not using lvmlockd, like the --foreign option
does for foreign VGs.
-.SS vgcreate comparison
-
-The type of VG access control is specified in the vgcreate command.
-See
-.BR vgcreate (8)
-for all vgcreate options.
-
-.B vgcreate <vgname> <devices>
-
-.IP \[bu] 2
-Creates a local VG with the local host's system ID when neither lvmlockd nor clvm are configured.
-.IP \[bu] 2
-Creates a local VG with the local host's system ID when lvmlockd is configured.
-.IP \[bu] 2
-Creates a clvm VG when clvm is configured.
-
-.P
-
-.B vgcreate --shared <vgname> <devices>
-.IP \[bu] 2
-Requires lvmlockd to be configured and running.
-.IP \[bu] 2
-Creates a lockd VG with lock type sanlock|dlm depending on which lock
-manager is running.
-.IP \[bu] 2
-LVM commands request locks from lvmlockd to use the VG.
-.IP \[bu] 2
-lvmlockd obtains locks from the selected lock manager.
-
-.P
-
-.B vgcreate -c|--clustered y <vgname> <devices>
-.IP \[bu] 2
-Requires clvm to be configured and running.
-.IP \[bu] 2
-Creates a clvm VG with the "clustered" flag.
-.IP \[bu] 2
-LVM commands request locks from clvmd to use the VG.
-
-.P
-
.SS creating the first sanlock VG
Creating the first sanlock VG is not protected by locking, so it requires
special attention. This is because sanlock locks exist on storage within
-the VG, so they are not available until the VG exists. The first sanlock
-VG created will automatically contain the "global lock". Be aware of the
-following special considerations:
+the VG, so they are not available until after the VG is created. The
+first sanlock VG that is created will automatically contain the "global
+lock". Be aware of the following special considerations:
.IP \[bu] 2
The first vgcreate command needs to be given the path to a device that has
@@ -318,54 +280,48 @@ to be accessible to all hosts that will use sanlock shared VGs. All hosts
will need to use the global lock from the first sanlock VG.
.IP \[bu] 2
-While running vgcreate for the first sanlock VG, ensure that the device
-being used is not used by another LVM command. Allocation of shared
-devices is usually protected by the global lock, but this cannot be done
-for the first sanlock VG which will hold the global lock.
-
-.IP \[bu] 2
-While running vgcreate for the first sanlock VG, ensure that the VG name
-being used is not used by another LVM command. Uniqueness of VG names is
-usually ensured by the global lock.
+The device and VG name used by the initial vgcreate will not be protected
+from concurrent use by another vgcreate on another host.
See below for more information about managing the sanlock global lock.
-.SS using lockd VGs
+.SS using shared VGs
-There are some special considerations when using lockd VGs.
+There are some special considerations when using shared VGs.
-When use_lvmlockd is first enabled in lvm.conf, and before the first lockd
-VG is created, no global lock will exist. In this initial state, LVM
-commands try and fail to acquire the global lock, producing a warning, and
-some commands are disallowed. Once the first lockd VG is created, the
-global lock will be available, and LVM will be fully operational.
+When use_lvmlockd is first enabled in lvm.conf, and before the first
+shared VG is created, no global lock will exist. In this initial state,
+LVM commands try and fail to acquire the global lock, producing a warning,
+and some commands are disallowed. Once the first shared VG is created,
+the global lock will be available, and LVM will be fully operational.
-When a new lockd VG is created, its lockspace is automatically started on
-the host that creates it. Other hosts need to run 'vgchange
---lock-start' to start the new VG before they can use it.
+When a new shared VG is created, its lockspace is automatically started on
+the host that creates it. Other hosts need to run 'vgchange --lock-start'
+to start the new VG before they can use it.
-From the 'vgs' command, lockd VGs are indicated by "s" (for shared) in the
-sixth attr field. The specific lock type and lock args for a lockd VG can
-be displayed with 'vgs -o+locktype,lockargs'.
+From the 'vgs' command, shared VGs are indicated by "s" (for shared) in
+the sixth attr field, and by "shared" in the "--options shared" report
+field. The specific lock type and lock args for a shared VG can be
+displayed with 'vgs -o+locktype,lockargs'.
-lockd VGs need to be "started" and "stopped", unlike other types of VGs.
+Shared VGs need to be "started" and "stopped", unlike other types of VGs.
See the following section for a full description of starting and stopping.
-vgremove of a lockd VG will fail if other hosts have the VG started.
-Run vgchange --lock-stop <vgname> on all other hosts before vgremove.
-(It may take several seconds before vgremove recognizes that all hosts
-have stopped a sanlock VG.)
+Removing a shared VG will fail if other hosts have the VG started. Run
+vgchange --lock-stop <vgname> on all other hosts before vgremove. (It may
+take several seconds before vgremove recognizes that all hosts have
+stopped a sanlock VG.)
.SS starting and stopping VGs
-Starting a lockd VG (vgchange --lock-start) causes the lock manager to
+Starting a shared VG (vgchange --lock-start) causes the lock manager to
start (join) the lockspace for the VG on the host where it is run. This
makes locks for the VG available to LVM commands on the host. Before a VG
is started, only LVM commands that read/display the VG are allowed to
continue without locks (and with a warning).
-Stopping a lockd VG (vgchange --lock-stop) causes the lock manager to
+Stopping a shared VG (vgchange --lock-stop) causes the lock manager to
stop (leave) the lockspace for the VG on the host where it is run. This
makes locks for the VG inaccessible to the host. A VG cannot be stopped
while it has active LVs.
@@ -374,7 +330,7 @@ When using the lock type sanlock, starting a VG can take a long time
(potentially minutes if the host was previously shut down without cleanly
stopping the VG.)
-A lockd VG can be started after all the following are true:
+A shared VG can be started after all the following are true:
.br
\[bu]
lvmlockd is running
@@ -386,9 +342,9 @@ the lock manager is running
the VG's devices are visible on the system
.br
-A lockd VG can be stopped if all LVs are deactivated.
+A shared VG can be stopped if all LVs are deactivated.
-All lockd VGs can be started/stopped using:
+All shared VGs can be started/stopped using:
.br
vgchange --lock-start
.br
@@ -407,12 +363,12 @@ vgchange --lock-start --lock-opt nowait ...
lvmlockd can be asked directly to stop all lockspaces:
.br
-lvmlockctl --stop-lockspaces
+lvmlockctl -S|--stop-lockspaces
-To start only selected lockd VGs, use the lvm.conf
+To start only selected shared VGs, use the lvm.conf
activation/lock_start_list. When defined, only VG names in this list are
started by vgchange. If the list is not defined (the default), all
-visible lockd VGs are started. To start only "vg1", use the following
+visible shared VGs are started. To start only "vg1", use the following
lvm.conf configuration:
.nf
@@ -435,7 +391,7 @@ The "auto" option causes the command to follow the lvm.conf
activation/auto_lock_start_list. If auto_lock_start_list is undefined,
all VGs are started, just as if the auto option was not used.
-When auto_lock_start_list is defined, it lists the lockd VGs that should
+When auto_lock_start_list is defined, it lists the shared VGs that should
be started by the auto command. VG names that do not match an item in the
list will be ignored by the auto start command.
@@ -443,23 +399,20 @@ list will be ignored by the auto start command.
commands, i.e. with or without the auto option. When the lock_start_list
is defined, only VGs matching a list item can be started with vgchange.)
-The auto_lock_start_list allows a user to select certain lockd VGs that
+The auto_lock_start_list allows a user to select certain shared VGs that
should be automatically started by the system (or indirectly, those that
should not).
-To use auto activation of lockd LVs (see auto_activation_volume_list),
-auto starting of the corresponding lockd VGs is necessary.
-
.SS internal command locking
To optimize the use of LVM with lvmlockd, be aware of the three kinds of
locks and when they are used:
-.I GL lock
+.I Global lock
-The global lock (GL lock) is associated with global information, which is
-information not isolated to a single VG. This includes:
+The global lock s associated with global information, which is information
+not isolated to a single VG. This includes:
\[bu]
The global VG namespace.
@@ -484,61 +437,58 @@ acquired.
.I VG lock
-A VG lock is associated with each lockd VG. The VG lock is acquired in
-shared mode to read the VG and in exclusive mode to change the VG (modify
-the VG metadata or activating LVs). This lock serializes access to a VG
-with all other LVM commands accessing the VG from all hosts.
-
-The command 'vgs' will not only acquire the GL lock to read the list of
-all VG names, but will acquire the VG lock for each VG prior to reading
-it.
+A VG lock is associated with each shared VG. The VG lock is acquired in
+shared mode to read the VG and in exclusive mode to change the VG or
+activate LVs. This lock serializes access to a VG with all other LVM
+commands accessing the VG from all hosts.
-The command 'vgs <vgname>' does not acquire the GL lock (it does not need
-the list of all VG names), but will acquire the VG lock on each VG name
-argument.
+The command 'vgs <vgname>' does not acquire the global lock (it does not
+need the list of all VG names), but will acquire the VG lock on each VG
+name argument.
.I LV lock
An LV lock is acquired before the LV is activated, and is released after
the LV is deactivated. If the LV lock cannot be acquired, the LV is not
-activated. LV locks are persistent and remain in place when the
-activation command is done. GL and VG locks are transient, and are held
-only while an LVM command is running.
+activated. (LV locks are persistent and remain in place when the
+activation command is done. Global and VG locks are transient, and are
+held only while an LVM command is running.)
.I lock retries
-If a request for a GL or VG lock fails due to a lock conflict with another
-host, lvmlockd automatically retries for a short time before returning a
-failure to the LVM command. If those retries are insufficient, the LVM
-command will retry the entire lock request a number of times specified by
-global/lvmlockd_lock_retries before failing. If a request for an LV lock
-fails due to a lock conflict, the command fails immediately.
+If a request for a Global or VG lock fails due to a lock conflict with
+another host, lvmlockd automatically retries for a short time before
+returning a failure to the LVM command. If those retries are
+insufficient, the LVM command will retry the entire lock request a number
+of times specified by global/lvmlockd_lock_retries before failing. If a
+request for an LV lock fails due to a lock conflict, the command fails
+immediately.
.SS managing the global lock in sanlock VGs
The global lock exists in one of the sanlock VGs. The first sanlock VG
created will contain the global lock. Subsequent sanlock VGs will each
-contain disabled global locks that can be enabled later if necessary.
+contain a disabled global lock that can be enabled later if necessary.
The VG containing the global lock must be visible to all hosts using
-sanlock VGs. This can be a reason to create a small sanlock VG, visible
-to all hosts, and dedicated to just holding the global lock. While not
-required, this strategy can help to avoid difficulty in the future if VGs
-are moved or removed.
+sanlock VGs. For this reason, it can be useful to create a small sanlock
+VG, visible to all hosts, and dedicated to just holding the global lock.
+While not required, this strategy can help to avoid difficulty in the
+future if VGs are moved or removed.
The vgcreate command typically acquires the global lock, but in the case
of the first sanlock VG, there will be no global lock to acquire until the
first vgcreate is complete. So, creating the first sanlock VG is a
special case that skips the global lock.
-vgcreate for a sanlock VG determines it is the first one to exist if no
-other sanlock VGs are visible. It is possible that other sanlock VGs do
-exist but are not visible on the host running vgcreate. In this case,
-vgcreate would create a new sanlock VG with the global lock enabled. When
-the other VG containing a global lock appears, lvmlockd will see more than
-one VG with a global lock enabled, and LVM commands will report that there
-are duplicate global locks.
+vgcreate determines that it's creating the first sanlock VG when no other
+sanlock VGs are visible on the system. It is possible that other sanlock
+VGs do exist, but are not visible when vgcreate checks for them. In this
+case, vgcreate will create a new sanlock VG with the global lock enabled.
+When the another VG containing a global lock appears, lvmlockd will then
+see more than one VG with a global lock enabled. LVM commands will report
+that there are duplicate global locks.
If the situation arises where more than one sanlock VG contains a global
lock, the global lock should be manually disabled in all but one of them
@@ -556,8 +506,8 @@ VGs with the command:
lvmlockctl --gl-enable <vgname>
-A small sanlock VG dedicated to holding the global lock can avoid the case
-where the GL lock must be manually enabled after a vgremove.
+(Using a small sanlock VG dedicated to holding the global lock can avoid
+the case where the global lock must be manually enabled after a vgremove.)
.SS internal lvmlock LV
@@ -574,8 +524,8 @@ device, then use vgextend to add other devices.
.SS LV activation
-In a shared VG, activation changes involve locking through lvmlockd, and
-the following values are possible with lvchange/vgchange -a:
+In a shared VG, LV activation involves locking through lvmlockd, and the
+following values are possible with lvchange/vgchange -a:
.IP \fBy\fP|\fBey\fP
The command activates the LV in exclusive mode, allowing a single host
@@ -596,10 +546,6 @@ The shared mode is intended for a multi-host/cluster application or
file system.
LV types that cannot be used concurrently
from multiple hosts include thin, cache, raid, and snapshot.
-lvextend on LV with shared locks is not yet allowed. The LV must be
-deactivated, or activated exclusively to run lvextend. (LVs with
-the mirror type can be activated in shared mode from multiple hosts
-when using the dlm lock type and cmirrord.)
.IP \fBn\fP
The command deactivates the LV. After deactivating the LV, the command
@@ -654,7 +600,7 @@ with the expiring lease before other hosts can acquire its locks.
When the sanlock daemon detects that the lease storage is lost, it runs
the command lvmlockctl --kill <vgname>. This command emits a syslog
-message stating that lease storage is lost for the VG and LVs must be
+message stating that lease storage is lost for the VG, and LVs must be
immediately deactivated.
If no LVs are active in the VG, then the lockspace with an expiring lease
@@ -666,10 +612,10 @@ If the VG has active LVs when the lock storage is lost, the LVs must be
quickly deactivated before the lockspace lease expires. After all LVs are
deactivated, run lvmlockctl --drop <vgname> to clear the expiring
lockspace from lvmlockd. If all LVs in the VG are not deactivated within
-about 40 seconds, sanlock will reset the host using the local watchdog.
-The machine reset is effectively a severe form of "deactivating" LVs
-before they can be activated on other hosts. The reset is considered a
-better alternative than having LVs used by multiple hosts at once, which
+about 40 seconds, sanlock uses wdmd and the local watchdog to reset the
+host. The machine reset is effectively a severe form of "deactivating"
+LVs before they can be activated on other hosts. The reset is considered
+a better alternative than having LVs used by multiple hosts at once, which
could easily damage or destroy their content.
In the future, the lvmlockctl kill command may automatically attempt to
@@ -681,8 +627,7 @@ sanlock resets the machine.
If the sanlock daemon fails or exits while a lockspace is started, the
local watchdog will reset the host. This is necessary to protect any
-application resources that depend on sanlock leases which will be lost
-without sanlock running.
+application resources that depend on sanlock leases.
.SS changing dlm cluster name
@@ -762,14 +707,14 @@ Start the VG on hosts to use it:
vgchange --lock-start <vgname>
-.SS changing a local VG to a lockd VG
+.SS changing a local VG to a shared VG
All LVs must be inactive to change the lock type.
lvmlockd must be configured and running as described in USAGE.
.IP \[bu] 2
-Change a local VG to a lockd VG with the command:
+Change a local VG to a shared VG with the command:
.br
vgchange --lock-type sanlock|dlm <vgname>
@@ -780,7 +725,7 @@ vgchange --lock-start <vgname>
.P
-.SS changing a lockd VG to a local VG
+.SS changing a shared VG to a local VG
All LVs must be inactive to change the lock type.
@@ -806,11 +751,11 @@ type can be forcibly changed to none with:
vgchange --lock-type none --lock-opt force <vgname>
-To change a VG from one lockd type to another (i.e. between sanlock and
+To change a VG from one lock type to another (i.e. between sanlock and
dlm), first change it to a local VG, then to the new type.
-.SS changing a clvm VG to a lockd VG
+.SS changing a clvm/clustered VG to a shared VG
All LVs must be inactive to change the lock type.
@@ -823,15 +768,15 @@ If the clvm cluster is no longer running on any nodes, then extra options
can be used to forcibly make the VG local. Caution: this is only safe if
all nodes have stopped using the VG:
-vgchange --lock-type none --lock-opt force <vgname>
+vgchange --lock-type none --lock-opt force <vgname>
After the VG is local, follow the steps described in "changing a local VG
-to a lockd VG".
+to a shared VG".
-.SS limitations of lockd VGs
+.SS limitations of shared VGs
-Things that do not yet work in lockd VGs:
+Things that do not yet work in shared VGs:
.br
\[bu]
using external origins for thin LVs
@@ -851,22 +796,22 @@ vgsplit and vgmerge (convert to a local VG to do this)
.SS lvmlockd changes from clvmd
-(See above for converting an existing clvm VG to a lockd VG.)
+(See above for converting an existing clvm VG to a shared VG.)
While lvmlockd and clvmd are entirely different systems, LVM command usage
remains similar. Differences are more notable when using lvmlockd's
sanlock option.
-Visible usage differences between lockd VGs (using lvmlockd) and clvm VGs
-(using clvmd):
+Visible usage differences between shared VGs (using lvmlockd) and
+clvm/clustered VGs (using clvmd):
.IP \[bu] 2
lvm.conf must be configured to use either lvmlockd (use_lvmlockd=1) or
clvmd (locking_type=3), but not both.
.IP \[bu] 2
-vgcreate --shared creates a lockd VG, and vgcreate --clustered y
-creates a clvm VG.
+vgcreate --shared creates a shared VG, and vgcreate --clustered y
+creates a clvm/clustered VG.
.IP \[bu] 2
lvmlockd adds the option of using sanlock for locking, avoiding the
@@ -887,11 +832,11 @@ lvmlockd works with thin and cache pools and LVs.
lvmlockd works with lvmetad.
.IP \[bu] 2
-lvmlockd saves the cluster name for a lockd VG using dlm. Only hosts in
+lvmlockd saves the cluster name for a shared VG using dlm. Only hosts in
the matching cluster can use the VG.
.IP \[bu] 2
-lvmlockd requires starting/stopping lockd VGs with vgchange --lock-start
+lvmlockd requires starting/stopping shared VGs with vgchange --lock-start
and --lock-stop.
.IP \[bu] 2
@@ -914,7 +859,7 @@ reporting option lock_args to view the corresponding metadata fields.
.IP \[bu] 2
In the 'vgs' command's sixth VG attr field, "s" for "shared" is displayed
-for lockd VGs.
+for shared VGs.
.IP \[bu] 2
If lvmlockd fails or is killed while in use, locks it held remain but are
5 years, 10 months
master - device_mapper: remove libdm-stats.c
by Joe Thornber
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=e84e9cd115c37a64de6...
Commit: e84e9cd115c37a64de6c0b67972a01044dffe843
Parent: fededfbbbc5ed79c509a34a0f8a681fa27e49533
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Thu Jun 14 14:32:17 2018 +0100
Committer: Joe Thornber <ejt(a)redhat.com>
CommitterDate: Thu Jun 14 14:32:17 2018 +0100
device_mapper: remove libdm-stats.c
We don't use it in lvm.
---
device_mapper/Makefile | 1 -
device_mapper/all.h | 1314 -----------
device_mapper/libdm-stats.c | 5096 -------------------------------------------
3 files changed, 0 insertions(+), 6411 deletions(-)
diff --git a/device_mapper/Makefile b/device_mapper/Makefile
index 3a1e415..931431c 100644
--- a/device_mapper/Makefile
+++ b/device_mapper/Makefile
@@ -17,7 +17,6 @@ DEVICE_MAPPER_SOURCE=\
device_mapper/libdm-deptree.c \
device_mapper/libdm-file.c \
device_mapper/libdm-report.c \
- device_mapper/libdm-stats.c \
device_mapper/libdm-string.c \
device_mapper/libdm-targets.c \
device_mapper/libdm-timestamp.c \
diff --git a/device_mapper/all.h b/device_mapper/all.h
index c834f32..8dcb38c 100644
--- a/device_mapper/all.h
+++ b/device_mapper/all.h
@@ -436,1006 +436,6 @@ int dm_get_status_thin(struct dm_pool *mem, const char *params,
struct dm_status_thin **status);
/*
- * device-mapper statistics support
- */
-
-/*
- * Statistics handle.
- *
- * Operations on dm_stats objects include managing statistics regions
- * and obtaining and manipulating current counter values from the
- * kernel. Methods are provided to return baisc count values and to
- * derive time-based metrics when a suitable interval estimate is
- * provided.
- *
- * Internally the dm_stats handle contains a pointer to a table of one
- * or more dm_stats_region objects representing the regions registered
- * with the dm_stats_create_region() method. These in turn point to a
- * table of one or more dm_stats_counters objects containing the
- * counter sets for each defined area within the region:
- *
- * dm_stats->dm_stats_region[nr_regions]->dm_stats_counters[nr_areas]
- *
- * This structure is private to the library and may change in future
- * versions: all users should make use of the public interface and treat
- * the dm_stats type as an opaque handle.
- *
- * Regions and counter sets are stored in order of increasing region_id.
- * Depending on region specifications and the sequence of create and
- * delete operations this may not correspond to increasing sector
- * number: users of the library should not assume that this is the case
- * unless region creation is deliberately managed to ensure this (by
- * always creating regions in strict order of ascending sector address).
- *
- * Regions may also overlap so the same sector range may be included in
- * more than one region or area: applications should be prepared to deal
- * with this or manage regions such that it does not occur.
- */
-struct dm_stats;
-
-/*
- * Histogram handle.
- *
- * A histogram object represents the latency histogram values and bin
- * boundaries of the histogram associated with a particular area.
- *
- * Operations on the handle allow the number of bins, bin boundaries,
- * counts and relative proportions to be obtained as well as the
- * conversion of a histogram or its bounds to a compact string
- * representation.
- */
-struct dm_histogram;
-
-/*
- * Allocate a dm_stats handle to use for subsequent device-mapper
- * statistics operations. A program_id may be specified and will be
- * used by default for subsequent operations on this handle.
- *
- * If program_id is NULL or the empty string a program_id will be
- * automatically set to the value contained in /proc/self/comm.
- */
-struct dm_stats *dm_stats_create(const char *program_id);
-
-/*
- * Bind a dm_stats handle to the specified device major and minor
- * values. Any previous binding is cleared and any preexisting counter
- * data contained in the handle is released.
- */
-int dm_stats_bind_devno(struct dm_stats *dms, int major, int minor);
-
-/*
- * Bind a dm_stats handle to the specified device name.
- * Any previous binding is cleared and any preexisting counter
- * data contained in the handle is released.
- */
-int dm_stats_bind_name(struct dm_stats *dms, const char *name);
-
-/*
- * Bind a dm_stats handle to the specified device UUID.
- * Any previous binding is cleared and any preexisting counter
- * data contained in the handle is released.
- */
-int dm_stats_bind_uuid(struct dm_stats *dms, const char *uuid);
-
-/*
- * Bind a dm_stats handle to the device backing the file referenced
- * by the specified file descriptor.
- *
- * File descriptor fd must reference a regular file, open for reading,
- * in a local file system, backed by a device-mapper device, that
- * supports the FIEMAP ioctl, and that returns data describing the
- * physical location of extents.
- */
-int dm_stats_bind_from_fd(struct dm_stats *dms, int fd);
-/*
- * Test whether the running kernel supports the precise_timestamps
- * feature. Presence of this feature also implies histogram support.
- * The library will check this call internally and fails any attempt
- * to use nanosecond counters or histograms on kernels that fail to
- * meet this check.
- */
-int dm_message_supports_precise_timestamps(void);
-
-/*
- * Precise timetamps and histogram support.
- *
- * Test for the presence of precise_timestamps and histogram support.
- */
-int dm_stats_driver_supports_precise(void);
-int dm_stats_driver_supports_histogram(void);
-
-/*
- * Returns 1 if the specified region has the precise_timestamps feature
- * enabled (i.e. produces nanosecond-precision counter values) or 0 for
- * a region using the default milisecond precision.
- */
-int dm_stats_get_region_precise_timestamps(const struct dm_stats *dms,
- uint64_t region_id);
-
-/*
- * Returns 1 if the region at the current cursor location has the
- * precise_timestamps feature enabled (i.e. produces
- * nanosecond-precision counter values) or 0 for a region using the
- * default milisecond precision.
- */
-int dm_stats_get_current_region_precise_timestamps(const struct dm_stats *dms);
-
-#define DM_STATS_ALL_PROGRAMS ""
-/*
- * Parse the response from a @stats_list message. dm_stats_list will
- * allocate the necessary dm_stats and dm_stats region structures from
- * the embedded dm_pool. No counter data will be obtained (the counters
- * members of dm_stats_region objects are set to NULL).
- *
- * A program_id may optionally be supplied; if the argument is non-NULL
- * only regions with a matching program_id value will be considered. If
- * the argument is NULL then the default program_id associated with the
- * dm_stats handle will be used. Passing the special value
- * DM_STATS_ALL_PROGRAMS will cause all regions to be queried
- * regardless of region program_id.
- */
-int dm_stats_list(struct dm_stats *dms, const char *program_id);
-
-#define DM_STATS_REGIONS_ALL UINT64_MAX
-/*
- * Populate a dm_stats object with statistics for one or more regions of
- * the specified device.
- *
- * A program_id may optionally be supplied; if the argument is non-NULL
- * only regions with a matching program_id value will be considered. If
- * the argument is NULL then the default program_id associated with the
- * dm_stats handle will be used. Passing the special value
- * DM_STATS_ALL_PROGRAMS will cause all regions to be queried
- * regardless of region program_id.
- *
- * Passing the special value DM_STATS_REGIONS_ALL as the region_id
- * argument will attempt to retrieve all regions selected by the
- * program_id argument.
- *
- * If region_id is used to request a single region_id to be populated
- * the program_id is ignored.
- */
-int dm_stats_populate(struct dm_stats *dms, const char *program_id,
- uint64_t region_id);
-
-/*
- * Create a new statistics region on the device bound to dms.
- *
- * start and len specify the region start and length in 512b sectors.
- * Passing zero for both start and len will create a region spanning
- * the entire device.
- *
- * Step determines how to subdivide the region into discrete counter
- * sets: a positive value specifies the size of areas into which the
- * region should be split while a negative value will split the region
- * into a number of areas equal to the absolute value of step:
- *
- * - a region with one area spanning the entire device:
- *
- * dm_stats_create_region(dms, 0, 0, -1, p, a);
- *
- * - a region with areas of 1MiB:
- *
- * dm_stats_create_region(dms, 0, 0, 1 << 11, p, a);
- *
- * - one 1MiB region starting at 1024 sectors with two areas:
- *
- * dm_stats_create_region(dms, 1024, 1 << 11, -2, p, a);
- *
- * If precise is non-zero attempt to create a region with nanosecond
- * precision counters using the kernel precise_timestamps feature.
- *
- * precise - A flag to request nanosecond precision counters
- * to be used for this region.
- *
- * histogram_bounds - specify the boundaries of a latency histogram to
- * be tracked for the region. The values are expressed as an array of
- * uint64_t terminated with a zero. Values must be in order of ascending
- * magnitude and specify the upper bounds of successive histogram bins
- * in nanoseconds (with an implicit lower bound of zero on the first bin
- * and an implicit upper bound of infinity on the final bin). For
- * example:
- *
- * uint64_t bounds_ary[] = { 1000, 2000, 3000, 0 };
- *
- * Specifies a histogram with four bins: 0-1000ns, 1000-2000ns,
- * 2000-3000ns and >3000ns.
- *
- * The smallest latency value that can be tracked for a region not using
- * precise_timestamps is 1ms: attempting to create a region with
- * histogram boundaries < 1ms will cause the precise_timestamps feature
- * to be enabled for that region automatically if it was not requested
- * explicitly.
- *
- * program_id is an optional string argument that identifies the
- * program creating the region. If program_id is NULL or the empty
- * string the default program_id stored in the handle will be used.
- *
- * user_data is an optional string argument that is added to the
- * content of the aux_data field stored with the statistics region by
- * the kernel.
- *
- * The library may also use this space internally, for example, to
- * store a group descriptor or other metadata: in this case the
- * library will strip any internal data fields from the value before
- * it is returned via a call to dm_stats_get_region_aux_data().
- *
- * The user data stored is not accessed by the library or kernel and
- * may be used to store an arbitrary data word (embedded whitespace is
- * not permitted).
- *
- * An application using both the library and direct access to the
- * @stats_list device-mapper message may see the internal values stored
- * in this field by the library. In such cases any string up to and
- * including the first '#' in the field must be treated as an opaque
- * value and preserved across any external modification of aux_data.
- *
- * The region_id of the newly-created region is returned in *region_id
- * if it is non-NULL.
- */
-int dm_stats_create_region(struct dm_stats *dms, uint64_t *region_id,
- uint64_t start, uint64_t len, int64_t step,
- int precise, struct dm_histogram *bounds,
- const char *program_id, const char *user_data);
-
-/*
- * Delete the specified statistics region. This will also mark the
- * region as not-present and discard any existing statistics data.
- */
-int dm_stats_delete_region(struct dm_stats *dms, uint64_t region_id);
-
-/*
- * Clear the specified statistics region. This requests the kernel to
- * zero all counter values (except in-flight I/O). Note that this
- * operation is not atomic with respect to reads of the counters; any IO
- * events occurring between the last print operation and the clear will
- * be lost. This can be avoided by using the atomic print-and-clear
- * function of the dm_stats_print_region() call or by using the higher
- * level dm_stats_populate() interface.
- */
-int dm_stats_clear_region(struct dm_stats *dms, uint64_t region_id);
-
-/*
- * Print the current counter values for the specified statistics region
- * and return them as a string. The memory for the string buffer will
- * be allocated from the dm_stats handle's private pool and should be
- * returned by calling dm_stats_buffer_destroy() when no longer
- * required. The pointer will become invalid following any call that
- * clears or reinitializes the handle (destroy, list, populate, bind).
- *
- * This allows applications that wish to access the raw message response
- * to obtain it via a dm_stats handle; no parsing of the textual counter
- * data is carried out by this function.
- *
- * Most users are recommended to use the dm_stats_populate() call
- * instead since this will automatically parse the statistics data into
- * numeric form accessible via the dm_stats_get_*() counter access
- * methods.
- *
- * A subset of the data lines may be requested by setting the
- * start_line and num_lines parameters. If both are zero all data
- * lines are returned.
- *
- * If the clear parameter is non-zero the operation will also
- * atomically reset all counter values to zero (except in-flight IO).
- */
-char *dm_stats_print_region(struct dm_stats *dms, uint64_t region_id,
- unsigned start_line, unsigned num_lines,
- unsigned clear);
-
-/*
- * Destroy a statistics response buffer obtained from a call to
- * dm_stats_print_region().
- */
-void dm_stats_buffer_destroy(struct dm_stats *dms, char *buffer);
-
-/*
- * Determine the number of regions contained in a dm_stats handle
- * following a dm_stats_list() or dm_stats_populate() call.
- *
- * The value returned is the number of registered regions visible with the
- * progam_id value used for the list or populate operation and may not be
- * equal to the highest present region_id (either due to program_id
- * filtering or gaps in the sequence of region_id values).
- *
- * Always returns zero on an empty handle.
- */
-uint64_t dm_stats_get_nr_regions(const struct dm_stats *dms);
-
-/*
- * Determine the number of groups contained in a dm_stats handle
- * following a dm_stats_list() or dm_stats_populate() call.
- *
- * The value returned is the number of registered groups visible with the
- * progam_id value used for the list or populate operation and may not be
- * equal to the highest present group_id (either due to program_id
- * filtering or gaps in the sequence of group_id values).
- *
- * Always returns zero on an empty handle.
- */
-uint64_t dm_stats_get_nr_groups(const struct dm_stats *dms);
-
-/*
- * Test whether region_id is present in this dm_stats handle.
- */
-int dm_stats_region_present(const struct dm_stats *dms, uint64_t region_id);
-
-/*
- * Returns the number of areas (counter sets) contained in the specified
- * region_id of the supplied dm_stats handle.
- */
-uint64_t dm_stats_get_region_nr_areas(const struct dm_stats *dms,
- uint64_t region_id);
-
-/*
- * Returns the total number of areas (counter sets) in all regions of the
- * given dm_stats object.
- */
-uint64_t dm_stats_get_nr_areas(const struct dm_stats *dms);
-
-/*
- * Test whether group_id is present in this dm_stats handle.
- */
-int dm_stats_group_present(const struct dm_stats *dms, uint64_t group_id);
-
-/*
- * Return the number of bins in the histogram configuration for the
- * specified region or zero if no histogram specification is configured.
- * Valid following a dm_stats_list() or dm_stats_populate() operation.
- */
-int dm_stats_get_region_nr_histogram_bins(const struct dm_stats *dms,
- uint64_t region_id);
-
-/*
- * Parse a histogram string with optional unit suffixes into a
- * dm_histogram bounds description.
- *
- * A histogram string is a string of numbers "n1,n2,n3,..." that
- * represent the boundaries of a histogram. The first and final bins
- * have implicit lower and upper bounds of zero and infinity
- * respectively and boundary values must occur in order of ascending
- * magnitude. Unless a unit suffix is given all values are specified in
- * nanoseconds.
- *
- * For example, if bounds_str="300,600,900", the region will be created
- * with a histogram containing four bins. Each report will include four
- * numbers a:b:c:d. a is the number of requests that took between 0 and
- * 300ns to complete, b is the number of requests that took 300-600ns to
- * complete, c is the number of requests that took 600-900ns to complete
- * and d is the number of requests that took more than 900ns to
- * complete.
- *
- * An optional unit suffix of 's', 'ms', 'us', or 'ns' may be used to
- * specify units of seconds, miliseconds, microseconds, or nanoseconds:
- *
- * bounds_str="1ns,1us,1ms,1s"
- * bounds_str="500us,1ms,1500us,2ms"
- * bounds_str="200ms,400ms,600ms,800ms,1s"
- *
- * The smallest valid unit of time for a histogram specification depends
- * on whether the region uses precise timestamps: for a region with the
- * default milisecond precision the smallest possible histogram boundary
- * magnitude is one milisecond: attempting to use a histogram with a
- * boundary less than one milisecond when creating a region will cause
- * the region to be created with the precise_timestamps feature enabled.
- *
- * On sucess a pointer to the struct dm_histogram representing the
- * bounds values is returned, or NULL in the case of error. The returned
- * pointer should be freed using free() when no longer required.
- */
-struct dm_histogram *dm_histogram_bounds_from_string(const char *bounds_str);
-
-/*
- * Parse a zero terminated array of uint64_t into a dm_histogram bounds
- * description.
- *
- * Each value in the array specifies the upper bound of a bin in the
- * latency histogram in nanoseconds. Values must appear in ascending
- * order of magnitude.
- *
- * The smallest valid unit of time for a histogram specification depends
- * on whether the region uses precise timestamps: for a region with the
- * default milisecond precision the smallest possible histogram boundary
- * magnitude is one milisecond: attempting to use a histogram with a
- * boundary less than one milisecond when creating a region will cause
- * the region to be created with the precise_timestamps feature enabled.
- */
-struct dm_histogram *dm_histogram_bounds_from_uint64(const uint64_t *bounds);
-
-/*
- * Destroy the histogram bounds array obtained from a call to
- * dm_histogram_bounds_from_string().
- */
-void dm_histogram_bounds_destroy(struct dm_histogram *bounds);
-
-/*
- * Destroy a dm_stats object and all associated regions, counter
- * sets and histograms.
- */
-void dm_stats_destroy(struct dm_stats *dms);
-
-/*
- * Counter sampling interval
- */
-
-/*
- * Set the sampling interval for counter data to the specified value in
- * either nanoseconds or milliseconds.
- *
- * The interval is used to calculate time-based metrics from the basic
- * counter data: an interval must be set before calling any of the
- * metric methods.
- *
- * For best accuracy the duration should be measured and updated at the
- * end of each interval.
- *
- * All values are stored internally with nanosecond precision and are
- * converted to or from ms when the millisecond interfaces are used.
- */
-void dm_stats_set_sampling_interval_ns(struct dm_stats *dms,
- uint64_t interval_ns);
-
-void dm_stats_set_sampling_interval_ms(struct dm_stats *dms,
- uint64_t interval_ms);
-
-/*
- * Retrieve the configured sampling interval in either nanoseconds or
- * milliseconds.
- */
-uint64_t dm_stats_get_sampling_interval_ns(const struct dm_stats *dms);
-uint64_t dm_stats_get_sampling_interval_ms(const struct dm_stats *dms);
-
-/*
- * Override program_id. This may be used to change the default
- * program_id value for an existing handle. If the allow_empty argument
- * is non-zero a NULL or empty program_id is permitted.
- *
- * Use with caution! Most users of the library should set a valid,
- * non-NULL program_id for every statistics region created. Failing to
- * do so may result in confusing state when multiple programs are
- * creating and managing statistics regions.
- *
- * All users of the library are encouraged to choose an unambiguous,
- * unique program_id: this could be based on PID (for programs that
- * create, report, and delete regions in a single process), session id,
- * executable name, or some other distinguishing string.
- *
- * Use of the empty string as a program_id does not simplify use of the
- * library or the command line tools and use of this value is strongly
- * discouraged.
- */
-int dm_stats_set_program_id(struct dm_stats *dms, int allow_empty,
- const char *program_id);
-
-/*
- * Region properties: size, length & area_len.
- *
- * Region start and length are returned in units of 512b as specified
- * at region creation time. The area_len value gives the size of areas
- * into which the region has been subdivided. For regions with a single
- * area spanning the range this value is equal to the region length.
- *
- * For regions created with a specified number of areas the value
- * represents the size of the areas into which the kernel divided the
- * region excluding any rounding of the last area size. The number of
- * areas may be obtained using the dm_stats_nr_areas_region() call.
- *
- * All values are returned in units of 512b sectors.
- */
-int dm_stats_get_region_start(const struct dm_stats *dms, uint64_t *start,
- uint64_t region_id);
-
-int dm_stats_get_region_len(const struct dm_stats *dms, uint64_t *len,
- uint64_t region_id);
-
-int dm_stats_get_region_area_len(const struct dm_stats *dms,
- uint64_t *len, uint64_t region_id);
-
-/*
- * Area properties: start, offset and length.
- *
- * The area length is always equal to the area length of the region
- * that contains it and is obtained from dm_stats_get_region_area_len().
- *
- * The start of an area is a function of the area_id and the containing
- * region's start and area length: it gives the absolute offset into the
- * containing device of the beginning of the area.
- *
- * The offset expresses the area's relative offset into the current
- * region. I.e. the area start minus the start offset of the containing
- * region.
- *
- * All values are returned in units of 512b sectors.
- */
-int dm_stats_get_area_start(const struct dm_stats *dms, uint64_t *start,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_area_offset(const struct dm_stats *dms, uint64_t *offset,
- uint64_t region_id, uint64_t area_id);
-
-/*
- * Retrieve program_id and user aux_data for a specific region.
- *
- * Only valid following a call to dm_stats_list().
- */
-
-/*
- * Retrieve program_id for the specified region.
- *
- * The returned pointer does not need to be freed separately from the
- * dm_stats handle but will become invalid after a dm_stats_destroy(),
- * dm_stats_list(), dm_stats_populate(), or dm_stats_bind*() of the
- * handle from which it was obtained.
- */
-const char *dm_stats_get_region_program_id(const struct dm_stats *dms,
- uint64_t region_id);
-
-/*
- * Retrieve user aux_data set for the specified region. This function
- * will return any stored user aux_data as a string in the memory
- * pointed to by the aux_data argument.
- *
- * Any library internal aux_data fields, such as DMS_GROUP descriptors,
- * are stripped before the value is returned.
- *
- * The returned pointer does not need to be freed separately from the
- * dm_stats handle but will become invalid after a dm_stats_destroy(),
- * dm_stats_list(), dm_stats_populate(), or dm_stats_bind*() of the
- * handle from which it was obtained.
- */
-const char *dm_stats_get_region_aux_data(const struct dm_stats *dms,
- uint64_t region_id);
-
-typedef enum {
- DM_STATS_OBJECT_TYPE_NONE,
- DM_STATS_OBJECT_TYPE_AREA,
- DM_STATS_OBJECT_TYPE_REGION,
- DM_STATS_OBJECT_TYPE_GROUP
-} dm_stats_obj_type_t;
-
-/*
- * Statistics cursor
- *
- * A dm_stats handle maintains an optional cursor into the statistics
- * tables that it stores. Iterators are provided to visit each region,
- * area, or group in a handle and accessor methods are provided to
- * obtain properties and values for the object at the current cursor
- * position.
- *
- * Using the cursor simplifies walking all regions or groups when
- * the tables are sparse (i.e. contains some present and some
- * non-present region_id or group_id values either due to program_id
- * filtering or the ordering of region and group creation and deletion).
- *
- * Simple macros are provided to visit each area, region, or group,
- * contained in a handle and applications are encouraged to use these
- * where possible.
- */
-
-/*
- * Walk flags are used to initialise a dm_stats handle's cursor control
- * and to select region or group aggregation when calling a metric or
- * counter property method with immediate group, region, and area ID
- * values.
- *
- * Walk flags are stored in the uppermost word of a uint64_t so that
- * a region_id or group_id may be encoded in the lower bits. This
- * allows an aggregate region_id or group_id to be specified when
- * retrieving counter or metric values.
- *
- * Flags may be ORred together when used to initialise a dm_stats_walk:
- * the resulting walk will visit instance of each type specified by
- * the flag combination.
- */
-#define DM_STATS_WALK_AREA 0x1000000000000ULL
-#define DM_STATS_WALK_REGION 0x2000000000000ULL
-#define DM_STATS_WALK_GROUP 0x4000000000000ULL
-
-#define DM_STATS_WALK_ALL 0x7000000000000ULL
-#define DM_STATS_WALK_DEFAULT (DM_STATS_WALK_AREA | DM_STATS_WALK_REGION)
-
-/*
- * Skip regions from a DM_STATS_WALK_REGION that contain only a single
- * area: in this case the region's aggregate values are identical to
- * the values of the single contained area. Setting this flag will
- * suppress these duplicate entries during a dm_stats_walk_* with the
- * DM_STATS_WALK_REGION flag set.
- */
-#define DM_STATS_WALK_SKIP_SINGLE_AREA 0x8000000000000ULL
-
-/*
- * Initialise the cursor control of a dm_stats handle for the specified
- * walk type(s). Including a walk flag in the flags argument will cause
- * any subsequent walk to visit that type of object (until the next
- * call to dm_stats_walk_init()).
- */
-int dm_stats_walk_init(struct dm_stats *dms, uint64_t flags);
-
-/*
- * Set the cursor of a dm_stats handle to address the first present
- * group, region, or area of the currently configured walk. It is
- * valid to attempt to walk a NULL stats handle or a handle containing
- * no present regions; in this case any call to dm_stats_walk_next()
- * becomes a no-op and all calls to dm_stats_walk_end() return true.
- */
-void dm_stats_walk_start(struct dm_stats *dms);
-
-/*
- * Advance the statistics cursor to the next area, or to the next
- * present region if at the end of the current region. If the end of
- * the region, area, or group tables is reached a subsequent call to
- * dm_stats_walk_end() will return 1 and dm_stats_object_type() called
- * on the location will return DM_STATS_OBJECT_TYPE_NONE,
- */
-void dm_stats_walk_next(struct dm_stats *dms);
-
-/*
- * Force the statistics cursor to advance to the next region. This will
- * stop any in-progress area walk (by clearing DM_STATS_WALK_AREA) and
- * advance the cursor to the next present region, the first present
- * group (if DM_STATS_GROUP_WALK is set), or to the end. In this case a
- * subsequent call to dm_stats_walk_end() will return 1 and a call to
- * dm_stats_object_type() for the location will return
- * DM_STATS_OBJECT_TYPE_NONE.
- */
-void dm_stats_walk_next_region(struct dm_stats *dms);
-
-/*
- * Test whether the end of a statistics walk has been reached.
- */
-int dm_stats_walk_end(struct dm_stats *dms);
-
-/*
- * Return the type of object at the location specified by region_id
- * and area_id. If either region_id or area_id uses one of the special
- * values DM_STATS_REGION_CURRENT or DM_STATS_AREA_CURRENT the
- * corresponding region or area identifier will be taken from the
- * current cursor location. If the cursor location or the value encoded
- * by region_id and area_id indicates an aggregate region or group,
- * this will be reflected in the value returned.
- */
-dm_stats_obj_type_t dm_stats_object_type(const struct dm_stats *dms,
- uint64_t region_id,
- uint64_t area_id);
-
-/*
- * Return the type of object at the current stats cursor location.
- */
-dm_stats_obj_type_t dm_stats_current_object_type(const struct dm_stats *dms);
-
-/*
- * Stats iterators
- *
- * C 'for' and 'do'/'while' style iterators for dm_stats data.
- *
- * It is not safe to call any function that modifies the region table
- * within the loop body (i.e. dm_stats_list(), dm_stats_populate(),
- * dm_stats_init(), or dm_stats_destroy()).
- *
- * All counter and property (dm_stats_get_*) access methods, as well as
- * dm_stats_populate_region() can be safely called from loops.
- *
- */
-
-/*
- * Iterate over the regions table visiting each region.
- *
- * If the region table is empty or unpopulated the loop body will not be
- * executed.
- */
-#define dm_stats_foreach_region(dms) \
-for (dm_stats_walk_init((dms), DM_STATS_WALK_REGION), \
- dm_stats_walk_start((dms)); \
- !dm_stats_walk_end((dms)); dm_stats_walk_next_region((dms)))
-
-/*
- * Iterate over the regions table visiting each area.
- *
- * If the region table is empty or unpopulated the loop body will not
- * be executed.
- */
-#define dm_stats_foreach_area(dms) \
-for (dm_stats_walk_init((dms), DM_STATS_WALK_AREA), \
- dm_stats_walk_start((dms)); \
- !dm_stats_walk_end((dms)); dm_stats_walk_next((dms)))
-
-/*
- * Iterate over the regions table visiting each group. Metric and
- * counter methods will return values for the group.
- *
- * If the group table is empty or unpopulated the loop body will not
- * be executed.
- */
-#define dm_stats_foreach_group(dms) \
-for (dm_stats_walk_init((dms), DM_STATS_WALK_GROUP), \
- dm_stats_walk_start(dms); \
- !dm_stats_walk_end(dms); \
- dm_stats_walk_next(dms))
-
-/*
- * Start a walk iterating over the regions contained in dm_stats handle
- * 'dms'.
- *
- * The body of the loop should call dm_stats_walk_next() or
- * dm_stats_walk_next_region() to advance to the next element.
- *
- * The loop body is executed at least once even if the stats handle is
- * empty.
- */
-#define dm_stats_walk_do(dms) \
-do { \
- dm_stats_walk_start((dms)); \
- do
-
-/*
- * Start a 'while' style loop or end a 'do..while' loop iterating over the
- * regions contained in dm_stats handle 'dms'.
- */
-#define dm_stats_walk_while(dms) \
- while(!dm_stats_walk_end((dms))); \
-} while (0)
-
-/*
- * Cursor relative property methods
- *
- * Calls with the prefix dm_stats_get_current_* operate relative to the
- * current cursor location, returning properties for the current region
- * or area of the supplied dm_stats handle.
- *
- */
-
-/*
- * Returns the number of areas (counter sets) contained in the current
- * region of the supplied dm_stats handle.
- */
-uint64_t dm_stats_get_current_nr_areas(const struct dm_stats *dms);
-
-/*
- * Retrieve the current values of the stats cursor.
- */
-uint64_t dm_stats_get_current_region(const struct dm_stats *dms);
-uint64_t dm_stats_get_current_area(const struct dm_stats *dms);
-
-/*
- * Current region properties: size, length & area_len.
- *
- * See the comments for the equivalent dm_stats_get_* versions for a
- * complete description of these methods.
- *
- * All values are returned in units of 512b sectors.
- */
-int dm_stats_get_current_region_start(const struct dm_stats *dms,
- uint64_t *start);
-
-int dm_stats_get_current_region_len(const struct dm_stats *dms,
- uint64_t *len);
-
-int dm_stats_get_current_region_area_len(const struct dm_stats *dms,
- uint64_t *area_len);
-
-/*
- * Current area properties: start and length.
- *
- * See the comments for the equivalent dm_stats_get_* versions for a
- * complete description of these methods.
- *
- * All values are returned in units of 512b sectors.
- */
-int dm_stats_get_current_area_start(const struct dm_stats *dms,
- uint64_t *start);
-
-int dm_stats_get_current_area_offset(const struct dm_stats *dms,
- uint64_t *offset);
-
-int dm_stats_get_current_area_len(const struct dm_stats *dms,
- uint64_t *start);
-
-/*
- * Return a pointer to the program_id string for region at the current
- * cursor location.
- */
-const char *dm_stats_get_current_region_program_id(const struct dm_stats *dms);
-
-/*
- * Return a pointer to the user aux_data string for the region at the
- * current cursor location.
- */
-const char *dm_stats_get_current_region_aux_data(const struct dm_stats *dms);
-
-/*
- * Statistics groups and data aggregation.
- */
-
-/*
- * Create a new group in stats handle dms from the group descriptor
- * passed in group. The group descriptor is a string containing a list
- * of region_id values that will be included in the group. The first
- * region_id found will be the group leader. Ranges of identifiers may
- * be expressed as "M-N", where M and N are the start and end region_id
- * values for the range.
- */
-int dm_stats_create_group(struct dm_stats *dms, const char *group,
- const char *alias, uint64_t *group_id);
-
-/*
- * Remove the specified group_id. If the remove argument is zero the
- * group will be removed but the regions that it contained will remain.
- * If remove is non-zero then all regions that belong to the group will
- * also be removed.
- */
-int dm_stats_delete_group(struct dm_stats *dms, uint64_t group_id, int remove);
-
-/*
- * Set an alias for this group or region. The alias will be returned
- * instead of the normal dm-stats name for this region or group.
- */
-int dm_stats_set_alias(struct dm_stats *dms, uint64_t group_id,
- const char *alias);
-
-/*
- * Returns a pointer to the currently configured alias for id, or the
- * name of the dm device the handle is bound to if no alias has been
- * set. The pointer will be freed automatically when a new alias is set
- * or when the stats handle is cleared.
- */
-const char *dm_stats_get_alias(const struct dm_stats *dms, uint64_t id);
-
-#define DM_STATS_GROUP_NONE UINT64_MAX
-/*
- * Return the group_id that the specified region_id belongs to, or the
- * special value DM_STATS_GROUP_NONE if the region does not belong
- * to any group.
- */
-uint64_t dm_stats_get_group_id(const struct dm_stats *dms, uint64_t region_id);
-
-/*
- * Store a pointer to a string describing the regions that are members
- * of the group specified by group_id in the memory pointed to by buf.
- * The string is in the same format as the 'group' argument to
- * dm_stats_create_group().
- *
- * The pointer does not need to be freed explicitly by the caller: it
- * will become invalid following a subsequent dm_stats_list(),
- * dm_stats_populate() or dm_stats_destroy() of the corresponding
- * dm_stats handle.
- */
-int dm_stats_get_group_descriptor(const struct dm_stats *dms,
- uint64_t group_id, char **buf);
-
-/*
- * Create regions that correspond to the extents of a file in the
- * filesystem and optionally place them into a group.
- *
- * File descriptor fd must reference a regular file, open for reading,
- * in a local file system that supports the FIEMAP ioctl, and that
- * returns data describing the physical location of extents.
- *
- * The file descriptor can be closed by the caller following the call
- * to dm_stats_create_regions_from_fd().
- *
- * Unless nogroup is non-zero the regions will be placed into a group
- * and the group alias set to the value supplied (if alias is NULL no
- * group alias will be assigned).
- *
- * On success the function returns a pointer to an array of uint64_t
- * containing the IDs of the newly created regions. The region_id
- * array is terminated by the value DM_STATS_REGION_NOT_PRESENT and
- * should be freed using free() when no longer required.
- *
- * On error NULL is returned.
- *
- * Following a call to dm_stats_create_regions_from_fd() the handle
- * is guaranteed to be in a listed state, and to contain any region
- * and group identifiers created by the operation.
- *
- * The group_id for the new group is equal to the region_id value in
- * the first array element.
- */
-uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
- int group, int precise,
- struct dm_histogram *bounds,
- const char *alias);
-/*
- * Update a group of regions that correspond to the extents of a file
- * in the filesystem, adding and removing regions to account for
- * allocation changes in the underlying file.
- *
- * File descriptor fd must reference a regular file, open for reading,
- * in a local file system that supports the FIEMAP ioctl, and that
- * returns data describing the physical location of extents.
- *
- * The file descriptor can be closed by the caller following the call
- * to dm_stats_update_regions_from_fd().
- *
- * On success the function returns a pointer to an array of uint64_t
- * containing the IDs of the updated regions (including any existing
- * regions that were not modified by the call).
- *
- * The region_id array is terminated by the special value
- * DM_STATS_REGION_NOT_PRESENT and should be freed using free()
- * when no longer required.
- *
- * On error NULL is returned.
- *
- * Following a call to dm_stats_update_regions_from_fd() the handle
- * is guaranteed to be in a listed state, and to contain any region
- * and group identifiers created by the operation.
- *
- * This function cannot be used with file mapped regions that are
- * not members of a group: either group the regions, or remove them
- * and re-map them with dm_stats_create_regions_from_fd().
- */
-uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
- uint64_t group_id);
-
-
-/*
- * The file map monitoring daemon can monitor files in two distinct
- * ways: the mode affects the behaviour of the daemon when a file
- * under monitoring is renamed or unlinked, and the conditions which
- * cause the daemon to terminate.
- *
- * In both modes, the daemon will always shut down when the group
- * being monitored is deleted.
- *
- * Follow inode:
- * The daemon follows the inode of the file, as it was at the time the
- * daemon started. The file descriptor referencing the file is kept
- * open at all times, and the daemon will exit when it detects that
- * the file has been unlinked and it is the last holder of a reference
- * to the file.
- *
- * This mode is useful if the file is expected to be renamed, or moved
- * within the file system, while it is being monitored.
- *
- * Follow path:
- * The daemon follows the path that was given on the daemon command
- * line. The file descriptor referencing the file is re-opened on each
- * iteration of the daemon, and the daemon will exit if no file exists
- * at this location (a tolerance is allowed so that a brief delay
- * between unlink() and creat() is permitted).
- *
- * This mode is useful if the file is updated by unlinking the original
- * and placing a new file at the same path.
- */
-
-typedef enum {
- DM_FILEMAPD_FOLLOW_INODE,
- DM_FILEMAPD_FOLLOW_PATH,
- DM_FILEMAPD_FOLLOW_NONE
-} dm_filemapd_mode_t;
-
-/*
- * Parse a string representation of a dmfilemapd mode.
- *
- * Returns a valid dm_filemapd_mode_t value on success, or
- * DM_FILEMAPD_FOLLOW_NONE on error.
- */
-dm_filemapd_mode_t dm_filemapd_mode_from_string(const char *mode_str);
-
-/*
- * Start the dmfilemapd filemap monitoring daemon for the specified
- * file descriptor, group, and file system path. The daemon will
- * monitor the file for allocation changes, and when a change is
- * detected, call dm_stats_update_regions_from_fd() to update the
- * mapped regions for the file.
- *
- * The path provided to dm_stats_start_filemapd() must be an absolute
- * path, and should reflect the path of 'fd' at the time that it was
- * opened.
- *
- * The mode parameter controls the behaviour of the daemon when the
- * file being monitored is unlinked or moved: see the comments for
- * dm_filemapd_mode_t for a full description and possible values.
- *
- * The daemon can be stopped at any time by sending SIGTERM to the
- * daemon pid.
- */
-int dm_stats_start_filemapd(int fd, uint64_t group_id, const char *path,
- dm_filemapd_mode_t mode, unsigned foreground,
- unsigned verbose);
-
-/*
* Call this to actually run the ioctl.
*/
int dm_task_run(struct dm_task *dmt);
@@ -2840,320 +1840,6 @@ int dm_report_group_pop(struct dm_report_group *group);
int dm_report_group_output_and_pop_all(struct dm_report_group *group);
int dm_report_group_destroy(struct dm_report_group *group);
-/*
- * Stats counter access methods
- *
- * Each method returns the corresponding stats counter value from the
- * supplied dm_stats handle for the specified region_id and area_id.
- * If either region_id or area_id uses one of the special values
- * DM_STATS_REGION_CURRENT or DM_STATS_AREA_CURRENT then the region
- * or area is selected according to the current state of the dm_stats
- * handle's embedded cursor.
- *
- * Two methods are provided to access counter values: a named function
- * for each available counter field and a single function that accepts
- * an enum value specifying the required field. New code is encouraged
- * to use the enum based interface as calls to the named functions are
- * implemented using the enum method internally.
- *
- * See the kernel documentation for complete descriptions of each
- * counter field:
- *
- * Documentation/device-mapper/statistics.txt
- * Documentation/iostats.txt
- *
- * reads: the number of reads completed
- * reads_merged: the number of reads merged
- * read_sectors: the number of sectors read
- * read_nsecs: the number of nanoseconds spent reading
- * writes: the number of writes completed
- * writes_merged: the number of writes merged
- * write_sectors: the number of sectors written
- * write_nsecs: the number of nanoseconds spent writing
- * io_in_progress: the number of I/Os currently in progress
- * io_nsecs: the number of nanoseconds spent doing I/Os
- * weighted_io_nsecs: the weighted number of nanoseconds spent doing I/Os
- * total_read_nsecs: the total time spent reading in nanoseconds
- * total_write_nsecs: the total time spent writing in nanoseconds
- */
-
-#define DM_STATS_REGION_CURRENT UINT64_MAX
-#define DM_STATS_AREA_CURRENT UINT64_MAX
-
-typedef enum {
- DM_STATS_READS_COUNT,
- DM_STATS_READS_MERGED_COUNT,
- DM_STATS_READ_SECTORS_COUNT,
- DM_STATS_READ_NSECS,
- DM_STATS_WRITES_COUNT,
- DM_STATS_WRITES_MERGED_COUNT,
- DM_STATS_WRITE_SECTORS_COUNT,
- DM_STATS_WRITE_NSECS,
- DM_STATS_IO_IN_PROGRESS_COUNT,
- DM_STATS_IO_NSECS,
- DM_STATS_WEIGHTED_IO_NSECS,
- DM_STATS_TOTAL_READ_NSECS,
- DM_STATS_TOTAL_WRITE_NSECS,
- DM_STATS_NR_COUNTERS
-} dm_stats_counter_t;
-
-uint64_t dm_stats_get_counter(const struct dm_stats *dms,
- dm_stats_counter_t counter,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_reads(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_reads_merged(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_read_sectors(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_read_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_writes(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_writes_merged(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_write_sectors(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_write_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_io_in_progress(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_io_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_weighted_io_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_total_read_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-uint64_t dm_stats_get_total_write_nsecs(const struct dm_stats *dms,
- uint64_t region_id, uint64_t area_id);
-
-/*
- * Derived statistics access methods
- *
- * Each method returns the corresponding value calculated from the
- * counters stored in the supplied dm_stats handle for the specified
- * region_id and area_id. If either region_id or area_id uses one of the
- * special values DM_STATS_REGION_CURRENT or DM_STATS_AREA_CURRENT then
- * the region or area is selected according to the current state of the
- * dm_stats handle's embedded cursor.
- *
- * The set of metrics is based on the fields provided by the Linux
- * iostats program.
- *
- * rd_merges_per_sec: the number of reads merged per second
- * wr_merges_per_sec: the number of writes merged per second
- * reads_per_sec: the number of reads completed per second
- * writes_per_sec: the number of writes completed per second
- * read_sectors_per_sec: the number of sectors read per second
- * write_sectors_per_sec: the number of sectors written per second
- * average_request_size: the average size of requests submitted
- * service_time: the average service time (in ns) for requests issued
- * average_queue_size: the average queue length
- * average_wait_time: the average time for requests to be served (in ns)
- * average_rd_wait_time: the average read wait time
- * average_wr_wait_time: the average write wait time
- */
-
-typedef enum {
- DM_STATS_RD_MERGES_PER_SEC,
- DM_STATS_WR_MERGES_PER_SEC,
- DM_STATS_READS_PER_SEC,
- DM_STATS_WRITES_PER_SEC,
- DM_STATS_READ_SECTORS_PER_SEC,
- DM_STATS_WRITE_SECTORS_PER_SEC,
- DM_STATS_AVERAGE_REQUEST_SIZE,
- DM_STATS_AVERAGE_QUEUE_SIZE,
- DM_STATS_AVERAGE_WAIT_TIME,
- DM_STATS_AVERAGE_RD_WAIT_TIME,
- DM_STATS_AVERAGE_WR_WAIT_TIME,
- DM_STATS_SERVICE_TIME,
- DM_STATS_THROUGHPUT,
- DM_STATS_UTILIZATION,
- DM_STATS_NR_METRICS
-} dm_stats_metric_t;
-
-int dm_stats_get_metric(const struct dm_stats *dms, int metric,
- uint64_t region_id, uint64_t area_id, double *value);
-
-int dm_stats_get_rd_merges_per_sec(const struct dm_stats *dms, double *rrqm,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_wr_merges_per_sec(const struct dm_stats *dms, double *rrqm,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_reads_per_sec(const struct dm_stats *dms, double *rd_s,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_writes_per_sec(const struct dm_stats *dms, double *wr_s,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_read_sectors_per_sec(const struct dm_stats *dms,
- double *rsec_s, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_write_sectors_per_sec(const struct dm_stats *dms,
- double *wr_s, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_average_request_size(const struct dm_stats *dms,
- double *arqsz, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_service_time(const struct dm_stats *dms, double *svctm,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_average_queue_size(const struct dm_stats *dms, double *qusz,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_average_wait_time(const struct dm_stats *dms, double *await,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_average_rd_wait_time(const struct dm_stats *dms,
- double *await, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_average_wr_wait_time(const struct dm_stats *dms,
- double *await, uint64_t region_id,
- uint64_t area_id);
-
-int dm_stats_get_throughput(const struct dm_stats *dms, double *tput,
- uint64_t region_id, uint64_t area_id);
-
-int dm_stats_get_utilization(const struct dm_stats *dms, dm_percent_t *util,
- uint64_t region_id, uint64_t area_id);
-
-/*
- * Statistics histogram access methods.
- *
- * Methods to access latency histograms for regions that have them
- * enabled. Each histogram contains a configurable number of bins
- * spanning a user defined latency interval.
- *
- * The bin count, upper and lower bin bounds, and bin values are
- * made available via the following area methods.
- *
- * Methods to obtain a simple string representation of the histogram
- * and its bounds are also provided.
- */
-
-/*
- * Retrieve a pointer to the histogram associated with the specified
- * area. If the area does not have a histogram configured this function
- * returns NULL.
- *
- * The pointer does not need to be freed explicitly by the caller: it
- * will become invalid following a subsequent dm_stats_list(),
- * dm_stats_populate() or dm_stats_destroy() of the corresponding
- * dm_stats handle.
- *
- * If region_id or area_id is one of the special values
- * DM_STATS_REGION_CURRENT or DM_STATS_AREA_CURRENT the current cursor
- * value is used to select the region or area.
- */
-struct dm_histogram *dm_stats_get_histogram(const struct dm_stats *dms,
- uint64_t region_id,
- uint64_t area_id);
-
-/*
- * Return the number of bins in the specified histogram handle.
- */
-int dm_histogram_get_nr_bins(const struct dm_histogram *dmh);
-
-/*
- * Get the lower bound of the specified bin of the histogram for the
- * area specified by region_id and area_id. The value is returned in
- * nanoseconds.
- */
-uint64_t dm_histogram_get_bin_lower(const struct dm_histogram *dmh, int bin);
-
-/*
- * Get the upper bound of the specified bin of the histogram for the
- * area specified by region_id and area_id. The value is returned in
- * nanoseconds.
- */
-uint64_t dm_histogram_get_bin_upper(const struct dm_histogram *dmh, int bin);
-
-/*
- * Get the width of the specified bin of the histogram for the area
- * specified by region_id and area_id. The width is equal to the bin
- * upper bound minus the lower bound and yields the range of latency
- * values covered by this bin. The value is returned in nanoseconds.
- */
-uint64_t dm_histogram_get_bin_width(const struct dm_histogram *dmh, int bin);
-
-/*
- * Get the value of the specified bin of the histogram for the area
- * specified by region_id and area_id.
- */
-uint64_t dm_histogram_get_bin_count(const struct dm_histogram *dmh, int bin);
-
-/*
- * Get the percentage (relative frequency) of the specified bin of the
- * histogram for the area specified by region_id and area_id.
- */
-dm_percent_t dm_histogram_get_bin_percent(const struct dm_histogram *dmh,
- int bin);
-
-/*
- * Return the total observations (sum of bin counts) for the histogram
- * of the area specified by region_id and area_id.
- */
-uint64_t dm_histogram_get_sum(const struct dm_histogram *dmh);
-
-/*
- * Histogram formatting flags.
- */
-#define DM_HISTOGRAM_SUFFIX 0x1
-#define DM_HISTOGRAM_VALUES 0x2
-#define DM_HISTOGRAM_PERCENT 0X4
-#define DM_HISTOGRAM_BOUNDS_LOWER 0x10
-#define DM_HISTOGRAM_BOUNDS_UPPER 0x20
-#define DM_HISTOGRAM_BOUNDS_RANGE 0x30
-
-/*
- * Return a string representation of the supplied histogram's values and
- * bin boundaries.
- *
- * The bin argument selects the bin to format. If this argument is less
- * than zero all bins will be included in the resulting string.
- *
- * width specifies a minimum width for the field in characters; if it is
- * zero the width will be determined automatically based on the options
- * selected for formatting. A value less than zero disables field width
- * control: bin boundaries and values will be output with a minimum
- * amount of whitespace.
- *
- * flags is a collection of flag arguments that control the string format:
- *
- * DM_HISTOGRAM_VALUES - Include bin values in the string.
- * DM_HISTOGRAM_SUFFIX - Include time unit suffixes when printing bounds.
- * DM_HISTOGRAM_PERCENT - Format bin values as a percentage.
- *
- * DM_HISTOGRAM_BOUNDS_LOWER - Include the lower bound of each bin.
- * DM_HISTOGRAM_BOUNDS_UPPER - Include the upper bound of each bin.
- * DM_HISTOGRAM_BOUNDS_RANGE - Show the span of each bin as "lo-up".
- *
- * The returned pointer does not need to be freed explicitly by the
- * caller: it will become invalid following a subsequent
- * dm_stats_list(), dm_stats_populate() or dm_stats_destroy() of the
- * corresponding dm_stats handle.
- */
-const char *dm_histogram_to_string(const struct dm_histogram *dmh, int bin,
- int width, int flags);
-
/*************************
* config file parse/print
*************************/
diff --git a/device_mapper/libdm-stats.c b/device_mapper/libdm-stats.c
deleted file mode 100644
index 219d393..0000000
--- a/device_mapper/libdm-stats.c
+++ /dev/null
@@ -1,5096 +0,0 @@
-/*
- * Copyright (C) 2016 Red Hat, Inc. All rights reserved.
- *
- * _stats_get_extents_for_file() based in part on filefrag_fiemap() from
- * e2fsprogs/misc/filefrag.c. Copyright 2003 by Theodore Ts'o.
- *
- * This file is part of the device-mapper userspace tools.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU Lesser General Public License v.2.1.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "base/memory/zalloc.h"
-#include "misc/dmlib.h"
-#include "misc/kdev_t.h"
-
-#include "math.h" /* log10() */
-
-#include <sys/sysmacros.h>
-#include <sys/ioctl.h>
-#include <sys/vfs.h> /* fstatfs */
-#include <unistd.h>
-
-#ifdef __linux__
- #include <linux/fs.h> /* FS_IOC_FIEMAP */
-#endif
-
-#ifdef HAVE_LINUX_FIEMAP_H
- #include <linux/fiemap.h> /* fiemap */
-#endif
-
-#ifdef HAVE_LINUX_MAGIC_H
- #include <linux/magic.h> /* BTRFS_SUPER_MAGIC */
-#endif
-
-#define DM_STATS_REGION_NOT_PRESENT UINT64_MAX
-#define DM_STATS_GROUP_NOT_PRESENT DM_STATS_GROUP_NONE
-
-#define NSEC_PER_USEC 1000L
-#define NSEC_PER_MSEC 1000000L
-#define NSEC_PER_SEC 1000000000L
-
-#define PRECISE_ARG "precise_timestamps"
-#define HISTOGRAM_ARG "histogram:"
-
-#define STATS_ROW_BUF_LEN 4096
-#define STATS_MSG_BUF_LEN 1024
-#define STATS_FIE_BUF_LEN 2048
-
-#define SECTOR_SHIFT 9L
-
-/* Histogram bin */
-struct dm_histogram_bin {
- uint64_t upper; /* Upper bound on this bin. */
- uint64_t count; /* Count value for this bin. */
-};
-
-struct dm_histogram {
- /* The stats handle this histogram belongs to. */
- const struct dm_stats *dms;
- /* The region this histogram belongs to. */
- const struct dm_stats_region *region;
- uint64_t sum; /* Sum of histogram bin counts. */
- int nr_bins; /* Number of histogram bins assigned. */
- struct dm_histogram_bin bins[0];
-};
-
-/*
- * See Documentation/device-mapper/statistics.txt for full descriptions
- * of the device-mapper statistics counter fields.
- */
-struct dm_stats_counters {
- uint64_t reads; /* Num reads completed */
- uint64_t reads_merged; /* Num reads merged */
- uint64_t read_sectors; /* Num sectors read */
- uint64_t read_nsecs; /* Num milliseconds spent reading */
- uint64_t writes; /* Num writes completed */
- uint64_t writes_merged; /* Num writes merged */
- uint64_t write_sectors; /* Num sectors written */
- uint64_t write_nsecs; /* Num milliseconds spent writing */
- uint64_t io_in_progress; /* Num I/Os currently in progress */
- uint64_t io_nsecs; /* Num milliseconds spent doing I/Os */
- uint64_t weighted_io_nsecs; /* Weighted num milliseconds doing I/Os */
- uint64_t total_read_nsecs; /* Total time spent reading in milliseconds */
- uint64_t total_write_nsecs; /* Total time spent writing in milliseconds */
- struct dm_histogram *histogram; /* Histogram. */
-};
-
-struct dm_stats_region {
- uint64_t region_id; /* as returned by @stats_list */
- uint64_t group_id;
- uint64_t start;
- uint64_t len;
- uint64_t step;
- char *program_id;
- char *aux_data;
- uint64_t timescale; /* precise_timestamps is per-region */
- struct dm_histogram *bounds; /* histogram configuration */
- struct dm_histogram *histogram; /* aggregate cache */
- struct dm_stats_counters *counters;
-};
-
-struct dm_stats_group {
- uint64_t group_id;
- const char *alias;
- dm_bitset_t regions;
- struct dm_histogram *histogram;
-};
-
-struct dm_stats {
- /* device binding */
- int bind_major; /* device major that this dm_stats object is bound to */
- int bind_minor; /* device minor that this dm_stats object is bound to */
- char *bind_name; /* device-mapper device name */
- char *bind_uuid; /* device-mapper UUID */
- char *program_id; /* default program_id for this handle */
- const char *name; /* cached device_name used for reporting */
- struct dm_pool *mem; /* memory pool for region and counter tables */
- struct dm_pool *hist_mem; /* separate pool for histogram tables */
- struct dm_pool *group_mem; /* separate pool for group tables */
- uint64_t nr_regions; /* total number of present regions */
- uint64_t max_region; /* size of the regions table */
- uint64_t interval_ns; /* sampling interval in nanoseconds */
- uint64_t timescale; /* default sample value multiplier */
- int precise; /* use precise_timestamps when creating regions */
- struct dm_stats_region *regions;
- struct dm_stats_group *groups;
- /* statistics cursor */
- uint64_t walk_flags; /* walk control flags */
- uint64_t cur_flags;
- uint64_t cur_group;
- uint64_t cur_region;
- uint64_t cur_area;
-};
-
-#define PROC_SELF_COMM "/proc/self/comm"
-static char *_program_id_from_proc(void)
-{
- FILE *comm = NULL;
- char buf[STATS_ROW_BUF_LEN];
-
- if (!(comm = fopen(PROC_SELF_COMM, "r")))
- return_NULL;
-
- if (!fgets(buf, sizeof(buf), comm)) {
- log_error("Could not read from %s", PROC_SELF_COMM);
- if (fclose(comm))
- stack;
- return NULL;
- }
-
- if (fclose(comm))
- stack;
-
- return strdup(buf);
-}
-
-static uint64_t _nr_areas(uint64_t len, uint64_t step)
-{
- /* Default is one area. */
- if (!len || !step)
- return 1;
- /*
- * drivers/md/dm-stats.c::message_stats_create()
- * A region may be sub-divided into areas with their own counters.
- * Any partial area at the end of the region is treated as an
- * additional complete area.
- */
- return (len + step - 1) / step;
-}
-
-static uint64_t _nr_areas_region(struct dm_stats_region *region)
-{
- return _nr_areas(region->len, region->step);
-}
-
-struct dm_stats *dm_stats_create(const char *program_id)
-{
- size_t hist_hint = sizeof(struct dm_histogram_bin);
- size_t group_hint = sizeof(struct dm_stats_group);
- struct dm_stats *dms = NULL;
-
- if (!(dms = zalloc(sizeof(*dms))))
- return_NULL;
-
- /* FIXME: better hint. */
- if (!(dms->mem = dm_pool_create("stats_pool", 4096))) {
- free(dms);
- return_NULL;
- }
-
- if (!(dms->hist_mem = dm_pool_create("histogram_pool", hist_hint)))
- goto_bad;
-
- if (!(dms->group_mem = dm_pool_create("group_pool", group_hint)))
- goto_bad;
-
- if (!program_id || !strlen(program_id))
- dms->program_id = _program_id_from_proc();
- else
- dms->program_id = strdup(program_id);
-
- if (!dms->program_id) {
- log_error("Could not allocate memory for program_id");
- goto bad;
- }
-
- dms->bind_major = -1;
- dms->bind_minor = -1;
- dms->bind_name = NULL;
- dms->bind_uuid = NULL;
-
- dms->name = NULL;
-
- /* by default all regions use msec precision */
- dms->timescale = NSEC_PER_MSEC;
- dms->precise = 0;
-
- dms->nr_regions = DM_STATS_REGION_NOT_PRESENT;
- dms->max_region = DM_STATS_REGION_NOT_PRESENT;
- dms->regions = NULL;
-
- /* maintain compatibility with earlier walk version */
- dms->walk_flags = dms->cur_flags = DM_STATS_WALK_DEFAULT;
-
- return dms;
-
-bad:
- dm_pool_destroy(dms->mem);
- if (dms->hist_mem)
- dm_pool_destroy(dms->hist_mem);
- if (dms->group_mem)
- dm_pool_destroy(dms->group_mem);
- free(dms);
- return NULL;
-}
-
-/*
- * Test whether the stats region pointed to by region is present.
- */
-static int _stats_region_present(const struct dm_stats_region *region)
-{
- return !(region->region_id == DM_STATS_REGION_NOT_PRESENT);
-}
-
-/*
- * Test whether the stats group pointed to by group is present.
- */
-static int _stats_group_present(const struct dm_stats_group *group)
-{
- return !(group->group_id == DM_STATS_GROUP_NOT_PRESENT);
-}
-
-/*
- * Test whether a stats group id is present.
- */
-static int _stats_group_id_present(const struct dm_stats *dms, uint64_t id)
-{
- struct dm_stats_group *group = NULL;
-
- if (id == DM_STATS_GROUP_NOT_PRESENT)
- return 0;
-
- if (!dms)
- return_0;
-
- if (!dms->regions)
- return 0;
-
- if (id > dms->max_region)
- return 0;
-
- group = &dms->groups[id];
-
- return _stats_group_present(group);
-}
-
-/*
- * Test whether the given region_id is a member of any group.
- */
-static uint64_t _stats_region_is_grouped(const struct dm_stats* dms,
- uint64_t region_id)
-{
- uint64_t group_id;
-
- if (region_id == DM_STATS_GROUP_NOT_PRESENT)
- return 0;
-
- if (!_stats_region_present(&dms->regions[region_id]))
- return 0;
-
- group_id = dms->regions[region_id].group_id;
-
- return group_id != DM_STATS_GROUP_NOT_PRESENT;
-}
-
-static void _stats_histograms_destroy(struct dm_pool *mem,
- struct dm_stats_region *region)
-{
- /* Unpopulated handle. */
- if (!region->counters)
- return;
-
- /*
- * Free everything in the pool back to the first histogram.
- */
- if (region->counters[0].histogram)
- dm_pool_free(mem, region->counters[0].histogram);
-}
-
-static void _stats_region_destroy(struct dm_stats_region *region)
-{
- if (!_stats_region_present(region))
- return;
-
- region->start = region->len = region->step = 0;
- region->timescale = 0;
-
- /*
- * Don't free counters and histogram bounds here: they are
- * dropped from the pool along with the corresponding
- * regions table.
- *
- * The following objects are all allocated with malloc.
- */
-
- region->counters = NULL;
- region->bounds = NULL;
-
- free(region->program_id);
- region->program_id = NULL;
- free(region->aux_data);
- region->aux_data = NULL;
- region->region_id = DM_STATS_REGION_NOT_PRESENT;
-}
-
-static void _stats_regions_destroy(struct dm_stats *dms)
-{
- struct dm_pool *mem = dms->mem;
- uint64_t i;
-
- if (!dms->regions)
- return;
-
- /* walk backwards to obey pool order */
- for (i = dms->max_region; (i != DM_STATS_REGION_NOT_PRESENT); i--) {
- _stats_histograms_destroy(dms->hist_mem, &dms->regions[i]);
- _stats_region_destroy(&dms->regions[i]);
- }
-
- dm_pool_free(mem, dms->regions);
- dms->regions = NULL;
-}
-
-static void _stats_group_destroy(struct dm_stats_group *group)
-{
- if (!_stats_group_present(group))
- return;
-
- group->histogram = NULL;
-
- if (group->alias) {
- free((char *) group->alias);
- group->alias = NULL;
- }
- if (group->regions) {
- dm_bitset_destroy(group->regions);
- group->regions = NULL;
- }
- group->group_id = DM_STATS_GROUP_NOT_PRESENT;
-}
-
-static void _stats_groups_destroy(struct dm_stats *dms)
-{
- uint64_t i;
-
- if (!dms->groups)
- return;
-
- for (i = dms->max_region; (i != DM_STATS_REGION_NOT_PRESENT); i--)
- _stats_group_destroy(&dms->groups[i]);
- dm_pool_free(dms->group_mem, dms->groups);
- dms->groups = NULL;
-}
-
-static int _set_stats_device(struct dm_stats *dms, struct dm_task *dmt)
-{
- if (dms->bind_name)
- return dm_task_set_name(dmt, dms->bind_name);
- if (dms->bind_uuid)
- return dm_task_set_uuid(dmt, dms->bind_uuid);
- if (dms->bind_major > 0)
- return dm_task_set_major(dmt, dms->bind_major)
- && dm_task_set_minor(dmt, dms->bind_minor);
- return_0;
-}
-
-static int _stats_bound(const struct dm_stats *dms)
-{
- if (dms->bind_major > 0 || dms->bind_name || dms->bind_uuid)
- return 1;
- /* %p format specifier expects a void pointer. */
- log_error("Stats handle at %p is not bound.", dms);
- return 0;
-}
-
-static void _stats_clear_binding(struct dm_stats *dms)
-{
- if (dms->bind_name)
- dm_pool_free(dms->mem, dms->bind_name);
- if (dms->bind_uuid)
- dm_pool_free(dms->mem, dms->bind_uuid);
- free((char *) dms->name);
-
- dms->bind_name = dms->bind_uuid = NULL;
- dms->bind_major = dms->bind_minor = -1;
- dms->name = NULL;
-}
-
-int dm_stats_bind_devno(struct dm_stats *dms, int major, int minor)
-{
- _stats_clear_binding(dms);
- _stats_regions_destroy(dms);
- _stats_groups_destroy(dms);
-
- dms->bind_major = major;
- dms->bind_minor = minor;
-
- return 1;
-}
-
-int dm_stats_bind_name(struct dm_stats *dms, const char *name)
-{
- _stats_clear_binding(dms);
- _stats_regions_destroy(dms);
- _stats_groups_destroy(dms);
-
- if (!(dms->bind_name = dm_pool_strdup(dms->mem, name)))
- return_0;
-
- return 1;
-}
-
-int dm_stats_bind_uuid(struct dm_stats *dms, const char *uuid)
-{
- _stats_clear_binding(dms);
- _stats_regions_destroy(dms);
- _stats_groups_destroy(dms);
-
- if (!(dms->bind_uuid = dm_pool_strdup(dms->mem, uuid)))
- return_0;
-
- return 1;
-}
-
-int dm_stats_bind_from_fd(struct dm_stats *dms, int fd)
-{
- int major, minor;
- struct stat buf;
-
- if (fstat(fd, &buf)) {
- log_error("fstat failed for fd %d.", fd);
- return 0;
- }
-
- major = (int) MAJOR(buf.st_dev);
- minor = (int) MINOR(buf.st_dev);
-
- if (!dm_stats_bind_devno(dms, major, minor))
- return_0;
- return 1;
-}
-
-static int _stats_check_precise_timestamps(const struct dm_stats *dms)
-{
- /* Already checked? */
- if (dms && dms->precise)
- return 1;
-
- return dm_message_supports_precise_timestamps();
-}
-
-int dm_stats_driver_supports_precise(void)
-{
- return _stats_check_precise_timestamps(NULL);
-}
-
-int dm_stats_driver_supports_histogram(void)
-{
- return _stats_check_precise_timestamps(NULL);
-}
-
-static int _fill_hist_arg(char *hist_arg, size_t hist_len, uint64_t scale,
- struct dm_histogram *bounds)
-{
- int i, l, len = 0, nr_bins;
- char *arg = hist_arg;
- uint64_t value;
-
- nr_bins = bounds->nr_bins;
-
- for (i = 0; i < nr_bins; i++) {
- value = bounds->bins[i].upper / scale;
- if ((l = dm_snprintf(arg, hist_len - len, FMTu64"%s", value,
- (i == (nr_bins - 1)) ? "" : ",")) < 0)
- return_0;
- len += l;
- arg += l;
- }
- return 1;
-}
-
-static void *_get_hist_arg(struct dm_histogram *bounds, uint64_t scale,
- size_t *len)
-{
- struct dm_histogram_bin *entry, *bins;
- size_t hist_len = 1; /* terminating '\0' */
- double value;
-
- entry = bins = bounds->bins;
-
- entry += bounds->nr_bins - 1;
- while(entry >= bins) {
- value = (double) (entry--)->upper;
- /* Use lround to avoid size_t -> double cast warning. */
- hist_len += 1 + (size_t) lround(log10(value / scale));
- if (entry != bins)
- hist_len++; /* ',' */
- }
-
- *len = hist_len;
-
- return zalloc(hist_len);
-}
-
-static char *_build_histogram_arg(struct dm_histogram *bounds, int *precise)
-{
- struct dm_histogram_bin *entry, *bins;
- size_t hist_len;
- char *hist_arg;
- uint64_t scale;
-
- entry = bins = bounds->bins;
-
- /* Empty histogram is invalid. */
- if (!bounds->nr_bins) {
- log_error("Cannot format empty histogram description.");
- return NULL;
- }
-
- /* Validate entries and set *precise if precision < 1ms. */
- entry += bounds->nr_bins - 1;
- while (entry >= bins) {
- if (entry != bins) {
- if (entry->upper < (entry - 1)->upper) {
- log_error("Histogram boundaries must be in "
- "order of increasing magnitude.");
- return 0;
- }
- }
-
- /*
- * Only enable precise_timestamps automatically if any
- * value in the histogram bounds uses precision < 1ms.
- */
- if (((entry--)->upper % NSEC_PER_MSEC) && !*precise)
- *precise = 1;
- }
-
- scale = (*precise) ? 1 : NSEC_PER_MSEC;
-
- /* Calculate hist_len and allocate a character buffer. */
- if (!(hist_arg = _get_hist_arg(bounds, scale, &hist_len))) {
- log_error("Could not allocate memory for histogram argument.");
- return 0;
- }
-
- /* Fill hist_arg with boundary strings. */
- if (!_fill_hist_arg(hist_arg, hist_len, scale, bounds))
- goto_bad;
-
- return hist_arg;
-
-bad:
- log_error("Could not build histogram arguments.");
- free(hist_arg);
-
- return NULL;
-}
-
-static struct dm_task *_stats_send_message(struct dm_stats *dms, char *msg)
-{
- struct dm_task *dmt;
-
- if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
- return_0;
-
- if (!_set_stats_device(dms, dmt))
- goto_bad;
-
- if (!dm_task_set_message(dmt, msg))
- goto_bad;
-
- if (!dm_task_run(dmt))
- goto_bad;
-
- return dmt;
-
-bad:
- dm_task_destroy(dmt);
- return NULL;
-}
-
-/*
- * Cache the dm device_name for the device bound to dms.
- */
-static int _stats_set_name_cache(struct dm_stats *dms)
-{
- struct dm_task *dmt;
-
- if (dms->name)
- return 1;
-
- if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
- return_0;
-
- if (!_set_stats_device(dms, dmt))
- goto_bad;
-
- if (!dm_task_run(dmt))
- goto_bad;
-
- if (!(dms->name = strdup(dm_task_get_name(dmt))))
- goto_bad;
-
- dm_task_destroy(dmt);
-
- return 1;
-
-bad:
- log_error("Could not retrieve device-mapper name for device.");
- dm_task_destroy(dmt);
- return 0;
-}
-
-/*
- * update region group_id values
- */
-static void _stats_update_groups(struct dm_stats *dms)
-{
- struct dm_stats_group *group;
- uint64_t group_id, i;
-
- for (group_id = 0; group_id < dms->max_region + 1; group_id++) {
- if (!_stats_group_id_present(dms, group_id))
- continue;
-
- group = &dms->groups[group_id];
-
- for (i = dm_bit_get_first(group->regions);
- i != DM_STATS_GROUP_NOT_PRESENT;
- i = dm_bit_get_next(group->regions, i))
- dms->regions[i].group_id = group_id;
- }
-}
-
-static void _check_group_regions_present(struct dm_stats *dms,
- struct dm_stats_group *group)
-{
- dm_bitset_t regions = group->regions;
- int64_t i, group_id;
-
- group_id = i = dm_bit_get_first(regions);
-
- for (; i > 0; i = dm_bit_get_next(regions, i))
- if (!_stats_region_present(&dms->regions[i])) {
- log_warn("Group descriptor " FMTd64 " contains "
- "non-existent region_id " FMTd64 ".",
- group_id, i);
- dm_bit_clear(regions, i);
- }
-}
-
-/*
- * Parse a DMS_GROUP group descriptor embedded in a region's aux_data.
- *
- * DMS_GROUP="ALIAS:MEMBERS"
- *
- * ALIAS: group alias
- * MEMBERS: list of group member region ids.
- *
- */
-#define DMS_GROUP_TAG "DMS_GROUP="
-#define DMS_GROUP_TAG_LEN (sizeof(DMS_GROUP_TAG) - 1)
-#define DMS_GROUP_SEP ':'
-#define DMS_AUX_SEP "#"
-
-static int _parse_aux_data_group(struct dm_stats *dms,
- struct dm_stats_region *region,
- struct dm_stats_group *group)
-{
- char *alias, *c, *end;
- dm_bitset_t regions;
-
- memset(group, 0, sizeof(*group));
- group->group_id = DM_STATS_GROUP_NOT_PRESENT;
-
- /* find start of group tag */
- c = strstr(region->aux_data, DMS_GROUP_TAG);
- if (!c)
- return 1; /* no group is not an error */
-
- alias = c + strlen(DMS_GROUP_TAG);
-
- c = strchr(c, DMS_GROUP_SEP);
-
- if (!c) {
- log_error("Found malformed group tag while reading aux_data");
- return 0;
- }
-
- /* terminate alias and advance to members */
- *(c++) = '\0';
-
- log_debug("Read alias '%s' from aux_data", alias);
-
- if (!c) {
- log_error("Found malformed group descriptor while "
- "reading aux_data, expected '%c'", DMS_GROUP_SEP);
- return 0;
- }
-
- /* if user aux_data follows make sure we have a terminated
- * string to pass to dm_bitset_parse_list().
- */
- end = strstr(c, DMS_AUX_SEP);
- if (!end)
- end = c + strlen(c);
- *(end++) = '\0';
-
- if (!(regions = dm_bitset_parse_list(c, NULL, 0))) {
- log_error("Could not parse member list while "
- "reading group aux_data");
- return 0;
- }
-
- group->group_id = dm_bit_get_first(regions);
- if (group->group_id != region->region_id) {
- log_error("Found invalid group descriptor in region " FMTu64
- " aux_data.", region->region_id);
- group->group_id = DM_STATS_GROUP_NOT_PRESENT;
- goto bad;
- }
-
- group->regions = regions;
- group->alias = NULL;
- if (strlen(alias)) {
- group->alias = strdup(alias);
- if (!group->alias) {
- log_error("Could not allocate memory for group alias");
- goto bad;
- }
- }
-
- /* separate group tag from user aux_data */
- if ((strlen(end) > 1) || strncmp(end, "-", 1))
- c = strdup(end);
- else
- c = strdup("");
-
- if (!c) {
- log_error("Could not allocate memory for user aux_data");
- goto bad_alias;
- }
-
- free(region->aux_data);
- region->aux_data = c;
-
- log_debug("Found group_id " FMTu64 ": alias=\"%s\"", group->group_id,
- (group->alias) ? group->alias : "");
-
- return 1;
-
-bad_alias:
- free((char *) group->alias);
-bad:
- dm_bitset_destroy(regions);
- return 0;
-}
-
-/*
- * Parse a histogram specification returned by the kernel in a
- * @stats_list response.
- */
-static int _stats_parse_histogram_spec(struct dm_stats *dms,
- struct dm_stats_region *region,
- const char *histogram)
-{
- static const char _valid_chars[] = "0123456789,";
- uint64_t scale = region->timescale, this_val = 0;
- struct dm_pool *mem = dms->hist_mem;
- struct dm_histogram_bin cur;
- struct dm_histogram hist;
- int nr_bins = 1;
- const char *c, *v, *val_start;
- char *p, *endptr = NULL;
-
- /* Advance past "histogram:". */
- histogram = strchr(histogram, ':');
- if (!histogram) {
- log_error("Could not parse histogram description.");
- return 0;
- }
- histogram++;
-
- /* @stats_list rows are newline terminated. */
- if ((p = strchr(histogram, '\n')))
- *p = '\0';
-
- if (!dm_pool_begin_object(mem, sizeof(cur)))
- return_0;
-
- memset(&hist, 0, sizeof(hist));
-
- hist.nr_bins = 0; /* fix later */
- hist.region = region;
- hist.dms = dms;
-
- if (!dm_pool_grow_object(mem, &hist, sizeof(hist)))
- goto_bad;
-
- c = histogram;
- do {
- for (v = _valid_chars; *v; v++)
- if (*c == *v)
- break;
- if (!*v) {
- stack;
- goto badchar;
- }
-
- if (*c == ',') {
- log_error("Invalid histogram description: %s",
- histogram);
- goto bad;
- } else {
- val_start = c;
- endptr = NULL;
-
- errno = 0;
- this_val = strtoull(val_start, &endptr, 10);
- if (errno || !endptr) {
- log_error("Could not parse histogram boundary.");
- goto bad;
- }
-
- c = endptr; /* Advance to units, comma, or end. */
-
- if (*c == ',')
- c++;
- else if (*c || (*c == ' ')) { /* Expected ',' or NULL. */
- stack;
- goto badchar;
- }
-
- if (*c == ',')
- c++;
-
- cur.upper = scale * this_val;
- cur.count = 0;
-
- if (!dm_pool_grow_object(mem, &cur, sizeof(cur)))
- goto_bad;
-
- nr_bins++;
- }
- } while (*c && (*c != ' '));
-
- /* final upper bound. */
- cur.upper = UINT64_MAX;
- if (!dm_pool_grow_object(mem, &cur, sizeof(cur)))
- goto_bad;
-
- region->bounds = dm_pool_end_object(mem);
-
- if (!region->bounds)
- return_0;
-
- region->bounds->nr_bins = nr_bins;
-
- log_debug("Added region histogram spec with %d entries.", nr_bins);
- return 1;
-
-badchar:
- log_error("Invalid character in histogram: '%c' (0x%x)", *c, *c);
-bad:
- dm_pool_abandon_object(mem);
- return 0;
-}
-
-static int _stats_parse_list_region(struct dm_stats *dms,
- struct dm_stats_region *region, char *line)
-{
- char *p = NULL, string_data[STATS_ROW_BUF_LEN];
- char *program_id, *aux_data, *stats_args;
- char *empty_string = (char *) "";
- int r;
-
- memset(string_data, 0, sizeof(string_data));
-
- /*
- * Parse fixed fields, line format:
- *
- * <region_id>: <start_sector>+<length> <step> <string data>
- *
- * Maximum string data size is 4096 - 1 bytes.
- */
- r = sscanf(line, FMTu64 ": " FMTu64 "+" FMTu64 " " FMTu64 " %4095c",
- ®ion->region_id, ®ion->start, ®ion->len,
- ®ion->step, string_data);
-
- if (r != 5)
- return_0;
-
- /* program_id is guaranteed to be first. */
- program_id = string_data;
-
- /*
- * FIXME: support embedded '\ ' in string data:
- * s/strchr/_find_unescaped_space()/
- */
- if ((p = strchr(string_data, ' '))) {
- /* terminate program_id string. */
- *p = '\0';
- if (!strncmp(program_id, "-", 1))
- program_id = empty_string;
- aux_data = p + 1;
- if ((p = strchr(aux_data, ' '))) {
- /* terminate aux_data string. */
- *p = '\0';
- stats_args = p + 1;
- } else
- stats_args = empty_string;
-
- /* no aux_data? */
- if (!strncmp(aux_data, "-", 1))
- aux_data = empty_string;
- else
- /* remove trailing newline */
- aux_data[strlen(aux_data) - 1] = '\0';
- } else
- aux_data = stats_args = empty_string;
-
- if (strstr(stats_args, PRECISE_ARG))
- region->timescale = 1;
- else
- region->timescale = NSEC_PER_MSEC;
-
- if ((p = strstr(stats_args, HISTOGRAM_ARG))) {
- if (!_stats_parse_histogram_spec(dms, region, p))
- return_0;
- } else
- region->bounds = NULL;
-
- /* clear aggregate cache */
- region->histogram = NULL;
-
- region->group_id = DM_STATS_GROUP_NOT_PRESENT;
-
- if (!(region->program_id = strdup(program_id)))
- return_0;
- if (!(region->aux_data = strdup(aux_data))) {
- free(region->program_id);
- return_0;
- }
-
- region->counters = NULL;
- return 1;
-}
-
-static int _stats_parse_list(struct dm_stats *dms, const char *resp)
-{
- uint64_t max_region = 0, nr_regions = 0;
- struct dm_stats_region cur, fill;
- struct dm_stats_group cur_group;
- struct dm_pool *mem = dms->mem, *group_mem = dms->group_mem;
- char line[STATS_ROW_BUF_LEN];
- FILE *list_rows;
-
- if (!resp) {
- log_error("Could not parse NULL @stats_list response.");
- return 0;
- }
-
- _stats_regions_destroy(dms);
- _stats_groups_destroy(dms);
-
- /* no regions */
- if (!strlen(resp)) {
- dms->nr_regions = dms->max_region = 0;
- dms->regions = NULL;
- return 1;
- }
-
- /*
- * dm_task_get_message_response() returns a 'const char *' but
- * since fmemopen also permits "w" it expects a 'char *'.
- */
- if (!(list_rows = fmemopen((char *)resp, strlen(resp), "r")))
- return_0;
-
- /* begin region table */
- if (!dm_pool_begin_object(mem, 1024))
- goto_bad;
-
- /* begin group table */
- if (!dm_pool_begin_object(group_mem, 32))
- goto_bad;
-
- while(fgets(line, sizeof(line), list_rows)) {
-
- cur_group.group_id = DM_STATS_GROUP_NOT_PRESENT;
- cur_group.regions = NULL;
- cur_group.alias = NULL;
-
- if (!_stats_parse_list_region(dms, &cur, line))
- goto_bad;
-
- /* handle holes in the list of region_ids */
- if (cur.region_id > max_region) {
- memset(&fill, 0, sizeof(fill));
- memset(&cur_group, 0, sizeof(cur_group));
- fill.region_id = DM_STATS_REGION_NOT_PRESENT;
- cur_group.group_id = DM_STATS_GROUP_NOT_PRESENT;
- do {
- if (!dm_pool_grow_object(mem, &fill, sizeof(fill)))
- goto_bad;
- if (!dm_pool_grow_object(group_mem, &cur_group,
- sizeof(cur_group)))
- goto_bad;
- } while (max_region++ < (cur.region_id - 1));
- }
-
- if (cur.aux_data)
- if (!_parse_aux_data_group(dms, &cur, &cur_group))
- log_error("Failed to parse group descriptor "
- "from region_id " FMTu64 " aux_data:"
- "'%s'", cur.region_id, cur.aux_data);
- /* continue */
-
- if (!dm_pool_grow_object(mem, &cur, sizeof(cur)))
- goto_bad;
-
- if (!dm_pool_grow_object(group_mem, &cur_group,
- sizeof(cur_group)))
- goto_bad;
-
- max_region++;
- nr_regions++;
- }
-
- if (!nr_regions)
- /* no region data read from @stats_list */
- goto bad;
-
- dms->nr_regions = nr_regions;
- dms->max_region = max_region - 1;
- dms->regions = dm_pool_end_object(mem);
- dms->groups = dm_pool_end_object(group_mem);
-
- dm_stats_foreach_group(dms)
- _check_group_regions_present(dms, &dms->groups[dms->cur_group]);
-
- _stats_update_groups(dms);
-
- if (fclose(list_rows))
- stack;
-
- return 1;
-
-bad:
- if (fclose(list_rows))
- stack;
- dm_pool_abandon_object(mem);
- dm_pool_abandon_object(group_mem);
-
- return 0;
-}
-
-int dm_stats_list(struct dm_stats *dms, const char *program_id)
-{
- char msg[STATS_MSG_BUF_LEN];
- struct dm_task *dmt;
- int r;
-
- if (!_stats_bound(dms))
- return_0;
-
- /* allow zero-length program_id for list */
- if (!program_id)
- program_id = dms->program_id;
-
- if (!_stats_set_name_cache(dms))
- return_0;
-
- if (dms->regions)
- _stats_regions_destroy(dms);
-
- r = dm_snprintf(msg, sizeof(msg), "@stats_list %s", program_id);
-
- if (r < 0) {
- log_error("Failed to prepare stats message.");
- return 0;
- }
-
- if (!(dmt = _stats_send_message(dms, msg)))
- return_0;
-
- if (!_stats_parse_list(dms, dm_task_get_message_response(dmt))) {
- log_error("Could not parse @stats_list response.");
- goto bad;
- }
-
- dm_task_destroy(dmt);
- return 1;
-
-bad:
- dm_task_destroy(dmt);
- return 0;
-}
-
-/*
- * Parse histogram data returned from a @stats_print operation.
- */
-static int _stats_parse_histogram(struct dm_pool *mem, char *hist_str,
- struct dm_histogram **histogram,
- struct dm_stats_region *region)
-{
- static const char _valid_chars[] = "0123456789:";
- struct dm_histogram *bounds = region->bounds;
- struct dm_histogram hist = {
- .nr_bins = region->bounds->nr_bins
- };
- const char *c, *v, *val_start;
- struct dm_histogram_bin cur;
- uint64_t sum = 0, this_val;
- char *endptr = NULL;
- int bin = 0;
-
- c = hist_str;
-
- if (!dm_pool_begin_object(mem, sizeof(cur)))
- return_0;
-
- if (!dm_pool_grow_object(mem, &hist, sizeof(hist)))
- goto_bad;
-
- do {
- memset(&cur, 0, sizeof(cur));
- for (v = _valid_chars; *v; v++)
- if (*c == *v)
- break;
- if (!*v)
- goto badchar;
-
- if (*c == ',')
- goto badchar;
- else {
- val_start = c;
- endptr = NULL;
-
- errno = 0;
- this_val = strtoull(val_start, &endptr, 10);
- if (errno || !endptr) {
- log_error("Could not parse histogram value.");
- goto bad;
- }
- c = endptr; /* Advance to colon, or end. */
-
- if (*c == ':')
- c++;
- else if (*c & (*c != '\n'))
- /* Expected ':', '\n', or NULL. */
- goto badchar;
-
- if (*c == ':')
- c++;
-
- cur.upper = bounds->bins[bin].upper;
- cur.count = this_val;
- sum += this_val;
-
- if (!dm_pool_grow_object(mem, &cur, sizeof(cur)))
- goto_bad;
-
- bin++;
- }
- } while (*c && (*c != '\n'));
-
- log_debug("Added region histogram data with %d entries.", hist.nr_bins);
-
- *histogram = dm_pool_end_object(mem);
- (*histogram)->sum = sum;
-
- return 1;
-
-badchar:
- log_error("Invalid character in histogram data: '%c' (0x%x)", *c, *c);
-bad:
- dm_pool_abandon_object(mem);
- return 0;
-}
-
-static int _stats_parse_region(struct dm_stats *dms, const char *resp,
- struct dm_stats_region *region,
- uint64_t timescale)
-{
- struct dm_histogram *hist = NULL;
- struct dm_pool *mem = dms->mem;
- struct dm_stats_counters cur;
- FILE *stats_rows = NULL;
- uint64_t start = 0, len = 0;
- char row[STATS_ROW_BUF_LEN];
- int r;
-
- if (!resp) {
- log_error("Could not parse empty @stats_print response.");
- return 0;
- }
-
- region->start = UINT64_MAX;
-
- if (!dm_pool_begin_object(mem, 512))
- goto_bad;
-
- /*
- * dm_task_get_message_response() returns a 'const char *' but
- * since fmemopen also permits "w" it expects a 'char *'.
- */
- stats_rows = fmemopen((char *)resp, strlen(resp), "r");
- if (!stats_rows)
- goto_bad;
-
- /*
- * Output format for each step-sized area of a region:
- *
- * <start_sector>+<length> counters
- *
- * The first 11 counters have the same meaning as
- * /sys/block/ * /stat or /proc/diskstats.
- *
- * Please refer to Documentation/iostats.txt for details.
- *
- * 1. the number of reads completed
- * 2. the number of reads merged
- * 3. the number of sectors read
- * 4. the number of milliseconds spent reading
- * 5. the number of writes completed
- * 6. the number of writes merged
- * 7. the number of sectors written
- * 8. the number of milliseconds spent writing
- * 9. the number of I/Os currently in progress
- * 10. the number of milliseconds spent doing I/Os
- * 11. the weighted number of milliseconds spent doing I/Os
- *
- * Additional counters:
- * 12. the total time spent reading in milliseconds
- * 13. the total time spent writing in milliseconds
- *
- */
- while (fgets(row, sizeof(row), stats_rows)) {
- r = sscanf(row, FMTu64 "+" FMTu64 /* start+len */
- /* reads */
- FMTu64 " " FMTu64 " " FMTu64 " " FMTu64 " "
- /* writes */
- FMTu64 " " FMTu64 " " FMTu64 " " FMTu64 " "
- /* in flight & io nsecs */
- FMTu64 " " FMTu64 " " FMTu64 " "
- /* tot read/write nsecs */
- FMTu64 " " FMTu64, &start, &len,
- &cur.reads, &cur.reads_merged, &cur.read_sectors,
- &cur.read_nsecs,
- &cur.writes, &cur.writes_merged, &cur.write_sectors,
- &cur.write_nsecs,
- &cur.io_in_progress,
- &cur.io_nsecs, &cur.weighted_io_nsecs,
- &cur.total_read_nsecs, &cur.total_write_nsecs);
- if (r != 15) {
- log_error("Could not parse @stats_print row.");
- goto bad;
- }
-
- /* scale time values up if needed */
- if (timescale != 1) {
- cur.read_nsecs *= timescale;
- cur.write_nsecs *= timescale;
- cur.io_nsecs *= timescale;
- cur.weighted_io_nsecs *= timescale;
- cur.total_read_nsecs *= timescale;
- cur.total_write_nsecs *= timescale;
- }
-
- if (region->bounds) {
- /* Find first histogram separator. */
- char *hist_str = strchr(row, ':');
- if (!hist_str) {
- log_error("Could not parse histogram value.");
- goto bad;
- }
- /* Find space preceding histogram. */
- while (hist_str && *(hist_str - 1) != ' ')
- hist_str--;
-
- /* Use a separate pool for histogram objects since we
- * are growing the area table and each area's histogram
- * table simultaneously.
- */
- if (!_stats_parse_histogram(dms->hist_mem, hist_str,
- &hist, region))
- goto_bad;
- hist->dms = dms;
- hist->region = region;
- }
-
- cur.histogram = hist;
-
- if (!dm_pool_grow_object(mem, &cur, sizeof(cur)))
- goto_bad;
-
- if (region->start == UINT64_MAX) {
- region->start = start;
- region->step = len; /* area size is always uniform. */
- }
- }
-
- if (region->start == UINT64_MAX)
- /* no area data read from @stats_print */
- goto bad;
-
- region->len = (start + len) - region->start;
- region->timescale = timescale;
- region->counters = dm_pool_end_object(mem);
-
- if (fclose(stats_rows))
- stack;
-
- return 1;
-
-bad:
- if (stats_rows)
- if (fclose(stats_rows))
- stack;
- dm_pool_abandon_object(mem);
-
- return 0;
-}
-
-static void _stats_walk_next_present(const struct dm_stats *dms,
- uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a,
- uint64_t *cur_g)
-{
- struct dm_stats_region *cur = NULL;
-
- /* start of walk: region loop advances *cur_r to 0. */
- if (*cur_r != DM_STATS_REGION_NOT_PRESENT)
- cur = &dms->regions[*cur_r];
-
- /* within current region? */
- if (cur && (*flags & DM_STATS_WALK_AREA)) {
- if (++(*cur_a) < _nr_areas_region(cur))
- return;
- else
- *cur_a = 0;
- }
-
- /* advance to next present, non-skipped region or end */
- while (++(*cur_r) <= dms->max_region) {
- cur = &dms->regions[*cur_r];
- if (!_stats_region_present(cur))
- continue;
- if ((*flags & DM_STATS_WALK_SKIP_SINGLE_AREA))
- if (!(*flags & DM_STATS_WALK_AREA))
- if (_nr_areas_region(cur) < 2)
- continue;
- /* matching region found */
- break;
- }
- return;
-}
-
-static void _stats_walk_next(const struct dm_stats *dms, uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a, uint64_t *cur_g)
-{
- if (!dms || !dms->regions)
- return;
-
- if (*flags & DM_STATS_WALK_AREA) {
- /* advance to next area, region, or end */
- _stats_walk_next_present(dms, flags, cur_r, cur_a, cur_g);
- return;
- }
-
- if (*flags & DM_STATS_WALK_REGION) {
- /* enable region aggregation */
- *cur_a = DM_STATS_WALK_REGION;
- _stats_walk_next_present(dms, flags, cur_r, cur_a, cur_g);
- return;
- }
-
- if (*flags & DM_STATS_WALK_GROUP) {
- /* enable group aggregation */
- *cur_r = *cur_a = DM_STATS_WALK_GROUP;
- while (!_stats_group_id_present(dms, ++(*cur_g))
- && (*cur_g) < dms->max_region + 1)
- ; /* advance to next present group or end */
- return;
- }
-
- log_error("stats_walk_next called with empty walk flags");
-}
-
-static void _group_walk_start(const struct dm_stats *dms, uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a, uint64_t *cur_g)
-{
- if (!(*flags & DM_STATS_WALK_GROUP))
- return;
-
- *cur_a = *cur_r = DM_STATS_WALK_GROUP;
- *cur_g = 0;
-
- /* advance to next present group or end */
- while ((*cur_g) <= dms->max_region) {
- if (_stats_region_is_grouped(dms, *cur_g))
- break;
- (*cur_g)++;
- }
-
- if (*cur_g > dms->max_region)
- /* no groups to walk */
- *flags &= ~DM_STATS_WALK_GROUP;
-}
-
-static void _stats_walk_start(const struct dm_stats *dms, uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a,
- uint64_t *cur_g)
-{
- log_debug("starting stats walk with %s %s %s %s",
- (*flags & DM_STATS_WALK_AREA) ? "AREA" : "",
- (*flags & DM_STATS_WALK_REGION) ? "REGION" : "",
- (*flags & DM_STATS_WALK_GROUP) ? "GROUP" : "",
- (*flags & DM_STATS_WALK_SKIP_SINGLE_AREA) ? "SKIP" : "");
-
- if (!dms->regions)
- return;
-
- if (!(*flags & (DM_STATS_WALK_AREA | DM_STATS_WALK_REGION)))
- return _group_walk_start(dms, flags, cur_r, cur_a, cur_g);
-
- /* initialise cursor state */
- *cur_a = 0;
- *cur_r = DM_STATS_REGION_NOT_PRESENT;
- *cur_g = DM_STATS_GROUP_NOT_PRESENT;
-
- if (!(*flags & DM_STATS_WALK_AREA))
- *cur_a = DM_STATS_WALK_REGION;
-
- /* advance to first present, non-skipped region */
- _stats_walk_next_present(dms, flags, cur_r, cur_a, cur_g);
-}
-
-#define DM_STATS_WALK_MASK (DM_STATS_WALK_AREA \
- | DM_STATS_WALK_REGION \
- | DM_STATS_WALK_GROUP \
- | DM_STATS_WALK_SKIP_SINGLE_AREA)
-
-int dm_stats_walk_init(struct dm_stats *dms, uint64_t flags)
-{
- if (!dms)
- return_0;
-
- if (flags & ~DM_STATS_WALK_MASK) {
- log_error("Unknown value in walk flags: 0x" FMTx64,
- (uint64_t) (flags & ~DM_STATS_WALK_MASK));
- return 0;
- }
- dms->walk_flags = flags;
- log_debug("dm_stats_walk_init: initialised flags to " FMTx64, flags);
- return 1;
-}
-
-void dm_stats_walk_start(struct dm_stats *dms)
-{
- if (!dms || !dms->regions)
- return;
-
- dms->cur_flags = dms->walk_flags;
-
- _stats_walk_start(dms, &dms->cur_flags,
- &dms->cur_region, &dms->cur_area,
- &dms->cur_group);
-}
-
-void dm_stats_walk_next(struct dm_stats *dms)
-{
- _stats_walk_next(dms, &dms->cur_flags,
- &dms->cur_region, &dms->cur_area,
- &dms->cur_group);
-}
-
-void dm_stats_walk_next_region(struct dm_stats *dms)
-{
- dms->cur_flags &= ~DM_STATS_WALK_AREA;
- _stats_walk_next(dms, &dms->cur_flags,
- &dms->cur_region, &dms->cur_area,
- &dms->cur_group);
-}
-
-/*
- * Return 1 if any regions remain that are present and not skipped
- * by the current walk flags or 0 otherwise.
- */
-static uint64_t _stats_walk_any_unskipped(const struct dm_stats *dms,
- uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a)
-{
- struct dm_stats_region *region;
- uint64_t i;
-
- if (*cur_r > dms->max_region)
- return 0;
-
- for (i = *cur_r; i <= dms->max_region; i++) {
- region = &dms->regions[i];
- if (!_stats_region_present(region))
- continue;
- if ((*flags & DM_STATS_WALK_SKIP_SINGLE_AREA)
- && !(*flags & DM_STATS_WALK_AREA))
- if (_nr_areas_region(region) < 2)
- continue;
- return 1;
- }
- return 0;
-}
-
-static void _stats_walk_end_areas(const struct dm_stats *dms, uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a,
- uint64_t *cur_g)
-{
- int end = !_stats_walk_any_unskipped(dms, flags, cur_r, cur_a);
-
- if (!(*flags & DM_STATS_WALK_AREA))
- return;
-
- if (!end)
- return;
-
- *flags &= ~DM_STATS_WALK_AREA;
- if (*flags & DM_STATS_WALK_REGION) {
- /* start region walk */
- *cur_a = DM_STATS_WALK_REGION;
- *cur_r = DM_STATS_REGION_NOT_PRESENT;
- _stats_walk_next_present(dms, flags, cur_r, cur_a, cur_g);
- if (!_stats_walk_any_unskipped(dms, flags, cur_r, cur_a)) {
- /* no more regions */
- *flags &= ~DM_STATS_WALK_REGION;
- if (!(*flags & DM_STATS_WALK_GROUP))
- *cur_r = dms->max_region;
- }
- }
-
- if (*flags & DM_STATS_WALK_REGION)
- return;
-
- if (*flags & DM_STATS_WALK_GROUP)
- _group_walk_start(dms, flags, cur_r, cur_a, cur_g);
-}
-
-static int _stats_walk_end(const struct dm_stats *dms, uint64_t *flags,
- uint64_t *cur_r, uint64_t *cur_a, uint64_t *cur_g)
-{
- if (*flags & DM_STATS_WALK_AREA) {
- _stats_walk_end_areas(dms, flags, cur_r, cur_a, cur_g);
- goto out;
- }
-
- if (*flags & DM_STATS_WALK_REGION) {
- if (!_stats_walk_any_unskipped(dms, flags, cur_r, cur_a)) {
- *flags &= ~DM_STATS_WALK_REGION;
- _group_walk_start(dms, flags, cur_r, cur_a, cur_g);
- }
- goto out;
- }
-
- if (*flags & DM_STATS_WALK_GROUP) {
- if (*cur_g <= dms->max_region)
- goto out;
- *flags &= ~DM_STATS_WALK_GROUP;
- }
-out:
- return !(*flags & ~DM_STATS_WALK_SKIP_SINGLE_AREA);
-}
-
-int dm_stats_walk_end(struct dm_stats *dms)
-{
- if (!dms)
- return 1;
-
- if (_stats_walk_end(dms, &dms->cur_flags,
- &dms->cur_region, &dms->cur_area,
- &dms->cur_group)) {
- dms->cur_flags = dms->walk_flags;
- return 1;
- }
- return 0;
-}
-
-dm_stats_obj_type_t dm_stats_object_type(const struct dm_stats *dms,
- uint64_t region_id,
- uint64_t area_id)
-{
- uint64_t group_id;
-
- region_id = (region_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_region : region_id ;
- area_id = (area_id == DM_STATS_AREA_CURRENT)
- ? dms->cur_area : area_id ;
-
- if (region_id == DM_STATS_REGION_NOT_PRESENT)
- /* no region */
- return DM_STATS_OBJECT_TYPE_NONE;
-
- if (region_id & DM_STATS_WALK_GROUP) {
- if (region_id == DM_STATS_WALK_GROUP)
- /* indirect group_id from cursor */
- group_id = dms->cur_group;
- else
- /* immediate group_id encoded in region_id */
- group_id = region_id & ~DM_STATS_WALK_GROUP;
- if (!_stats_group_id_present(dms, group_id))
- return DM_STATS_OBJECT_TYPE_NONE;
- return DM_STATS_OBJECT_TYPE_GROUP;
- }
-
- if (region_id > dms->max_region)
- /* end of table */
- return DM_STATS_OBJECT_TYPE_NONE;
-
- if (area_id & DM_STATS_WALK_REGION)
- /* aggregate region */
- return DM_STATS_OBJECT_TYPE_REGION;
-
- /* plain region_id and area_id */
- return DM_STATS_OBJECT_TYPE_AREA;
-}
-
-dm_stats_obj_type_t dm_stats_current_object_type(const struct dm_stats *dms)
-{
- /* dm_stats_object_type will decode region/area */
- return dm_stats_object_type(dms,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT);
-}
-
-uint64_t dm_stats_get_region_nr_areas(const struct dm_stats *dms,
- uint64_t region_id)
-{
- struct dm_stats_region *region = NULL;
-
- /* groups or aggregate regions cannot be subdivided */
- if (region_id & DM_STATS_WALK_GROUP)
- return 1;
-
- region = &dms->regions[region_id];
- return _nr_areas_region(region);
-}
-
-uint64_t dm_stats_get_current_nr_areas(const struct dm_stats *dms)
-{
- /* groups or aggregate regions cannot be subdivided */
- if (dms->cur_region & DM_STATS_WALK_GROUP)
- return 1;
-
- return dm_stats_get_region_nr_areas(dms, dms->cur_region);
-}
-
-uint64_t dm_stats_get_nr_areas(const struct dm_stats *dms)
-{
- uint64_t nr_areas = 0, flags = DM_STATS_WALK_AREA;
- /* use a separate cursor */
- uint64_t cur_region = 0, cur_area = 0, cur_group = 0;
-
- /* no regions to visit? */
- if (!dms->regions)
- return 0;
-
- flags = DM_STATS_WALK_AREA;
- _stats_walk_start(dms, &flags, &cur_region, &cur_area, &cur_group);
- do {
- nr_areas += dm_stats_get_current_nr_areas(dms);
- _stats_walk_next(dms, &flags,
- &cur_region, &cur_area,
- &cur_group);
- } while (!_stats_walk_end(dms, &flags,
- &cur_region, &cur_area,
- &cur_group));
- return nr_areas;
-}
-
-int dm_stats_group_present(const struct dm_stats *dms, uint64_t group_id)
-{
- return _stats_group_id_present(dms, group_id);
-}
-
-int dm_stats_get_region_nr_histogram_bins(const struct dm_stats *dms,
- uint64_t region_id)
-{
- region_id = (region_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_region : region_id ;
-
- /* FIXME: support group histograms if all region bounds match */
- if (region_id & DM_STATS_WALK_GROUP)
- return 0;
-
- if (!dms->regions[region_id].bounds)
- return 0;
-
- return dms->regions[region_id].bounds->nr_bins;
-}
-
-/*
- * Fill buf with a list of set regions in the regions bitmap. Consecutive
- * ranges of set region IDs are output using "M-N" range notation.
- *
- * The number of bytes consumed is returned or zero on error.
- */
-static size_t _stats_group_tag_fill(const struct dm_stats *dms,
- dm_bitset_t regions,
- char *buf, size_t buflen)
-{
- int i, j, r, next, last = 0;
- size_t used = 0;
-
- last = dm_bit_get_last(regions);
-
- i = dm_bit_get_first(regions);
- for(; i >= 0; i = dm_bit_get_next(regions, i)) {
- /* find range end */
- j = i;
- do
- next = j + 1;
- while ((j = dm_bit_get_next(regions, j)) == next);
-
- /* set to last set bit */
- j = next - 1;
-
- /* handle range vs. single region */
- if (i != j)
- r = dm_snprintf(buf, buflen, FMTu64 "-" FMTu64 "%s",
- (uint64_t) i, (uint64_t) j,
- (j == last) ? "" : ",");
- else
- r = dm_snprintf(buf, buflen, FMTu64 "%s", (uint64_t) i,
- (i == last) ? "" : ",");
- if (r < 0)
- goto_bad;
-
- i = next; /* skip handled bits if in range */
-
- buf += r;
- used += r;
- }
-
- return used;
-bad:
- log_error("Could not format group list.");
- return 0;
-}
-
-/*
- * Calculate the space required to hold a string description of the group
- * described by the regions bitset using comma separated list in range
- * notation ("A,B,C,M-N").
- */
-static size_t _stats_group_tag_len(const struct dm_stats *dms,
- dm_bitset_t regions)
-{
- int64_t i, j, next, nr_regions = 0;
- size_t buflen = 0, id_len = 0;
-
- /* check region ids and find last set bit */
- i = dm_bit_get_first(regions);
- for (; i >= 0; i = dm_bit_get_next(regions, i)) {
- /* length of region_id or range start in characters */
- id_len = (i) ? 1 + (size_t) log10(i) : 1;
- buflen += id_len;
- j = i;
- do
- next = j + 1;
- while ((j = dm_bit_get_next(regions, j)) == next);
-
- /* set to last set bit */
- j = next - 1;
-
- nr_regions += j - i + 1;
-
- /* handle range */
- if (i != j) {
- /* j is always > i, which is always >= 0 */
- id_len = 1 + (size_t) log10(j);
- buflen += id_len + 1; /* range end plus "-" */
- }
- buflen++;
- i = next; /* skip bits if handling range */
- }
- return buflen;
-}
-
-/*
- * Build a DMS_GROUP="..." tag for the group specified by group_id,
- * to be stored in the corresponding region's aux_data field.
- */
-static char *_build_group_tag(struct dm_stats *dms, uint64_t group_id)
-{
- char *aux_string, *buf;
- dm_bitset_t regions;
- const char *alias;
- size_t buflen = 0;
- int r;
-
- regions = dms->groups[group_id].regions;
- alias = dms->groups[group_id].alias;
-
- buflen = _stats_group_tag_len(dms, regions);
-
- if (!buflen)
- return_0;
-
- buflen += DMS_GROUP_TAG_LEN;
- buflen += 1 + (alias ? strlen(alias) : 0); /* 'alias:' */
-
- buf = aux_string = malloc(buflen);
- if (!buf) {
- log_error("Could not allocate memory for aux_data string.");
- return NULL;
- }
-
- if (!dm_strncpy(buf, DMS_GROUP_TAG, DMS_GROUP_TAG_LEN + 1))
- goto_bad;
-
- buf += DMS_GROUP_TAG_LEN;
- buflen -= DMS_GROUP_TAG_LEN;
-
- r = dm_snprintf(buf, buflen, "%s%c", alias ? alias : "", DMS_GROUP_SEP);
- if (r < 0)
- goto_bad;
-
- buf += r;
- buflen -= r;
-
- r = _stats_group_tag_fill(dms, regions, buf, buflen);
- if (!r)
- goto_bad;
-
- return aux_string;
-bad:
- log_error("Could not format group aux_data.");
- free(aux_string);
- return NULL;
-}
-
-/*
- * Store updated aux_data for a region. The aux_data is passed to the
- * kernel using the @stats_set_aux message. Any required group tag is
- * generated from the current group table and included in the message.
- */
-static int _stats_set_aux(struct dm_stats *dms,
- uint64_t region_id, const char *aux_data)
-{
- const char *group_tag = NULL;
- struct dm_task *dmt = NULL;
- char msg[STATS_MSG_BUF_LEN];
-
- /* group data required? */
- if (_stats_group_id_present(dms, region_id)) {
- group_tag = _build_group_tag(dms, region_id);
- if (!group_tag) {
- log_error("Could not build group descriptor for "
- "region ID " FMTu64, region_id);
- goto bad;
- }
- }
-
- if (dm_snprintf(msg, sizeof(msg), "@stats_set_aux " FMTu64 " %s%s%s ",
- region_id, (group_tag) ? group_tag : "",
- (group_tag) ? DMS_AUX_SEP : "",
- (strlen(aux_data)) ? aux_data : "-") < 0) {
- log_error("Could not prepare @stats_set_aux message");
- goto bad;
- }
-
- if (!(dmt = _stats_send_message(dms, msg)))
- goto_bad;
-
- free((char *) group_tag);
-
- /* no response to a @stats_set_aux message */
- dm_task_destroy(dmt);
-
- return 1;
-bad:
- free((char *) group_tag);
- return 0;
-}
-
-/*
- * Maximum length of a "start+end" range string:
- * Two 20 digit uint64_t, '+', and NULL.
- */
-#define RANGE_LEN 42
-static int _stats_create_region(struct dm_stats *dms, uint64_t *region_id,
- uint64_t start, uint64_t len, int64_t step,
- int precise, const char *hist_arg,
- const char *program_id, const char *aux_data)
-{
- char msg[STATS_MSG_BUF_LEN], range[RANGE_LEN], *endptr = NULL;
- const char *err_fmt = "Could not prepare @stats_create %s.";
- const char *precise_str = PRECISE_ARG;
- const char *resp, *opt_args = NULL;
- struct dm_task *dmt = NULL;
- int r = 0, nr_opt = 0;
-
- if (!_stats_bound(dms))
- return_0;
-
- if (!program_id || !strlen(program_id))
- program_id = dms->program_id;
-
- if (start || len) {
- if (dm_snprintf(range, sizeof(range), FMTu64 "+" FMTu64,
- start, len) < 0) {
- log_error(err_fmt, "range");
- return 0;
- }
- }
-
- if (precise < 0)
- precise = dms->precise;
-
- if (precise)
- nr_opt++;
- else
- precise_str = "";
-
- if (hist_arg)
- nr_opt++;
- else
- hist_arg = "";
-
- if (nr_opt) {
- if ((dm_asprintf((char **)&opt_args, "%d %s %s%s", nr_opt,
- precise_str,
- (strlen(hist_arg)) ? HISTOGRAM_ARG : "",
- hist_arg)) < 0) {
- log_error(err_fmt, PRECISE_ARG " option.");
- return 0;
- }
- } else
- opt_args = strdup("");
-
- if (dm_snprintf(msg, sizeof(msg), "@stats_create %s %s" FMTu64
- " %s %s %s", (start || len) ? range : "-",
- (step < 0) ? "/" : "",
- (uint64_t)llabs(step),
- opt_args, program_id, aux_data) < 0) {
- log_error(err_fmt, "message");
- free((void *) opt_args);
- return 0;
- }
-
- if (!(dmt = _stats_send_message(dms, msg)))
- goto_out;
-
- resp = dm_task_get_message_response(dmt);
- if (!resp) {
- log_error("Could not parse empty @stats_create response.");
- goto out;
- }
-
- if (region_id) {
- errno = 0;
- *region_id = strtoull(resp, &endptr, 10);
- if (errno || resp == endptr)
- goto_out;
- }
-
- r = 1;
-
-out:
- if (dmt)
- dm_task_destroy(dmt);
- free((void *) opt_args);
-
- return r;
-}
-
-int dm_stats_create_region(struct dm_stats *dms, uint64_t *region_id,
- uint64_t start, uint64_t len, int64_t step,
- int precise, struct dm_histogram *bounds,
- const char *program_id, const char *user_data)
-{
- char *hist_arg = NULL;
- int r = 0;
-
- /* Nanosecond counters and histograms both need precise_timestamps. */
- if ((precise || bounds) && !_stats_check_precise_timestamps(dms))
- return_0;
-
- if (bounds) {
- /* _build_histogram_arg enables precise if vals < 1ms. */
- if (!(hist_arg = _build_histogram_arg(bounds, &precise)))
- goto_out;
- }
-
- r = _stats_create_region(dms, region_id, start, len, step,
- precise, hist_arg, program_id, user_data);
- free(hist_arg);
-
-out:
- return r;
-}
-
-static void _stats_clear_group_regions(struct dm_stats *dms, uint64_t group_id)
-{
- struct dm_stats_group *group;
- uint64_t i;
-
- group = &dms->groups[group_id];
- for (i = dm_bit_get_first(group->regions);
- i != DM_STATS_GROUP_NOT_PRESENT;
- i = dm_bit_get_next(group->regions, i))
- dms->regions[i].group_id = DM_STATS_GROUP_NOT_PRESENT;
-}
-
-static int _stats_remove_region_id_from_group(struct dm_stats *dms,
- uint64_t region_id)
-{
- struct dm_stats_region *region = &dms->regions[region_id];
- uint64_t group_id = region->group_id;
- dm_bitset_t regions = dms->groups[group_id].regions;
-
- if (!_stats_region_is_grouped(dms, region_id))
- return_0;
-
- dm_bit_clear(regions, region_id);
-
- /* removing group leader? */
- if (region_id == group_id) {
- _stats_clear_group_regions(dms, group_id);
- _stats_group_destroy(&dms->groups[group_id]);
- }
-
- return _stats_set_aux(dms, group_id, dms->regions[group_id].aux_data);
-}
-
-static int _stats_delete_region(struct dm_stats *dms, uint64_t region_id)
-{
- char msg[STATS_MSG_BUF_LEN];
- struct dm_task *dmt;
-
- if (_stats_region_is_grouped(dms, region_id))
- if (!_stats_remove_region_id_from_group(dms, region_id)) {
- log_error("Could not remove region ID " FMTu64 " from "
- "group ID " FMTu64,
- region_id, dms->regions[region_id].group_id);
- return 0;
- }
-
- if (dm_snprintf(msg, sizeof(msg), "@stats_delete " FMTu64, region_id) < 0) {
- log_error("Could not prepare @stats_delete message.");
- return 0;
- }
-
- dmt = _stats_send_message(dms, msg);
- if (!dmt)
- return_0;
- dm_task_destroy(dmt);
-
- return 1;
-}
-
-int dm_stats_delete_region(struct dm_stats *dms, uint64_t region_id)
-{
- int listed = 0;
-
- if (!_stats_bound(dms))
- return_0;
-
- /*
- * To correctly delete a region, that may be part of a group, a
- * listed handle is required, since the region may need to be
- * removed from another region's group descriptor; earlier
- * versions of the region deletion interface do not have this
- * requirement since there are no dependencies between regions.
- *
- * Listing a previously unlisted handle has numerous
- * side-effects on other calls and operations (e.g. stats
- * walks), especially when returning to a function that depends
- * on the state of the region table, or statistics cursor.
- *
- * To avoid changing the semantics of the API, and the need for
- * a versioned symbol, maintain a flag indicating when a listing
- * has been carried out, and drop the region table before
- * returning.
- *
- * This ensures compatibility with programs compiled against
- * earlier versions of libdm.
- */
- if (!dms->regions && !(listed = dm_stats_list(dms, dms->program_id))) {
- log_error("Could not obtain region list while deleting "
- "region ID " FMTu64, region_id);
- goto bad;
- }
-
- if (!dm_stats_get_nr_regions(dms)) {
- log_error("Could not delete region ID " FMTu64 ": "
- "no regions found", region_id);
- goto bad;
- }
-
- /* includes invalid and special region_id values */
- if (!dm_stats_region_present(dms, region_id)) {
- log_error("Region ID " FMTu64 " does not exist", region_id);
- goto bad;
- }
-
- if (!_stats_delete_region(dms, region_id))
- goto bad;
-
- if (!listed)
- /* wipe region and mark as not present */
- _stats_region_destroy(&dms->regions[region_id]);
- else
- /* return handle to prior state */
- _stats_regions_destroy(dms);
-
- return 1;
-bad:
- if (listed)
- _stats_regions_destroy(dms);
-
- return 0;
-}
-
-int dm_stats_clear_region(struct dm_stats *dms, uint64_t region_id)
-{
- char msg[STATS_MSG_BUF_LEN];
- struct dm_task *dmt;
-
- if (!_stats_bound(dms))
- return_0;
-
- if (dm_snprintf(msg, sizeof(msg), "@stats_clear " FMTu64, region_id) < 0) {
- log_error("Could not prepare @stats_clear message.");
- return 0;
- }
-
- dmt = _stats_send_message(dms, msg);
-
- if (!dmt)
- return_0;
-
- dm_task_destroy(dmt);
-
- return 1;
-}
-
-static struct dm_task *_stats_print_region(struct dm_stats *dms,
- uint64_t region_id, unsigned start_line,
- unsigned num_lines, unsigned clear)
-{
- /* @stats_print[_clear] <region_id> [<start_line> <num_lines>] */
- const char *err_fmt = "Could not prepare @stats_print %s.";
- char msg[STATS_MSG_BUF_LEN], lines[RANGE_LEN];
- struct dm_task *dmt = NULL;
-
- if (start_line || num_lines)
- if (dm_snprintf(lines, sizeof(lines),
- "%u %u", start_line, num_lines) < 0) {
- log_error(err_fmt, "row specification");
- return NULL;
- }
-
- if (dm_snprintf(msg, sizeof(msg), "@stats_print%s " FMTu64 " %s",
- (clear) ? "_clear" : "",
- region_id, (start_line || num_lines) ? lines : "") < 0) {
- log_error(err_fmt, "message");
- return NULL;
- }
-
- if (!(dmt = _stats_send_message(dms, msg)))
- return_NULL;
-
- return dmt;
-}
-
-char *dm_stats_print_region(struct dm_stats *dms, uint64_t region_id,
- unsigned start_line, unsigned num_lines,
- unsigned clear)
-{
- char *resp = NULL;
- struct dm_task *dmt = NULL;
- const char *response;
-
- if (!_stats_bound(dms))
- return_0;
-
- /*
- * FIXME: 'print' can be emulated for groups or aggregate regions
- * by populating the handle and emitting aggregate counter data
- * in the kernel print format.
- */
- if (region_id == DM_STATS_WALK_GROUP)
- return_0;
-
- dmt = _stats_print_region(dms, region_id,
- start_line, num_lines, clear);
-
- if (!dmt)
- return_0;
-
- if (!(response = dm_task_get_message_response(dmt)))
- goto_out;
-
- if (!(resp = dm_pool_strdup(dms->mem, response)))
- log_error("Could not allocate memory for response buffer.");
-out:
- dm_task_destroy(dmt);
-
- return resp;
-}
-
-void dm_stats_buffer_destroy(struct dm_stats *dms, char *buffer)
-{
- dm_pool_free(dms->mem, buffer);
-}
-
-uint64_t dm_stats_get_nr_regions(const struct dm_stats *dms)
-{
- if (!dms)
- return_0;
-
- if (!dms->regions)
- return 0;
-
- return dms->nr_regions;
-}
-
-uint64_t dm_stats_get_nr_groups(const struct dm_stats *dms)
-{
- uint64_t group_id, nr_groups = 0;
-
- if (!dms)
- return_0;
-
- /* no regions or groups? */
- if (!dms->regions || !dms->groups)
- return 0;
-
- for (group_id = 0; group_id <= dms->max_region; group_id++)
- if (dms->groups[group_id].group_id
- != DM_STATS_GROUP_NOT_PRESENT)
- nr_groups++;
-
- return nr_groups;
-}
-
-/**
- * Test whether region_id is present in this set of stats data.
- */
-int dm_stats_region_present(const struct dm_stats *dms, uint64_t region_id)
-{
- if (!dms->regions)
- return_0;
-
- if (region_id > dms->max_region)
- return 0;
-
- return _stats_region_present(&dms->regions[region_id]);
-}
-
-static int _dm_stats_populate_region(struct dm_stats *dms, uint64_t region_id,
- const char *resp)
-{
- struct dm_stats_region *region = &dms->regions[region_id];
-
- if (!_stats_bound(dms))
- return_0;
-
- if (!region) {
- log_error("Cannot populate empty handle before dm_stats_list().");
- return 0;
- }
- if (!_stats_parse_region(dms, resp, region, region->timescale)) {
- log_error("Could not parse @stats_print message response.");
- return 0;
- }
- region->region_id = region_id;
- return 1;
-}
-
-int dm_stats_populate(struct dm_stats *dms, const char *program_id,
- uint64_t region_id)
-{
- int all_regions = (region_id == DM_STATS_REGIONS_ALL);
- struct dm_task *dmt = NULL; /* @stats_print task */
- uint64_t saved_flags; /* saved walk flags */
- const char *resp;
-
- /*
- * We are about do destroy and re-create the region table, so it
- * is safe to use the cursor embedded in the stats handle: just
- * save a copy of the current walk_flags to restore later.
- */
- saved_flags = dms->walk_flags;
-
- if (!_stats_bound(dms))
- return_0;
-
- if ((!all_regions) && (region_id & DM_STATS_WALK_GROUP)) {
- log_error("Invalid region_id for dm_stats_populate: "
- "DM_STATS_WALK_GROUP");
- return 0;
- }
-
- if (!dms->nr_regions) {
- log_error("No regions registered.");
- return 0;
- }
-
- /* allow zero-length program_id for populate */
- if (!program_id)
- program_id = dms->program_id;
-
- if (all_regions && !dm_stats_list(dms, program_id)) {
- log_error("Could not parse @stats_list response.");
- goto bad;
- } else if (!_stats_set_name_cache(dms)) {
- goto_bad;
- }
-
- dms->walk_flags = DM_STATS_WALK_REGION;
- dm_stats_walk_start(dms);
- do {
- region_id = (all_regions)
- ? dm_stats_get_current_region(dms) : region_id;
-
- /* obtain all lines and clear counter values */
- if (!(dmt = _stats_print_region(dms, region_id, 0, 0, 1)))
- goto_bad;
-
- resp = dm_task_get_message_response(dmt);
- if (!_dm_stats_populate_region(dms, region_id, resp)) {
- dm_task_destroy(dmt);
- goto_bad;
- }
-
- dm_task_destroy(dmt);
- dm_stats_walk_next(dms);
-
- } while (all_regions && !dm_stats_walk_end(dms));
-
- dms->walk_flags = saved_flags;
- return 1;
-
-bad:
- dms->walk_flags = saved_flags;
- _stats_regions_destroy(dms);
- dms->regions = NULL;
- return 0;
-}
-
-/**
- * destroy a dm_stats object and all associated regions and counter sets.
- */
-void dm_stats_destroy(struct dm_stats *dms)
-{
- if (!dms)
- return;
-
- _stats_regions_destroy(dms);
- _stats_groups_destroy(dms);
- _stats_clear_binding(dms);
- dm_pool_destroy(dms->mem);
- dm_pool_destroy(dms->hist_mem);
- dm_pool_destroy(dms->group_mem);
- free(dms->program_id);
- free((char *) dms->name);
- free(dms);
-}
-
-/*
- * Walk each area that is a member of region_id rid.
- * i is a variable of type int that holds the current area_id.
- */
-#define _foreach_region_area(dms, rid, i) \
-for ((i) = 0; (i) < _nr_areas_region(&dms->regions[(rid)]); (i)++) \
-
-/*
- * Walk each region that is a member of group_id gid.
- * i is a variable of type int that holds the current region_id.
- */
-#define _foreach_group_region(dms, gid, i) \
-for ((i) = dm_bit_get_first((dms)->groups[(gid)].regions); \
- (i) != DM_STATS_GROUP_NOT_PRESENT; \
- (i) = dm_bit_get_next((dms)->groups[(gid)].regions, (i))) \
-
-/*
- * Walk each region that is a member of group_id gid visiting each
- * area within the region.
- * i is a variable of type int that holds the current region_id.
- * j is a variable of type int variable that holds the current area_id.
- */
-#define _foreach_group_area(dms, gid, i, j) \
-_foreach_group_region(dms, gid, i) \
- _foreach_region_area(dms, i, j)
-
-static uint64_t _stats_get_counter(const struct dm_stats *dms,
- const struct dm_stats_counters *area,
- dm_stats_counter_t counter)
-{
- switch(counter) {
- case DM_STATS_READS_COUNT:
- return area->reads;
- case DM_STATS_READS_MERGED_COUNT:
- return area->reads_merged;
- case DM_STATS_READ_SECTORS_COUNT:
- return area->read_sectors;
- case DM_STATS_READ_NSECS:
- return area->read_nsecs;
- case DM_STATS_WRITES_COUNT:
- return area->writes;
- case DM_STATS_WRITES_MERGED_COUNT:
- return area->writes_merged;
- case DM_STATS_WRITE_SECTORS_COUNT:
- return area->write_sectors;
- case DM_STATS_WRITE_NSECS:
- return area->write_nsecs;
- case DM_STATS_IO_IN_PROGRESS_COUNT:
- return area->io_in_progress;
- case DM_STATS_IO_NSECS:
- return area->io_nsecs;
- case DM_STATS_WEIGHTED_IO_NSECS:
- return area->weighted_io_nsecs;
- case DM_STATS_TOTAL_READ_NSECS:
- return area->total_read_nsecs;
- case DM_STATS_TOTAL_WRITE_NSECS:
- return area->total_write_nsecs;
- case DM_STATS_NR_COUNTERS:
- default:
- log_error("Attempt to read invalid counter: %d", counter);
- }
- return 0;
-}
-
-uint64_t dm_stats_get_counter(const struct dm_stats *dms,
- dm_stats_counter_t counter,
- uint64_t region_id, uint64_t area_id)
-{
- uint64_t i, j, sum = 0; /* aggregation */
- int sum_regions = 0;
- struct dm_stats_region *region;
- struct dm_stats_counters *area;
-
- region_id = (region_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_region : region_id ;
- area_id = (area_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_area : area_id ;
-
- sum_regions = !!(region_id & DM_STATS_WALK_GROUP);
-
- if (region_id == DM_STATS_WALK_GROUP)
- /* group walk using the cursor */
- region_id = dms->cur_group;
- else if (region_id & DM_STATS_WALK_GROUP)
- /* group walk using immediate group_id */
- region_id &= ~DM_STATS_WALK_GROUP;
- region = &dms->regions[region_id];
-
- /*
- * All statistics aggregation takes place here: aggregate metrics
- * are calculated as normal using the aggregated counter values
- * returned for the region or group specified.
- */
-
- if (_stats_region_is_grouped(dms, region_id) && (sum_regions)) {
- /* group */
- if (area_id & DM_STATS_WALK_GROUP)
- _foreach_group_area(dms, region->group_id, i, j) {
- area = &dms->regions[i].counters[j];
- sum += _stats_get_counter(dms, area, counter);
- }
- else
- _foreach_group_region(dms, region->group_id, i) {
- area = &dms->regions[i].counters[area_id];
- sum += _stats_get_counter(dms, area, counter);
- }
- } else if (area_id == DM_STATS_WALK_REGION) {
- /* aggregate region */
- _foreach_region_area(dms, region_id, j) {
- area = &dms->regions[region_id].counters[j];
- sum += _stats_get_counter(dms, area, counter);
- }
- } else {
- /* plain region / area */
- area = ®ion->counters[area_id];
- sum = _stats_get_counter(dms, area, counter);
- }
-
- return sum;
-}
-
-/*
- * Methods for accessing named counter fields. All methods share the
- * following naming scheme and prototype:
- *
- * uint64_t dm_stats_get_COUNTER(const struct dm_stats *, uint64_t, uint64_t)
- *
- * Where the two integer arguments are the region_id and area_id
- * respectively.
- *
- * name is the name of the counter (lower case)
- * counter is the part of the enum name following DM_STATS_ (upper case)
- */
-#define MK_STATS_GET_COUNTER_FN(name, counter) \
-uint64_t dm_stats_get_ ## name(const struct dm_stats *dms, \
- uint64_t region_id, uint64_t area_id) \
-{ \
- return dm_stats_get_counter(dms, DM_STATS_ ## counter, \
- region_id, area_id); \
-}
-
-MK_STATS_GET_COUNTER_FN(reads, READS_COUNT)
-MK_STATS_GET_COUNTER_FN(reads_merged, READS_MERGED_COUNT)
-MK_STATS_GET_COUNTER_FN(read_sectors, READ_SECTORS_COUNT)
-MK_STATS_GET_COUNTER_FN(read_nsecs, READ_NSECS)
-MK_STATS_GET_COUNTER_FN(writes, WRITES_COUNT)
-MK_STATS_GET_COUNTER_FN(writes_merged, WRITES_MERGED_COUNT)
-MK_STATS_GET_COUNTER_FN(write_sectors, WRITE_SECTORS_COUNT)
-MK_STATS_GET_COUNTER_FN(write_nsecs, WRITE_NSECS)
-MK_STATS_GET_COUNTER_FN(io_in_progress, IO_IN_PROGRESS_COUNT)
-MK_STATS_GET_COUNTER_FN(io_nsecs, IO_NSECS)
-MK_STATS_GET_COUNTER_FN(weighted_io_nsecs, WEIGHTED_IO_NSECS)
-MK_STATS_GET_COUNTER_FN(total_read_nsecs, TOTAL_READ_NSECS)
-MK_STATS_GET_COUNTER_FN(total_write_nsecs, TOTAL_WRITE_NSECS)
-#undef MK_STATS_GET_COUNTER_FN
-
-/*
- * Floating point stats metric functions
- *
- * Called from dm_stats_get_metric() to calculate the value of
- * the requested metric.
- *
- * int _metric_name(const struct dm_stats *dms,
- * struct dm_stats_counters *c,
- * double *value);
- *
- * Calculate a metric value from the counter data for the given
- * identifiers and store it in the memory pointed to by value,
- * applying group or region aggregation if enabled.
- *
- * Return one on success or zero on failure.
- *
- * To add a new metric:
- *
- * o Add a new name to the dm_stats_metric_t enum.
- * o Create a _metric_fn() to calculate the new metric.
- * o Add _metric_fn to the _metrics function table
- * (entries in enum order).
- * o Do not add a new named public function for the metric -
- * users of new metrics are encouraged to convert to the enum
- * based metric interface.
- *
- */
-
-static int _rd_merges_per_sec(const struct dm_stats *dms, double *rrqm,
- uint64_t region_id, uint64_t area_id)
-{
- double mrgs;
- mrgs = (double) dm_stats_get_counter(dms, DM_STATS_READS_MERGED_COUNT,
- region_id, area_id);
-
- *rrqm = mrgs / (double) dms->interval_ns;
-
- return 1;
-}
-
-static int _wr_merges_per_sec(const struct dm_stats *dms, double *wrqm,
- uint64_t region_id, uint64_t area_id)
-{
- double mrgs;
- mrgs = (double) dm_stats_get_counter(dms, DM_STATS_WRITES_MERGED_COUNT,
- region_id, area_id);
-
- *wrqm = mrgs / (double) dms->interval_ns;
-
- return 1;
-}
-
-static int _reads_per_sec(const struct dm_stats *dms, double *rd_s,
- uint64_t region_id, uint64_t area_id)
-{
- double reads;
- reads = (double) dm_stats_get_counter(dms, DM_STATS_READS_COUNT,
- region_id, area_id);
-
- *rd_s = (reads * NSEC_PER_SEC) / (double) dms->interval_ns;
-
- return 1;
-}
-
-static int _writes_per_sec(const struct dm_stats *dms, double *wr_s,
- uint64_t region_id, uint64_t area_id)
-{
- double writes;
- writes = (double) dm_stats_get_counter(dms, DM_STATS_WRITES_COUNT,
- region_id, area_id);
-
- *wr_s = (writes * NSEC_PER_SEC) / (double) dms->interval_ns;
-
- return 1;
-}
-
-static int _read_sectors_per_sec(const struct dm_stats *dms, double *rsec_s,
- uint64_t region_id, uint64_t area_id)
-{
- double sect;
- sect = (double) dm_stats_get_counter(dms, DM_STATS_READ_SECTORS_COUNT,
- region_id, area_id);
-
- *rsec_s = (sect * (double) NSEC_PER_SEC) / (double) dms->interval_ns;
-
- return 1;
-}
-
-static int _write_sectors_per_sec(const struct dm_stats *dms, double *wsec_s,
- uint64_t region_id, uint64_t area_id)
-{
- double sect;
- sect = (double) dm_stats_get_counter(dms, DM_STATS_WRITE_SECTORS_COUNT,
- region_id, area_id);
-
- *wsec_s = (sect * (double) NSEC_PER_SEC) / (double) dms->interval_ns;
-
- return 1;
-}
-
-static int _average_request_size(const struct dm_stats *dms, double *arqsz,
- uint64_t region_id, uint64_t area_id)
-{
- double ios, sectors;
-
- ios = (double) (dm_stats_get_counter(dms, DM_STATS_READS_COUNT,
- region_id, area_id)
- + dm_stats_get_counter(dms, DM_STATS_WRITES_COUNT,
- region_id, area_id));
- sectors = (double) (dm_stats_get_counter(dms, DM_STATS_READ_SECTORS_COUNT,
- region_id, area_id)
- + dm_stats_get_counter(dms, DM_STATS_WRITE_SECTORS_COUNT,
- region_id, area_id));
-
- if (ios > 0.0)
- *arqsz = sectors / ios;
- else
- *arqsz = 0.0;
-
- return 1;
-}
-
-static int _average_queue_size(const struct dm_stats *dms, double *qusz,
- uint64_t region_id, uint64_t area_id)
-{
- double io_ticks;
- io_ticks = (double) dm_stats_get_counter(dms, DM_STATS_WEIGHTED_IO_NSECS,
- region_id, area_id);
-
- if (io_ticks > 0.0)
- *qusz = io_ticks / (double) dms->interval_ns;
- else
- *qusz = 0.0;
-
- return 1;
-}
-
-static int _average_wait_time(const struct dm_stats *dms, double *await,
- uint64_t region_id, uint64_t area_id)
-{
- uint64_t io_ticks, nr_ios;
-
- io_ticks = dm_stats_get_counter(dms, DM_STATS_READ_NSECS,
- region_id, area_id);
- io_ticks += dm_stats_get_counter(dms, DM_STATS_WRITE_NSECS,
- region_id, area_id);
-
- nr_ios = dm_stats_get_counter(dms, DM_STATS_READS_COUNT,
- region_id, area_id);
- nr_ios += dm_stats_get_counter(dms, DM_STATS_WRITES_COUNT,
- region_id, area_id);
-
- if (nr_ios > 0)
- *await = (double) io_ticks / (double) nr_ios;
- else
- *await = 0.0;
-
- return 1;
-}
-
-static int _average_rd_wait_time(const struct dm_stats *dms, double *await,
- uint64_t region_id, uint64_t area_id)
-{
- uint64_t rd_io_ticks, nr_rd_ios;
-
- rd_io_ticks = dm_stats_get_counter(dms, DM_STATS_READ_NSECS,
- region_id, area_id);
- nr_rd_ios = dm_stats_get_counter(dms, DM_STATS_READS_COUNT,
- region_id, area_id);
-
- /*
- * If rd_io_ticks is > 0 this should imply that nr_rd_ios is
- * also > 0 (unless a kernel bug exists). Test for both here
- * before using the IO count as a divisor (Coverity).
- */
- if (rd_io_ticks > 0 && nr_rd_ios > 0)
- *await = (double) rd_io_ticks / (double) nr_rd_ios;
- else
- *await = 0.0;
-
- return 1;
-}
-
-static int _average_wr_wait_time(const struct dm_stats *dms, double *await,
- uint64_t region_id, uint64_t area_id)
-{
- uint64_t wr_io_ticks, nr_wr_ios;
-
- wr_io_ticks = dm_stats_get_counter(dms, DM_STATS_WRITE_NSECS,
- region_id, area_id);
- nr_wr_ios = dm_stats_get_counter(dms, DM_STATS_WRITES_COUNT,
- region_id, area_id);
-
- /*
- * If wr_io_ticks is > 0 this should imply that nr_wr_ios is
- * also > 0 (unless a kernel bug exists). Test for both here
- * before using the IO count as a divisor (Coverity).
- */
- if (wr_io_ticks > 0 && nr_wr_ios > 0)
- *await = (double) wr_io_ticks / (double) nr_wr_ios;
- else
- *await = 0.0;
-
- return 1;
-}
-
-static int _throughput(const struct dm_stats *dms, double *tput,
- uint64_t region_id, uint64_t area_id)
-{
- uint64_t nr_ios;
-
- nr_ios = dm_stats_get_counter(dms, DM_STATS_READS_COUNT,
- region_id, area_id);
- nr_ios += dm_stats_get_counter(dms, DM_STATS_WRITES_COUNT,
- region_id, area_id);
-
- *tput = ((double) NSEC_PER_SEC * (double) nr_ios)
- / (double) (dms->interval_ns);
-
- return 1;
-}
-
-static int _utilization(const struct dm_stats *dms, double *util,
- uint64_t region_id, uint64_t area_id)
-{
- uint64_t io_nsecs, interval_ns = dms->interval_ns;
-
- /**
- * If io_nsec > interval_ns there is something wrong with the clock
- * for the last interval; do not allow a value > 100% utilization
- * to be passed to a dm_make_percent() call. We expect to see these
- * at startup if counters have not been cleared before the first read.
- *
- * A zero interval_ns is also an error since metrics cannot be
- * calculated without a defined interval - return zero and emit a
- * backtrace in this case.
- */
- io_nsecs = dm_stats_get_counter(dms, DM_STATS_IO_NSECS,
- region_id, area_id);
-
- if (!interval_ns) {
- *util = 0.0;
- return_0;
- }
-
- io_nsecs = ((io_nsecs < interval_ns) ? io_nsecs : interval_ns);
-
- *util = (double) io_nsecs / (double) interval_ns;
-
- return 1;
-}
-
-static int _service_time(const struct dm_stats *dms, double *svctm,
- uint64_t region_id, uint64_t area_id)
-{
- double tput, util;
-
- if (!_throughput(dms, &tput, region_id, area_id))
- return 0;
-
- if (!_utilization(dms, &util, region_id, area_id))
- return 0;
-
- util *= 100;
-
- /* avoid NAN with zero counter values */
- if ( (uint64_t) tput == 0 || (uint64_t) util == 0) {
- *svctm = 0.0;
- return 1;
- }
-
- *svctm = ((double) NSEC_PER_SEC * dm_percent_to_float(util))
- / (100.0 * tput);
-
- return 1;
-}
-
-/*
- * Table in enum order:
- * DM_STATS_RD_MERGES_PER_SEC,
- * DM_STATS_WR_MERGES_PER_SEC,
- * DM_STATS_READS_PER_SEC,
- * DM_STATS_WRITES_PER_SEC,
- * DM_STATS_READ_SECTORS_PER_SEC,
- * DM_STATS_WRITE_SECTORS_PER_SEC,
- * DM_STATS_AVERAGE_REQUEST_SIZE,
- * DM_STATS_AVERAGE_QUEUE_SIZE,
- * DM_STATS_AVERAGE_WAIT_TIME,
- * DM_STATS_AVERAGE_RD_WAIT_TIME,
- * DM_STATS_AVERAGE_WR_WAIT_TIME
- * DM_STATS_SERVICE_TIME,
- * DM_STATS_THROUGHPUT,
- * DM_STATS_UTILIZATION
- *
-*/
-
-typedef int (*_metric_fn_t)(const struct dm_stats *, double *,
- uint64_t, uint64_t);
-
-_metric_fn_t _metrics[DM_STATS_NR_METRICS] = {
- _rd_merges_per_sec,
- _wr_merges_per_sec,
- _reads_per_sec,
- _writes_per_sec,
- _read_sectors_per_sec,
- _write_sectors_per_sec,
- _average_request_size,
- _average_queue_size,
- _average_wait_time,
- _average_rd_wait_time,
- _average_wr_wait_time,
- _service_time,
- _throughput,
- _utilization
-};
-
-int dm_stats_get_metric(const struct dm_stats *dms, int metric,
- uint64_t region_id, uint64_t area_id, double *value)
-{
- if (!dms->interval_ns)
- return_0;
-
- /*
- * Decode DM_STATS_{REGION,AREA}_CURRENT here; counters will then
- * be returned for the actual current region and area.
- *
- * DM_STATS_WALK_GROUP is passed through to the counter methods -
- * aggregates for the group are returned and used to calculate
- * the metric for the group totals.
- */
- region_id = (region_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_region : region_id ;
- area_id = (area_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_area : area_id ;
-
- if (metric < 0 || metric >= DM_STATS_NR_METRICS) {
- log_error("Attempt to read invalid metric: %d", metric);
- return 0;
- }
-
- return _metrics[metric](dms, value, region_id, area_id);
-}
-
-/**
- * Methods for accessing stats metrics. All methods share the
- * following naming scheme and prototype:
- *
- * uint64_t dm_stats_get_metric(struct dm_stats *,
- * int, int,
- * uint64_t, uint64_t,
- * double *v)
- *
- * Where the two integer arguments are the region_id and area_id
- * respectively.
- *
- * name is the name of the metric (lower case)
- * metric is the part of the enum name following DM_STATS_ (upper case)
- */
-#define MK_STATS_GET_METRIC_FN(name, metric, meta) \
-int dm_stats_get_ ## name(const struct dm_stats *dms, double *meta, \
- uint64_t region_id, uint64_t area_id) \
-{ \
- return dm_stats_get_metric(dms, DM_STATS_ ## metric, \
- region_id, area_id, meta); \
-}
-
-MK_STATS_GET_METRIC_FN(rd_merges_per_sec, RD_MERGES_PER_SEC, rrqm)
-MK_STATS_GET_METRIC_FN(wr_merges_per_sec, WR_MERGES_PER_SEC, wrqm)
-MK_STATS_GET_METRIC_FN(reads_per_sec, READS_PER_SEC, rd_s)
-MK_STATS_GET_METRIC_FN(writes_per_sec, WRITES_PER_SEC, wr_s)
-MK_STATS_GET_METRIC_FN(read_sectors_per_sec, READ_SECTORS_PER_SEC, rsec_s)
-MK_STATS_GET_METRIC_FN(write_sectors_per_sec, WRITE_SECTORS_PER_SEC, wsec_s)
-MK_STATS_GET_METRIC_FN(average_request_size, AVERAGE_REQUEST_SIZE, arqsz)
-MK_STATS_GET_METRIC_FN(average_queue_size, AVERAGE_QUEUE_SIZE, qusz)
-MK_STATS_GET_METRIC_FN(average_wait_time, AVERAGE_WAIT_TIME, await)
-MK_STATS_GET_METRIC_FN(average_rd_wait_time, AVERAGE_RD_WAIT_TIME, await)
-MK_STATS_GET_METRIC_FN(average_wr_wait_time, AVERAGE_WR_WAIT_TIME, await)
-MK_STATS_GET_METRIC_FN(service_time, SERVICE_TIME, svctm)
-MK_STATS_GET_METRIC_FN(throughput, THROUGHPUT, tput)
-
-/*
- * Utilization is an exception since it used the dm_percent_t type in the
- * original named function based interface: preserve this behaviour for
- * backwards compatibility with existing users.
- *
- * The same metric may be accessed as a double via the enum based metric
- * interface.
- */
-int dm_stats_get_utilization(const struct dm_stats *dms, dm_percent_t *util,
- uint64_t region_id, uint64_t area_id)
-{
- double _util;
-
- if (!dm_stats_get_metric(dms, DM_STATS_UTILIZATION,
- region_id, area_id, &_util))
- return_0;
- /* scale up utilization value in the range [0.00..1.00] */
- *util = dm_make_percent(DM_PERCENT_1 * _util, DM_PERCENT_1);
- return 1;
-}
-
-void dm_stats_set_sampling_interval_ms(struct dm_stats *dms, uint64_t interval_ms)
-{
- /* All times use nsecs internally. */
- dms->interval_ns = interval_ms * NSEC_PER_MSEC;
-}
-
-void dm_stats_set_sampling_interval_ns(struct dm_stats *dms, uint64_t interval_ns)
-{
- dms->interval_ns = interval_ns;
-}
-
-uint64_t dm_stats_get_sampling_interval_ms(const struct dm_stats *dms)
-{
- /* All times use nsecs internally. */
- return (dms->interval_ns / NSEC_PER_MSEC);
-}
-
-uint64_t dm_stats_get_sampling_interval_ns(const struct dm_stats *dms)
-{
- /* All times use nsecs internally. */
- return (dms->interval_ns);
-}
-
-int dm_stats_set_program_id(struct dm_stats *dms, int allow_empty,
- const char *program_id)
-{
- if (!allow_empty && (!program_id || !strlen(program_id))) {
- log_error("Empty program_id not permitted without "
- "allow_empty=1");
- return 0;
- }
-
- if (!program_id)
- program_id = "";
-
- free(dms->program_id);
-
- if (!(dms->program_id = strdup(program_id)))
- return_0;
-
- return 1;
-}
-
-uint64_t dm_stats_get_current_region(const struct dm_stats *dms)
-{
- return dms->cur_region;
-}
-
-uint64_t dm_stats_get_current_area(const struct dm_stats *dms)
-{
- return dms->cur_area & ~DM_STATS_WALK_ALL;
-}
-
-int dm_stats_get_region_start(const struct dm_stats *dms, uint64_t *start,
- uint64_t region_id)
-{
- if (!dms || !dms->regions)
- return_0;
-
- /* start is unchanged when aggregating areas */
- if (region_id & DM_STATS_WALK_REGION)
- region_id &= ~DM_STATS_WALK_REGION;
-
- /* use start of first region as group start */
- if (region_id & DM_STATS_WALK_GROUP) {
- if (region_id == DM_STATS_WALK_GROUP)
- region_id = dms->cur_group;
- else
- region_id &= ~DM_STATS_WALK_GROUP;
- }
-
- *start = dms->regions[region_id].start;
- return 1;
-}
-
-int dm_stats_get_region_len(const struct dm_stats *dms, uint64_t *len,
- uint64_t region_id)
-{
- uint64_t i;
- if (!dms || !dms->regions)
- return_0;
-
- *len = 0;
-
- /* length is unchanged when aggregating areas */
- if (region_id & DM_STATS_WALK_REGION)
- region_id &= ~DM_STATS_WALK_REGION;
-
- if (region_id & DM_STATS_WALK_GROUP) {
- /* decode region / group ID */
- if (region_id == DM_STATS_WALK_GROUP)
- region_id = dms->cur_group;
- else
- region_id &= ~DM_STATS_WALK_GROUP;
-
- /* use sum of region sizes as group size */
- if (_stats_region_is_grouped(dms, region_id))
- _foreach_group_region(dms, dms->cur_group, i)
- *len += dms->regions[i].len;
- else {
- log_error("Group ID " FMTu64 " does not exist",
- region_id);
- return 0;
- }
- } else
- *len = dms->regions[region_id].len;
-
- return 1;
-}
-
-int dm_stats_get_region_area_len(const struct dm_stats *dms, uint64_t *len,
- uint64_t region_id)
-{
- if (!dms || !dms->regions)
- return_0;
-
- /* groups are not subdivided - area size equals group size */
- if (region_id & (DM_STATS_WALK_GROUP | DM_STATS_WALK_REGION))
- /* get_region_len will decode region_id */
- return dm_stats_get_region_len(dms, len, region_id);
-
- *len = dms->regions[region_id].step;
- return 1;
-}
-
-int dm_stats_get_current_region_start(const struct dm_stats *dms,
- uint64_t *start)
-{
- return dm_stats_get_region_start(dms, start, dms->cur_region);
-}
-
-int dm_stats_get_current_region_len(const struct dm_stats *dms,
- uint64_t *len)
-{
- return dm_stats_get_region_len(dms, len, dms->cur_region);
-}
-
-int dm_stats_get_current_region_area_len(const struct dm_stats *dms,
- uint64_t *step)
-{
- return dm_stats_get_region_area_len(dms, step, dms->cur_region);
-}
-
-int dm_stats_get_area_start(const struct dm_stats *dms, uint64_t *start,
- uint64_t region_id, uint64_t area_id)
-{
- struct dm_stats_region *region;
- if (!dms || !dms->regions)
- return_0;
-
- /* group or region area start equals region start */
- if (region_id & (DM_STATS_WALK_GROUP | DM_STATS_WALK_REGION))
- return dm_stats_get_region_start(dms, start, region_id);
-
- region = &dms->regions[region_id];
- *start = region->start + region->step * area_id;
- return 1;
-}
-
-int dm_stats_get_area_offset(const struct dm_stats *dms, uint64_t *offset,
- uint64_t region_id, uint64_t area_id)
-{
- if (!dms || !dms->regions)
- return_0;
-
- /* no areas for groups or aggregate regions */
- if (region_id & (DM_STATS_WALK_GROUP | DM_STATS_WALK_REGION))
- *offset = 0;
- else
- *offset = dms->regions[region_id].step * area_id;
-
- return 1;
-}
-
-int dm_stats_get_current_area_start(const struct dm_stats *dms,
- uint64_t *start)
-{
- return dm_stats_get_area_start(dms, start,
- dms->cur_region, dms->cur_area);
-}
-
-int dm_stats_get_current_area_offset(const struct dm_stats *dms,
- uint64_t *offset)
-{
- return dm_stats_get_area_offset(dms, offset,
- dms->cur_region, dms->cur_area);
-}
-
-int dm_stats_get_current_area_len(const struct dm_stats *dms,
- uint64_t *len)
-{
- return dm_stats_get_region_area_len(dms, len, dms->cur_region);
-}
-
-const char *dm_stats_get_region_program_id(const struct dm_stats *dms,
- uint64_t region_id)
-{
- const char *program_id = NULL;
-
- if (region_id & DM_STATS_WALK_GROUP)
- return dms->program_id;
-
- if (region_id & DM_STATS_WALK_REGION)
- region_id &= ~DM_STATS_WALK_REGION;
-
- program_id = dms->regions[region_id].program_id;
- return (program_id) ? program_id : "";
-}
-
-const char *dm_stats_get_region_aux_data(const struct dm_stats *dms,
- uint64_t region_id)
-{
- const char *aux_data = NULL;
-
- if (region_id & DM_STATS_WALK_GROUP)
- return "";
-
- if (region_id & DM_STATS_WALK_REGION)
- region_id &= ~DM_STATS_WALK_REGION;
-
- aux_data = dms->regions[region_id].aux_data;
- return (aux_data) ? aux_data : "" ;
-}
-
-int dm_stats_set_alias(struct dm_stats *dms, uint64_t group_id, const char *alias)
-{
- struct dm_stats_group *group = NULL;
- const char *old_alias = NULL;
-
- if (!dms->regions || !dms->groups || !alias)
- return_0;
-
- if (!_stats_region_is_grouped(dms, group_id)) {
- log_error("Cannot set alias for ungrouped region ID "
- FMTu64, group_id);
- return 0;
- }
-
- if (group_id & DM_STATS_WALK_GROUP) {
- if (group_id == DM_STATS_WALK_GROUP)
- group_id = dms->cur_group;
- else
- group_id &= ~DM_STATS_WALK_GROUP;
- }
-
- if (group_id != dms->regions[group_id].group_id) {
- /* dm_stats_set_alias() must be called on the group ID. */
- log_error("Cannot set alias for group member " FMTu64 ".",
- group_id);
- return 0;
- }
-
- group = &dms->groups[group_id];
- old_alias = group->alias;
-
- group->alias = strdup(alias);
- if (!group->alias) {
- log_error("Could not allocate memory for alias.");
- goto bad;
- }
-
- if (!_stats_set_aux(dms, group_id, dms->regions[group_id].aux_data)) {
- log_error("Could not set new aux_data");
- goto bad;
- }
-
- free((char *) old_alias);
-
- return 1;
-
-bad:
- free((char *) group->alias);
- group->alias = old_alias;
- return 0;
-}
-
-const char *dm_stats_get_alias(const struct dm_stats *dms, uint64_t id)
-{
- const struct dm_stats_region *region;
-
- id = (id == DM_STATS_REGION_CURRENT) ? dms->cur_region : id;
-
- if (id & DM_STATS_WALK_GROUP) {
- if (id == DM_STATS_WALK_GROUP)
- id = dms->cur_group;
- else
- id &= ~DM_STATS_WALK_GROUP;
- }
-
- region = &dms->regions[id];
- if (!_stats_region_is_grouped(dms, id)
- || !dms->groups[region->group_id].alias)
- return dms->name;
-
- return dms->groups[region->group_id].alias;
-}
-
-const char *dm_stats_get_current_region_program_id(const struct dm_stats *dms)
-{
- return dm_stats_get_region_program_id(dms, dms->cur_region);
-}
-
-const char *dm_stats_get_current_region_aux_data(const struct dm_stats *dms)
-{
- return dm_stats_get_region_aux_data(dms, dms->cur_region);
-}
-
-int dm_stats_get_region_precise_timestamps(const struct dm_stats *dms,
- uint64_t region_id)
-{
- struct dm_stats_region *region;
-
- if (region_id == DM_STATS_REGION_CURRENT)
- region_id = dms->cur_region;
-
- if (region_id == DM_STATS_WALK_GROUP)
- region_id = dms->cur_group;
- else if (region_id & DM_STATS_WALK_GROUP)
- region_id &= ~DM_STATS_WALK_GROUP;
-
- region = &dms->regions[region_id];
- return region->timescale == 1;
-}
-
-int dm_stats_get_current_region_precise_timestamps(const struct dm_stats *dms)
-{
- return dm_stats_get_region_precise_timestamps(dms,
- DM_STATS_REGION_CURRENT);
-}
-
-/*
- * Histogram access methods.
- */
-
-static void _sum_histogram_bins(const struct dm_stats *dms,
- struct dm_histogram *dmh_aggr,
- uint64_t region_id, uint64_t area_id)
-{
- struct dm_stats_region *region;
- struct dm_histogram_bin *bins;
- struct dm_histogram *dmh_cur;
- int bin;
-
- region = &dms->regions[region_id];
- dmh_cur = region->counters[area_id].histogram;
- bins = dmh_aggr->bins;
-
- for (bin = 0; bin < dmh_aggr->nr_bins; bin++)
- bins[bin].count += dmh_cur->bins[bin].count;
-}
-
-/*
- * Create an aggregate histogram for a sub-divided region or a group.
- */
-static struct dm_histogram *_aggregate_histogram(const struct dm_stats *dms,
- uint64_t region_id,
- uint64_t area_id)
-{
- struct dm_histogram *dmh_aggr, *dmh_cur, **dmh_cachep;
- uint64_t group_id = DM_STATS_GROUP_NOT_PRESENT;
- int bin, nr_bins, group = 1;
- size_t hist_size;
-
- if (area_id == DM_STATS_WALK_REGION) {
- /* region aggregation */
- group = 0;
- if (!_stats_region_present(&dms->regions[region_id]))
- return_NULL;
-
- if (!dms->regions[region_id].bounds)
- return_NULL;
-
- if (!dms->regions[region_id].counters)
- return dms->regions[region_id].bounds;
-
- if (dms->regions[region_id].histogram)
- return dms->regions[region_id].histogram;
-
- dmh_cur = dms->regions[region_id].counters[0].histogram;
- dmh_cachep = &dms->regions[region_id].histogram;
- nr_bins = dms->regions[region_id].bounds->nr_bins;
- } else {
- /* group aggregation */
- group_id = region_id;
- area_id = DM_STATS_WALK_GROUP;
- if (!_stats_group_id_present(dms, group_id))
- return_NULL;
-
- if (!dms->regions[group_id].bounds)
- return_NULL;
-
- if (!dms->regions[group_id].counters)
- return dms->regions[group_id].bounds;
-
- if (dms->groups[group_id].histogram)
- return dms->groups[group_id].histogram;
-
- dmh_cur = dms->regions[group_id].counters[0].histogram;
- dmh_cachep = &dms->groups[group_id].histogram;
- nr_bins = dms->regions[group_id].bounds->nr_bins;
- }
-
- hist_size = sizeof(*dmh_aggr)
- + nr_bins * sizeof(struct dm_histogram_bin);
-
- if (!(dmh_aggr = dm_pool_zalloc(dms->hist_mem, hist_size))) {
- log_error("Could not allocate group histogram");
- return 0;
- }
-
- dmh_aggr->nr_bins = dmh_cur->nr_bins;
- dmh_aggr->dms = dms;
-
- if (!group)
- _foreach_region_area(dms, region_id, area_id) {
- _sum_histogram_bins(dms, dmh_aggr, region_id, area_id);
- }
- else {
- _foreach_group_area(dms, group_id, region_id, area_id) {
- _sum_histogram_bins(dms, dmh_aggr, region_id, area_id);
- }
- }
-
- for (bin = 0; bin < nr_bins; bin++) {
- dmh_aggr->sum += dmh_aggr->bins[bin].count;
- dmh_aggr->bins[bin].upper = dmh_cur->bins[bin].upper;
- }
-
- /* cache aggregate histogram for subsequent access */
- *dmh_cachep = dmh_aggr;
-
- return dmh_aggr;
-}
-
-struct dm_histogram *dm_stats_get_histogram(const struct dm_stats *dms,
- uint64_t region_id,
- uint64_t area_id)
-{
- int aggr = 0;
-
- if (region_id == DM_STATS_REGION_CURRENT) {
- region_id = dms->cur_region;
- if (region_id & DM_STATS_WALK_GROUP) {
- region_id = dms->cur_group;
- aggr = 1;
- }
- } else if (region_id & DM_STATS_WALK_GROUP) {
- region_id &= ~DM_STATS_WALK_GROUP;
- aggr = 1;
- }
-
- area_id = (area_id == DM_STATS_AREA_CURRENT)
- ? dms->cur_area : area_id ;
-
- if (area_id == DM_STATS_WALK_REGION)
- aggr = 1;
-
- if (aggr)
- return _aggregate_histogram(dms, region_id, area_id);
-
- if (region_id & DM_STATS_WALK_REGION)
- region_id &= ~DM_STATS_WALK_REGION;
-
- if (!dms->regions[region_id].counters)
- return dms->regions[region_id].bounds;
-
- return dms->regions[region_id].counters[area_id].histogram;
-}
-
-int dm_histogram_get_nr_bins(const struct dm_histogram *dmh)
-{
- return dmh->nr_bins;
-}
-
-uint64_t dm_histogram_get_bin_lower(const struct dm_histogram *dmh, int bin)
-{
- return (!bin) ? 0 : dmh->bins[bin - 1].upper;
-}
-
-uint64_t dm_histogram_get_bin_upper(const struct dm_histogram *dmh, int bin)
-{
- return dmh->bins[bin].upper;
-}
-
-uint64_t dm_histogram_get_bin_width(const struct dm_histogram *dmh, int bin)
-{
- uint64_t upper, lower;
- upper = dm_histogram_get_bin_upper(dmh, bin);
- lower = dm_histogram_get_bin_lower(dmh, bin);
- return (upper - lower);
-}
-
-uint64_t dm_histogram_get_bin_count(const struct dm_histogram *dmh, int bin)
-{
- return dmh->bins[bin].count;
-}
-
-uint64_t dm_histogram_get_sum(const struct dm_histogram *dmh)
-{
- return dmh->sum;
-}
-
-dm_percent_t dm_histogram_get_bin_percent(const struct dm_histogram *dmh,
- int bin)
-{
- uint64_t value = dm_histogram_get_bin_count(dmh, bin);
- uint64_t width = dm_histogram_get_bin_width(dmh, bin);
- uint64_t total = dm_histogram_get_sum(dmh);
-
- double val = (double) value;
-
- if (!total || !value || !width)
- return DM_PERCENT_0;
-
- return dm_make_percent((uint64_t) val, total);
-}
-
-/*
- * Histogram string helper functions: used to construct histogram and
- * bin boundary strings from numeric data.
- */
-
-/*
- * Allocate an unbound histogram object with nr_bins bins. Only used
- * for histograms used to hold bounds values as arguments for calls to
- * dm_stats_create_region().
- */
-static struct dm_histogram *_alloc_dm_histogram(int nr_bins)
-{
- /* Allocate space for dm_histogram + nr_entries. */
- size_t size = sizeof(struct dm_histogram) +
- (unsigned) nr_bins * sizeof(struct dm_histogram_bin);
- return zalloc(size);
-}
-
-/*
- * Parse a histogram bounds string supplied by the user. The string
- * consists of a list of numbers, "n1,n2,n3,..." with optional 'ns',
- * 'us', 'ms', or 's' unit suffixes.
- *
- * The scale parameter indicates the timescale used for this region: one
- * for nanoscale resolution and NSEC_PER_MSEC for miliseconds.
- *
- * On return bounds contains a pointer to an array of uint64_t
- * histogram bounds values expressed in units of nanoseconds.
- */
-struct dm_histogram *dm_histogram_bounds_from_string(const char *bounds_str)
-{
- static const char _valid_chars[] = "0123456789,muns";
- uint64_t this_val = 0, mult = 1;
- const char *c, *v, *val_start;
- struct dm_histogram_bin *cur;
- struct dm_histogram *dmh;
- int nr_entries = 1;
- char *endptr;
-
- c = bounds_str;
-
- /* Count number of bounds entries. */
- while(*c)
- if (*(c++) == ',')
- nr_entries++;
-
- c = bounds_str;
-
- if (!(dmh = _alloc_dm_histogram(nr_entries)))
- return_0;
-
- dmh->nr_bins = nr_entries;
-
- cur = dmh->bins;
-
- do {
- for (v = _valid_chars; *v; v++)
- if (*c == *v)
- break;
-
- if (!*v) {
- stack;
- goto badchar;
- }
-
- if (*c == ',') {
- log_error("Empty histogram bin not allowed: %s",
- bounds_str);
- goto bad;
- } else {
- val_start = c;
- endptr = NULL;
-
- this_val = strtoull(val_start, &endptr, 10);
- if (!endptr) {
- log_error("Could not parse histogram bound.");
- goto bad;
- }
- c = endptr; /* Advance to units, comma, or end. */
-
- if (*c == 's') {
- mult = NSEC_PER_SEC;
- c++; /* Advance over 's'. */
- } else if (*(c + 1) == 's') {
- if (*c == 'm')
- mult = NSEC_PER_MSEC;
- else if (*c == 'u')
- mult = NSEC_PER_USEC;
- else if (*c == 'n')
- mult = 1;
- else {
- stack;
- goto badchar;
- }
- c += 2; /* Advance over 'ms', 'us', or 'ns'. */
- } else if (*c == ',')
- c++;
- else if (*c) { /* Expected ',' or NULL. */
- stack;
- goto badchar;
- }
-
- if (*c == ',')
- c++;
- this_val *= mult;
- (cur++)->upper = this_val;
- }
- } while (*c);
-
- /* Bounds histograms have no owner. */
- dmh->dms = NULL;
- dmh->region = NULL;
-
- return dmh;
-
-badchar:
- log_error("Invalid character in histogram: %c", *c);
-bad:
- free(dmh);
- return NULL;
-}
-
-struct dm_histogram *dm_histogram_bounds_from_uint64(const uint64_t *bounds)
-{
- const uint64_t *entry = bounds;
- struct dm_histogram_bin *cur;
- struct dm_histogram *dmh;
- int nr_entries = 1;
-
- if (!bounds || !bounds[0]) {
- log_error("Could not parse empty histogram bounds array");
- return 0;
- }
-
- /* Count number of bounds entries. */
- while(*entry)
- if (*(++entry))
- nr_entries++;
-
- entry = bounds;
-
- if (!(dmh = _alloc_dm_histogram(nr_entries)))
- return_0;
-
- dmh->nr_bins = nr_entries;
-
- cur = dmh->bins;
-
- while (*entry)
- (cur++)->upper = *(entry++);
-
- /* Bounds histograms have no owner. */
- dmh->dms = NULL;
- dmh->region = NULL;
-
- return dmh;
-}
-
-void dm_histogram_bounds_destroy(struct dm_histogram *bounds)
-{
- if (!bounds)
- return;
-
- /* Bounds histograms are not bound to any handle or region. */
- if (bounds->dms || bounds->region) {
- log_error("Freeing invalid histogram bounds pointer %p.",
- (void *) bounds);
- stack;
- }
- /* free() expects a (void *). */
- free((void *) bounds);
-}
-
-/*
- * Scale a bounds value down from nanoseconds to the largest possible
- * whole unit suffix.
- */
-static void _scale_bound_value_to_suffix(uint64_t *bound, const char **suffix)
-{
- *suffix = "ns";
- if (!(*bound % NSEC_PER_SEC)) {
- *bound /= NSEC_PER_SEC;
- *suffix = "s";
- } else if (!(*bound % NSEC_PER_MSEC)) {
- *bound /= NSEC_PER_MSEC;
- *suffix = "ms";
- } else if (!(*bound % NSEC_PER_USEC)) {
- *bound /= NSEC_PER_USEC;
- *suffix = "us";
- }
-}
-
-#define DM_HISTOGRAM_BOUNDS_MASK 0x30
-#define BOUNDS_LEN 64
-
-static int _make_bounds_string(char *buf, size_t size, uint64_t lower,
- uint64_t upper, int flags, int width)
-{
- char bound_buf[BOUNDS_LEN];
- const char *l_suff = NULL;
- const char *u_suff = NULL;
- const char *sep = "";
- int bounds = flags & DM_HISTOGRAM_BOUNDS_MASK;
-
- if (!bounds)
- return_0;
-
- *buf = '\0';
-
- if (flags & DM_HISTOGRAM_SUFFIX) {
- _scale_bound_value_to_suffix(&lower, &l_suff);
- _scale_bound_value_to_suffix(&upper, &u_suff);
- } else
- l_suff = u_suff = "";
-
- if (flags & DM_HISTOGRAM_VALUES)
- sep = ":";
-
- if (bounds > DM_HISTOGRAM_BOUNDS_LOWER) {
- /* Handle infinite uppermost bound. */
- if (upper == UINT64_MAX) {
- if (dm_snprintf(bound_buf, sizeof(bound_buf),
- ">" FMTu64 "%s", lower, l_suff) < 0)
- goto_out;
- /* Only display an 'upper' string for final bin. */
- bounds = DM_HISTOGRAM_BOUNDS_UPPER;
- } else {
- if (dm_snprintf(bound_buf, sizeof(bound_buf),
- FMTu64 "%s", upper, u_suff) < 0)
- goto_out;
- }
- } else if (bounds == DM_HISTOGRAM_BOUNDS_LOWER) {
- if ((dm_snprintf(bound_buf, sizeof(bound_buf), FMTu64 "%s",
- lower, l_suff)) < 0)
- goto_out;
- }
-
- switch (bounds) {
- case DM_HISTOGRAM_BOUNDS_LOWER:
- case DM_HISTOGRAM_BOUNDS_UPPER:
- return dm_snprintf(buf, size, "%*s%s", width, bound_buf, sep);
- case DM_HISTOGRAM_BOUNDS_RANGE:
- return dm_snprintf(buf, size, FMTu64 "%s-%s%s",
- lower, l_suff, bound_buf, sep);
- }
-out:
- return 0;
-}
-
-#define BOUND_WIDTH_NOSUFFIX 10 /* 999999999 nsecs */
-#define BOUND_WIDTH 6 /* bounds string up to 9999xs */
-#define COUNT_WIDTH 6 /* count string: up to 9999 */
-#define PERCENT_WIDTH 6 /* percent string : 0.00-100.00% */
-#define DM_HISTOGRAM_VALUES_MASK 0x06
-
-const char *dm_histogram_to_string(const struct dm_histogram *dmh, int bin,
- int width, int flags)
-{
- char buf[BOUNDS_LEN], bounds_buf[BOUNDS_LEN];
- int minwidth, bounds, values, start, last;
- uint64_t lower, upper, val_u64; /* bounds of the current bin. */
- /* Use the histogram pool for string building. */
- struct dm_pool *mem = dmh->dms->hist_mem;
- const char *sep = "";
- int bounds_width;
- ssize_t len = 0;
- float val_flt;
-
- bounds = flags & DM_HISTOGRAM_BOUNDS_MASK;
- values = flags & DM_HISTOGRAM_VALUES;
-
- if (bin < 0) {
- start = 0;
- last = dmh->nr_bins - 1;
- } else
- start = last = bin;
-
- minwidth = width;
-
- if (width < 0 || !values)
- width = minwidth = 0; /* no padding */
- else if (flags & DM_HISTOGRAM_PERCENT)
- width = minwidth = (width) ? : PERCENT_WIDTH;
- else if (flags & DM_HISTOGRAM_VALUES)
- width = minwidth = (width) ? : COUNT_WIDTH;
-
- if (values && !width)
- sep = ":";
-
- /* Set bounds string to the empty string. */
- bounds_buf[0] = '\0';
-
- if (!dm_pool_begin_object(mem, 64))
- return_0;
-
- for (bin = start; bin <= last; bin++) {
- if (bounds) {
- /* Default bounds width depends on time suffixes. */
- bounds_width = (!(flags & DM_HISTOGRAM_SUFFIX))
- ? BOUND_WIDTH_NOSUFFIX
- : BOUND_WIDTH ;
-
- bounds_width = (!width) ? width : bounds_width;
-
- lower = dm_histogram_get_bin_lower(dmh, bin);
- upper = dm_histogram_get_bin_upper(dmh, bin);
-
- len = sizeof(bounds_buf);
- len = _make_bounds_string(bounds_buf, len,
- lower, upper, flags,
- bounds_width);
- /*
- * Comma separates "bounds: value" pairs unless
- * --noheadings is used.
- */
- sep = (width || !values) ? "," : ":";
-
- /* Adjust width by real bounds length if set. */
- width -= (width) ? (len - (bounds_width + 1)) : 0;
-
- /* -ve width indicates specified width was overrun. */
- width = (width > 0) ? width : 0;
- }
-
- if (bin == last)
- sep = "";
-
- if (flags & DM_HISTOGRAM_PERCENT) {
- dm_percent_t pr;
- pr = dm_histogram_get_bin_percent(dmh, bin);
- val_flt = dm_percent_to_float(pr);
- len = dm_snprintf(buf, sizeof(buf), "%s%*.2f%%%s",
- bounds_buf, width, val_flt, sep);
- } else if (values) {
- val_u64 = dmh->bins[bin].count;
- len = dm_snprintf(buf, sizeof(buf), "%s%*"PRIu64"%s",
- bounds_buf, width, val_u64, sep);
- } else if (bounds)
- len = dm_snprintf(buf, sizeof(buf), "%s%s", bounds_buf,
- sep);
- else {
- *buf = '\0';
- len = 0;
- }
-
- if (len < 0)
- goto_bad;
-
- width = minwidth; /* re-set histogram column width. */
- if (!dm_pool_grow_object(mem, buf, (size_t) len))
- goto_bad;
- }
-
- if (!dm_pool_grow_object(mem, "\0", 1))
- goto_bad;
-
- return (const char *) dm_pool_end_object(mem);
-
-bad:
- dm_pool_abandon_object(mem);
- return NULL;
-}
-
-/*
- * A lightweight representation of an extent (region, area, file
- * system block or extent etc.). A table of extents can be used
- * to sort and to efficiently find holes or overlaps among a set
- * of tuples of the form (id, start, len).
- */
-struct _extent {
- struct dm_list list;
- uint64_t id;
- uint64_t start;
- uint64_t len;
-};
-
-/* last address in an extent */
-#define _extent_end(a) ((a)->start + (a)->len - 1)
-
-/* a and b must be sorted by increasing start sector */
-#define _extents_overlap(a, b) (_extent_end(a) > (b)->start)
-
-/*
- * Comparison function to sort extents in ascending start order.
- */
-static int _extent_start_compare(const void *p1, const void *p2)
-{
- const struct _extent *r1, *r2;
- r1 = (const struct _extent *) p1;
- r2 = (const struct _extent *) p2;
-
- if (r1->start < r2->start)
- return -1;
- else if (r1->start == r2->start)
- return 0;
- return 1;
-}
-
-static int _stats_create_group(struct dm_stats *dms, dm_bitset_t regions,
- const char *alias, uint64_t *group_id)
-{
- struct dm_stats_group *group;
- *group_id = dm_bit_get_first(regions);
-
- /* group has no regions? */
- if (*group_id == DM_STATS_GROUP_NOT_PRESENT)
- return_0;
-
- group = &dms->groups[*group_id];
-
- if (group->regions) {
- log_error(INTERNAL_ERROR "Unexpected group state while"
- "creating group ID bitmap" FMTu64, *group_id);
- return 0;
- }
-
- group->group_id = *group_id;
- group->regions = regions;
-
- if (alias)
- group->alias = strdup(alias);
- else
- group->alias = NULL;
-
- /* force an update of the group tag stored in aux_data */
- if (!_stats_set_aux(dms, *group_id, dms->regions[*group_id].aux_data))
- return 0;
-
- return 1;
-}
-
-static int _stats_group_check_overlap(const struct dm_stats *dms,
- dm_bitset_t regions, int count)
-{
- struct dm_list ext_list = DM_LIST_HEAD_INIT(ext_list);
- struct _extent *ext, *tmp, *next, *map = NULL;
- size_t map_size = (dms->max_region + 1) * sizeof(*map);
- int i = 0, id, overlap, merged;
-
- map = dm_pool_alloc(dms->mem, map_size);
- if (!map) {
- log_error("Could not allocate memory for region map");
- return 0;
- }
-
- /* build a table of extents in order of region_id */
- for (id = dm_bit_get_first(regions); id >= 0;
- id = dm_bit_get_next(regions, id)) {
- dm_list_init(&map[i].list);
- map[i].id = id;
- map[i].start = dms->regions[id].start;
- map[i].len = dms->regions[id].len;
- i++;
- }
-
- /* A single region cannot overlap itself. */
- if (i == 1) {
- dm_pool_free(dms->mem, map);
- return 1;
- }
-
- /* sort by extent.start */
- qsort(map, count, sizeof(*map), _extent_start_compare);
-
- for (i = 0; i < count; i++)
- dm_list_add(&ext_list, &map[i].list);
-
- overlap = 0;
-merge:
- merged = 0;
- dm_list_iterate_items_safe(ext, tmp, &ext_list) {
- next = dm_list_item(dm_list_next(&ext_list, &ext->list),
- struct _extent);
- if (!next)
- continue;
-
- if (_extents_overlap(ext, next)) {
- log_warn("WARNING: region IDs " FMTu64 " and "
- FMTu64 " overlap. Some events will be "
- "counted twice.", ext->id, next->id);
- /* merge larger extent into smaller */
- if (_extent_end(ext) > _extent_end(next)) {
- next->id = ext->id;
- next->len = ext->len;
- }
- if (ext->start < next->start)
- next->start = ext->start;
- dm_list_del(&ext->list);
- overlap = merged = 1;
- }
- }
- /* continue until no merge candidates remain */
- if (merged)
- goto merge;
-
- dm_pool_free(dms->mem, map);
- return (overlap == 0);
-}
-
-static void _stats_copy_histogram_bounds(struct dm_histogram *to,
- struct dm_histogram *from)
-{
- int i;
-
- to->nr_bins = from->nr_bins;
-
- for (i = 0; i < to->nr_bins; i++)
- to->bins[i].upper = from->bins[i].upper;
-}
-
-/*
- * Compare histogram bounds h1 and h2, and return 1 if they match (i.e.
- * have the same number of bins and identical bin boundary values), or 0
- * otherwise.
- */
-static int _stats_check_histogram_bounds(struct dm_histogram *h1,
- struct dm_histogram *h2)
-{
- int i;
-
- if (!h1 || !h2)
- return 0;
-
- if (h1->nr_bins != h2->nr_bins)
- return 0;
-
- for (i = 0; i < h1->nr_bins; i++)
- if (h1->bins[i].upper != h2->bins[i].upper)
- return 0;
- return 1;
-}
-
-/*
- * Create a new group in stats handle dms from the group description
- * passed in group.
- */
-int dm_stats_create_group(struct dm_stats *dms, const char *members,
- const char *alias, uint64_t *group_id)
-{
- struct dm_histogram *check = NULL, *bounds;
- int i, count = 0, precise = 0;
- dm_bitset_t regions;
-
- if (!dms->regions || !dms->groups) {
- log_error("Could not create group: no regions found.");
- return 0;
- };
-
- if (!(regions = dm_bitset_parse_list(members, NULL, 0))) {
- log_error("Could not parse list: '%s'", members);
- return 0;
- }
-
- if (!(check = dm_pool_zalloc(dms->hist_mem, sizeof(*check)))) {
- log_error("Could not allocate memory for bounds check");
- goto bad;
- }
-
- /* too many bits? */
- if ((*regions - 1) > dms->max_region) {
- log_error("Invalid region ID: %d", *regions - 1);
- goto bad;
- }
-
- /*
- * Check that each region_id in the bitmap meets the group
- * constraints: present, not already grouped, and if any
- * histogram is present that they all have the same bounds.
- */
- for (i = dm_bit_get_first(regions); i >= 0;
- i = dm_bit_get_next(regions, i)) {
- if (!dm_stats_region_present(dms, i)) {
- log_error("Region ID %d does not exist", i);
- goto bad;
- }
- if (_stats_region_is_grouped(dms, i)) {
- log_error("Region ID %d already a member of group ID "
- FMTu64, i, dms->regions[i].group_id);
- goto bad;
- }
- if (dms->regions[i].timescale == 1)
- precise++;
-
- /* check for matching histogram bounds */
- bounds = dms->regions[i].bounds;
- if (bounds && !check->nr_bins)
- _stats_copy_histogram_bounds(check, bounds);
- else if (bounds) {
- if (!_stats_check_histogram_bounds(check, bounds)) {
- log_error("All region histogram bounds "
- "must match exactly");
- goto bad;
- }
- }
- count++;
- }
-
- if (precise && (precise != count))
- log_warn("WARNING: Grouping regions with different clock resolution: "
- "precision may be lost.");
-
- if (!_stats_group_check_overlap(dms, regions, count))
- log_very_verbose("Creating group with overlapping regions.");
-
- if (!_stats_create_group(dms, regions, alias, group_id))
- goto bad;
-
- dm_pool_free(dms->hist_mem, check);
- return 1;
-
-bad:
- dm_pool_free(dms->hist_mem, check);
- dm_bitset_destroy(regions);
- return 0;
-}
-
-/*
- * Remove the specified group_id.
- */
-int dm_stats_delete_group(struct dm_stats *dms, uint64_t group_id,
- int remove_regions)
-{
- struct dm_stats_region *leader;
- dm_bitset_t regions;
- uint64_t i;
-
- if (group_id > dms->max_region) {
- log_error("Invalid group ID: " FMTu64, group_id);
- return 0;
- }
-
- if (!_stats_group_id_present(dms, group_id)) {
- log_error("Group ID " FMTu64 " does not exist", group_id);
- return 0;
- }
-
- regions = dms->groups[group_id].regions;
- leader = &dms->regions[group_id];
-
- /* delete all but the group leader */
- for (i = (*regions - 1); i > leader->region_id; i--) {
- if (dm_bit(regions, i)) {
- dm_bit_clear(regions, i);
- if (remove_regions && !dm_stats_delete_region(dms, i))
- log_warn("WARNING: Failed to delete region "
- FMTu64 " on %s.", i, dms->name);
- }
- }
-
- /* clear group and mark as not present */
- _stats_clear_group_regions(dms, group_id);
- _stats_group_destroy(&dms->groups[group_id]);
-
- /* delete leader or clear aux_data */
- if (remove_regions)
- return dm_stats_delete_region(dms, group_id);
- else if (!_stats_set_aux(dms, group_id, leader->aux_data))
- return 0;
-
- return 1;
-}
-
-uint64_t dm_stats_get_group_id(const struct dm_stats *dms, uint64_t region_id)
-{
- region_id = (region_id == DM_STATS_REGION_CURRENT)
- ? dms->cur_region : region_id;
-
- if (region_id & DM_STATS_WALK_GROUP) {
- if (region_id == DM_STATS_WALK_GROUP)
- return dms->cur_group;
- else
- return region_id & ~DM_STATS_WALK_GROUP;
- }
-
- if (region_id & DM_STATS_WALK_REGION)
- region_id &= ~DM_STATS_WALK_REGION;
-
- return dms->regions[region_id].group_id;
-}
-
-int dm_stats_get_group_descriptor(const struct dm_stats *dms,
- uint64_t group_id, char **buf)
-{
- dm_bitset_t regions = dms->groups[group_id].regions;
- size_t buflen;
-
- buflen = _stats_group_tag_len(dms, regions);
-
- *buf = dm_pool_alloc(dms->mem, buflen);
- if (!*buf) {
- log_error("Could not allocate memory for regions string");
- return 0;
- }
-
- if (!_stats_group_tag_fill(dms, regions, *buf, buflen))
- return 0;
-
- return 1;
-}
-
-#ifdef HAVE_LINUX_FIEMAP_H
-/*
- * Resize the group bitmap corresponding to group_id so that it can
- * contain at least num_regions members.
- */
-static int _stats_resize_group(struct dm_stats_group *group,
- uint64_t num_regions)
-{
- uint64_t last_bit = dm_bit_get_last(group->regions);
- dm_bitset_t new, old;
-
- if (last_bit >= num_regions) {
- log_error("Cannot resize group bitmap to " FMTu64
- " with bit " FMTu64 " set.", num_regions, last_bit);
- return 0;
- }
-
- log_very_verbose("Resizing group bitmap from " FMTu32 " to " FMTu64
- " (last_bit: " FMTu64 ").", group->regions[0],
- num_regions, last_bit);
-
- new = dm_bitset_create(NULL, (unsigned) num_regions);
- if (!new) {
- log_error("Could not allocate memory for new group bitmap.");
- return 0;
- }
-
- old = group->regions;
- dm_bit_copy(new, old);
- group->regions = new;
- dm_bitset_destroy(old);
- return 1;
-}
-
-/*
- * Group a table of region_ids corresponding to the extents of a file.
- */
-static int _stats_group_file_regions(struct dm_stats *dms, uint64_t *region_ids,
- uint64_t count, const char *alias)
-{
- dm_bitset_t regions = dm_bitset_create(NULL, dms->nr_regions);
- uint64_t i, group_id = DM_STATS_GROUP_NOT_PRESENT;
- char *members = NULL;
- size_t buflen;
-
- if (!regions) {
- log_error("Cannot map file: failed to allocate group bitmap.");
- return 0;
- }
-
- for (i = 0; i < count; i++)
- dm_bit_set(regions, region_ids[i]);
-
- buflen = _stats_group_tag_len(dms, regions);
- members = malloc(buflen);
-
- if (!members) {
- log_error("Cannot map file: failed to allocate group "
- "descriptor.");
- dm_bitset_destroy(regions);
- return 0;
- }
-
- if (!_stats_group_tag_fill(dms, regions, members, buflen))
- goto bad;
-
- /*
- * overlaps should not be possible: overlapping file extents
- * returned by FIEMAP imply a kernel bug or a corrupt fs.
- */
- if (!_stats_group_check_overlap(dms, regions, count))
- log_very_verbose("Creating group with overlapping regions.");
-
- if (!_stats_create_group(dms, regions, alias, &group_id))
- goto bad;
-
- free(members);
- return 1;
-bad:
- dm_bitset_destroy(regions);
- free(members);
- return 0;
-}
-
-static int _stats_add_file_extent(int fd, struct dm_pool *mem, uint64_t id,
- struct fiemap_extent *fm_ext)
-{
- struct _extent extent;
-
- /* final address of list is unknown */
- memset(&extent.list, 0, sizeof(extent.list));
-
- /* convert bytes to dm (512b) sectors */
- extent.start = fm_ext->fe_physical >> SECTOR_SHIFT;
- extent.len = fm_ext->fe_length >> SECTOR_SHIFT;
- extent.id = id;
-
- log_very_verbose("Extent " FMTu64 " on fd %d at " FMTu64 "+"
- FMTu64, extent.id, fd, extent.start, extent.len);
-
- if (!dm_pool_grow_object(mem, &extent,
- sizeof(extent))) {
- log_error("Cannot map file: failed to grow extent map.");
- return 0;
- }
- return 1;
-}
-
-/* test for the boundary of an extent */
-#define ext_boundary(ext, exp) \
-((ext).fe_logical != 0) && \
-((ext).fe_physical != (exp))
-
-/*
- * Copy fields from fiemap_extent 'from' to the fiemap_extent
- * pointed to by 'to'.
- */
-#define ext_copy(to, from) \
-do { \
- *(to) = *(from); \
-} while (0)
-
-static uint64_t _stats_map_extents(int fd, struct dm_pool *mem,
- struct fiemap *fiemap,
- struct fiemap_extent *fm_ext,
- struct fiemap_extent *fm_last,
- struct fiemap_extent *fm_pending,
- uint64_t next_extent,
- int *eof)
-{
- uint64_t expected = 0, nr_extents = next_extent;
- unsigned int i;
-
- /*
- * Loop over the returned extents adding the fm_pending extent
- * to the table of extents each time a discontinuity (or eof)
- * is detected.
- *
- * We use a pointer to fm_pending in the caller since it is
- * possible that logical extents comprising a single physical
- * extent are returned by successive FIEMAP calls.
- */
- for (i = 0; i < fiemap->fm_mapped_extents; i++) {
- expected = fm_last->fe_physical + fm_last->fe_length;
-
- if (fm_ext[i].fe_flags & FIEMAP_EXTENT_LAST)
- *eof = 1;
-
- /* cannot map extents that are not yet allocated. */
- if (fm_ext[i].fe_flags
- & (FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_DELALLOC))
- continue;
-
- /*
- * Begin a new extent if the current physical address differs
- * from the expected address yielded by fm_last.fe_physical +
- * fm_last.fe_length.
- *
- * A logical discontinuity is seen at the start of the file if
- * unwritten space exists before the first extent: do not add
- * any extent record until we have accumulated a non-zero length
- * in fm_pending.
- */
- if (fm_pending->fe_length &&
- ext_boundary(fm_ext[i], expected)) {
- if (!_stats_add_file_extent(fd, mem, nr_extents,
- fm_pending))
- goto_bad;
- nr_extents++;
- /* Begin a new pending extent. */
- ext_copy(fm_pending, fm_ext + i);
- } else {
- expected = 0;
- /* Begin a new pending extent for extent 0. If there is
- * a hole at the start of the file, the first allocated
- * extent will have a non-zero fe_logical. Detect this
- * case by testing fm_pending->fe_length: if no length
- * has been accumulated we are handling the first
- * physical extent of the file.
- */
- if (!fm_pending->fe_length || fm_ext[i].fe_logical == 0)
- ext_copy(fm_pending, fm_ext + i);
- else
- /* accumulate this logical extent's length */
- fm_pending->fe_length += fm_ext[i].fe_length;
- }
- *fm_last = fm_ext[i];
- }
-
- /*
- * If the file only has a single extent, no boundary is ever
- * detected to trigger addition of the first extent.
- */
- if (*eof || (fm_ext[i - 1].fe_logical == 0)) {
- _stats_add_file_extent(fd, mem, nr_extents, fm_pending);
- nr_extents++;
- }
-
- fiemap->fm_start = (fm_ext[i - 1].fe_logical +
- fm_ext[i - 1].fe_length);
-
- /* return the number of extents found in this call. */
- return nr_extents - next_extent;
-bad:
- /* signal mapping error to caller */
- *eof = -1;
- return 0;
-}
-
-/*
- * Read the extents of an open file descriptor into a table of struct _extent.
- *
- * Based on e2fsprogs/misc/filefrag.c::filefrag_fiemap().
- *
- * Copyright 2003 by Theodore Ts'o.
- *
- */
-static struct _extent *_stats_get_extents_for_file(struct dm_pool *mem, int fd,
- uint64_t *count)
-{
- struct fiemap_extent fm_last = {0}, fm_pending = {0}, *fm_ext = NULL;
- struct fiemap *fiemap = NULL;
- int eof = 0, nr_extents = 0;
- struct _extent *extents;
- unsigned long flags = 0;
- uint64_t *buf;
-
- /* grow temporary extent table in the pool */
- if (!dm_pool_begin_object(mem, sizeof(*extents)))
- return NULL;
-
- buf = zalloc(STATS_FIE_BUF_LEN);
- if (!buf) {
- log_error("Could not allocate memory for FIEMAP buffer.");
- goto bad;
- }
-
- /* initialise pointers into the ioctl buffer. */
- fiemap = (struct fiemap *) buf;
- fm_ext = &fiemap->fm_extents[0];
-
- /* space available per ioctl */
- *count = (STATS_FIE_BUF_LEN - sizeof(*fiemap))
- / sizeof(struct fiemap_extent);
-
- flags = FIEMAP_FLAG_SYNC;
-
- do {
- /* start of ioctl loop - zero size and set count to bufsize */
- fiemap->fm_length = ~0ULL;
- fiemap->fm_flags = flags;
- fiemap->fm_extent_count = *count;
-
- /* get count-sized chunk of extents */
- if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap) < 0) {
- if (errno == EBADR)
- log_err_once("FIEMAP failed with unknown "
- "flags %x.", fiemap->fm_flags);
- goto bad;
- }
-
- /* If 0 extents are returned, more ioctls are not needed */
- if (fiemap->fm_mapped_extents == 0)
- break;
-
- nr_extents += _stats_map_extents(fd, mem, fiemap, fm_ext,
- &fm_last, &fm_pending,
- nr_extents, &eof);
-
- /* check for extent mapping error */
- if (eof < 0)
- goto bad;
-
- } while (eof == 0);
-
- if (!nr_extents) {
- log_error("Cannot map file: no allocated extents.");
- goto bad;
- }
-
- /* return total number of extents */
- *count = nr_extents;
- extents = dm_pool_end_object(mem);
-
- /* free FIEMAP buffer. */
- free(buf);
-
- return extents;
-
-bad:
- *count = 0;
- dm_pool_abandon_object(mem);
- free(buf);
- return NULL;
-}
-
-#define MATCH_EXTENT(e, s, l) \
-(((e).start == (s)) && ((e).len == (l)))
-
-static struct _extent *_find_extent(uint64_t nr_extents, struct _extent *extents,
- uint64_t start, uint64_t len)
-{
- size_t i;
- for (i = 0; i < nr_extents; i++)
- if (MATCH_EXTENT(extents[i], start, len))
- return extents + i;
- return NULL;
-}
-
-/*
- * Clean up a table of region_id values that were created during a
- * failed dm_stats_create_regions_from_fd, or dm_stats_update_regions_from_fd
- * operation.
- */
-static void _stats_cleanup_region_ids(struct dm_stats *dms, uint64_t *regions,
- uint64_t nr_regions)
-{
- uint64_t i;
-
- for (i = 0; i < nr_regions; i++)
- if (!_stats_delete_region(dms, regions[i]))
- log_error("Could not delete region " FMTu64 ".", i);
-}
-
-/*
- * First update pass: prune no-longer-allocated extents from the group
- * and build a table of the remaining extents so that their creation
- * can be skipped in the second pass.
- */
-static int _stats_unmap_regions(struct dm_stats *dms, uint64_t group_id,
- struct dm_pool *mem, struct _extent *extents,
- struct _extent **old_extents, uint64_t *count,
- int *regroup)
-{
- struct dm_stats_region *region = NULL;
- struct dm_stats_group *group = NULL;
- uint64_t nr_kept, nr_old;
- struct _extent ext;
- int64_t i;
-
- group = &dms->groups[group_id];
-
- log_very_verbose("Checking for changed file extents in group ID "
- FMTu64, group_id);
-
- if (!dm_pool_begin_object(mem, sizeof(**old_extents))) {
- log_error("Could not allocate extent table.");
- return 0;
- }
-
- nr_kept = nr_old = 0; /* counts of old and retained extents */
-
- /*
- * First pass: delete de-allocated extents and set regroup=1 if
- * deleting the current group leader.
- */
- i = dm_bit_get_last(group->regions);
- for (; i >= 0; i = dm_bit_get_prev(group->regions, i)) {
- region = &dms->regions[i];
- nr_old++;
-
- if (extents && _find_extent(*count, extents,
- region->start, region->len)) {
- ext.start = region->start;
- ext.len = region->len;
- ext.id = i;
- nr_kept++;
-
- if (!dm_pool_grow_object(mem, &ext, sizeof(ext)))
- goto out;
-
- log_very_verbose("Kept region " FMTu64, i);
- } else {
-
- if (i == group_id)
- *regroup = 1;
-
- if (!_stats_delete_region(dms, i)) {
- log_error("Could not remove region ID " FMTu64,
- i);
- goto out;
- }
-
- log_very_verbose("Deleted region " FMTu64, i);
- }
- }
-
- *old_extents = dm_pool_end_object(mem);
- if (!*old_extents) {
- log_error("Could not finalize region extent table.");
- goto out;
- }
- log_very_verbose("Kept " FMTd64 " of " FMTd64 " old extents",
- nr_kept, nr_old);
- log_very_verbose("Found " FMTu64 " new extents",
- *count - nr_kept);
-
- return (int) nr_kept;
-out:
- dm_pool_abandon_object(mem);
- return -1;
-}
-
-/*
- * Create or update a set of regions representing the extents of a file
- * and return a table of uint64_t region_id values. The number of regions
- * created is returned in the memory pointed to by count (which must be
- * non-NULL).
- *
- * If group_id is not equal to DM_STATS_GROUP_NOT_PRESENT, it is assumed
- * that group_id corresponds to a group containing existing regions that
- * were mapped to this file at an earlier time: regions will be added or
- * removed to reflect the current status of the file.
- */
-static uint64_t *_stats_map_file_regions(struct dm_stats *dms, int fd,
- struct dm_histogram *bounds,
- int precise, uint64_t group_id,
- uint64_t *count, int *regroup)
-{
- struct _extent *extents = NULL, *old_extents = NULL;
- uint64_t *regions = NULL, fail_region, i, num_bits;
- struct dm_stats_group *group = NULL;
- struct dm_pool *extent_mem = NULL;
- struct _extent *old_ext;
- char *hist_arg = NULL;
- struct statfs fsbuf;
- int64_t nr_kept = 0;
- struct stat buf;
- int update;
-
- *count = 0;
- update = _stats_group_id_present(dms, group_id);
-
-#ifdef BTRFS_SUPER_MAGIC
- if (fstatfs(fd, &fsbuf)) {
- log_error("fstatfs failed for fd %d", fd);
- return 0;
- }
-
- if (fsbuf.f_type == BTRFS_SUPER_MAGIC) {
- log_error("Cannot map file: btrfs does not provide "
- "physical FIEMAP extent data.");
- return 0;
- }
-#endif
-
- if (fstat(fd, &buf)) {
- log_error("fstat failed for fd %d", fd);
- return 0;
- }
-
- if (!(buf.st_mode & S_IFREG)) {
- log_error("Not a regular file");
- return 0;
- }
-
- if (!dm_is_dm_major(major(buf.st_dev))) {
- log_error("Cannot map file: not a device-mapper device.");
- return 0;
- }
-
- /*
- * If regroup is set here, we are creating a new filemap: otherwise
- * we are updating a group with a valid group identifier in group_id.
- */
- if (update)
- log_very_verbose("Updating extents from fd %d with group ID "
- FMTu64 " on (%d:%d)", fd, group_id,
- major(buf.st_dev), minor(buf.st_dev));
- else
- log_very_verbose("Mapping extents from fd %d on (%d:%d)",
- fd, major(buf.st_dev), minor(buf.st_dev));
-
- /* Use a temporary, private pool for the extent table. This avoids
- * hijacking the dms->mem (region table) pool which would lead to
- * interleaving temporary allocations with dm_stats_list() data,
- * causing complications in the error path.
- */
- if (!(extent_mem = dm_pool_create("extents", sizeof(*extents))))
- return_NULL;
-
- if (!(extents = _stats_get_extents_for_file(extent_mem, fd, count))) {
- log_very_verbose("No extents found in fd %d", fd);
- if (!update)
- goto out;
- }
-
- if (update) {
- group = &dms->groups[group_id];
- if ((nr_kept = _stats_unmap_regions(dms, group_id, extent_mem,
- extents, &old_extents,
- count, regroup)) < 0)
- goto_out;
- }
-
- if (bounds)
- if (!(hist_arg = _build_histogram_arg(bounds, &precise)))
- goto_out;
-
- /* make space for end-of-table marker */
- if (!(regions = malloc((1 + *count) * sizeof(*regions)))) {
- log_error("Could not allocate memory for region IDs.");
- goto_out;
- }
-
- /*
- * Second pass (first for non-update case): create regions for
- * all extents not retained from the prior mapping, and insert
- * retained regions into the table of region_id values.
- *
- * If a regroup is not scheduled, set group bits for newly
- * created regions in the group leader bitmap.
- */
- for (i = 0; i < *count; i++) {
- if (update) {
- if ((old_ext = _find_extent((uint64_t) nr_kept,
- old_extents,
- extents[i].start,
- extents[i].len))) {
- regions[i] = old_ext->id;
- continue;
- }
- }
- if (!_stats_create_region(dms, regions + i, extents[i].start,
- extents[i].len, -1, precise, hist_arg,
- dms->program_id, "")) {
- log_error("Failed to create region " FMTu64 " of "
- FMTu64 " at " FMTu64 ".", i, *count,
- extents[i].start);
- goto out_remove;
- }
-
- log_very_verbose("Created new region mapping " FMTu64 "+" FMTu64
- " with region ID " FMTu64, extents[i].start,
- extents[i].len, regions[i]);
-
- if (!*regroup && update) {
- /* expand group bitmap */
- if (regions[i] > (group->regions[0] - 1)) {
- num_bits = regions[i] + *count;
- if (!_stats_resize_group(group, num_bits)) {
- log_error("Failed to resize group "
- "bitmap.");
- goto out_remove;
- }
- }
- dm_bit_set(group->regions, regions[i]);
- }
-
- }
- regions[*count] = DM_STATS_REGION_NOT_PRESENT;
-
- /* Update group leader aux_data for new group members. */
- if (!*regroup && update)
- if (!_stats_set_aux(dms, group_id,
- dms->regions[group_id].aux_data))
- log_error("Failed to update group aux_data.");
-
- if (bounds)
- free(hist_arg);
-
- /* the extent table will be empty if the file has been truncated. */
- if (extents)
- dm_pool_free(extent_mem, extents);
-
- dm_pool_destroy(extent_mem);
-
- return regions;
-
-out_remove:
- /* New region creation may begin to fail part-way through creating
- * a set of file mapped regions: in this case we need to roll back
- * the regions that were already created and return the handle to
- * a consistent state. A listed handle is required for this: use a
- * single list operation and call _stats_delete_region() directly
- * to avoid a @stats_list ioctl and list parsing for each region.
- */
- if (!dm_stats_list(dms, NULL))
- goto out;
-
- fail_region = i;
- _stats_cleanup_region_ids(dms, regions, fail_region);
- *count = 0;
-
-out:
- dm_pool_destroy(extent_mem);
- free(hist_arg);
- free(regions);
- return NULL;
-}
-
-uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
- int group, int precise,
- struct dm_histogram *bounds,
- const char *alias)
-{
- uint64_t *regions, count;
- int regroup = 1;
-
- if (alias && !group) {
- log_error("Cannot set alias without grouping regions.");
- return NULL;
- }
-
- if (!(regions = _stats_map_file_regions(dms, fd, bounds, precise,
- DM_STATS_GROUP_NOT_PRESENT,
- &count, ®roup)))
- return NULL;
-
- if (!group)
- return regions;
-
- /* refresh handle */
- if (!dm_stats_list(dms, NULL))
- goto_out;
-
- if (!_stats_group_file_regions(dms, regions, count, alias))
- goto_out;
-
- return regions;
-out:
- _stats_cleanup_region_ids(dms, regions, count);
- free(regions);
- return NULL;
-}
-
-uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
- uint64_t group_id)
-{
- struct dm_histogram *bounds = NULL;
- int nr_bins, precise, regroup;
- uint64_t *regions, count = 0;
- const char *alias = NULL;
-
- if (!dms->regions || !dm_stats_group_present(dms, group_id)) {
- if (!dm_stats_list(dms, dms->program_id)) {
- log_error("Could not obtain region list while "
- "updating group " FMTu64 ".", group_id);
- return NULL;
- }
- }
-
- if (!dm_stats_group_present(dms, group_id)) {
- log_error("Group ID " FMTu64 " does not exist.", group_id);
- return NULL;
- }
-
- /*
- * If the extent corresponding to the group leader's region has been
- * deallocated, _stats_map_file_regions() will remove the region and
- * the group. In this case, regroup will be set by the call and the
- * group will be re-created using saved values.
- */
- regroup = 0;
-
- /*
- * A copy of the alias is needed to re-create the group when regroup=1.
- */
- if (dms->groups[group_id].alias) {
- alias = strdup(dms->groups[group_id].alias);
- if (!alias) {
- log_error("Failed to allocate group alias string.");
- return NULL;
- }
- }
-
- if (dms->regions[group_id].bounds) {
- /*
- * A copy of the histogram bounds must be passed to
- * _stats_map_file_regions() to be used when creating new
- * regions: it is not safe to use the copy in the current group
- * leader since it may be destroyed during the first group
- * update pass.
- */
- nr_bins = dms->regions[group_id].bounds->nr_bins;
- bounds = _alloc_dm_histogram(nr_bins);
- if (!bounds) {
- log_error("Could not allocate memory for group "
- "histogram bounds.");
- goto out;
- }
- _stats_copy_histogram_bounds(bounds,
- dms->regions[group_id].bounds);
- }
-
- precise = (dms->regions[group_id].timescale == 1);
-
- regions = _stats_map_file_regions(dms, fd, bounds, precise,
- group_id, &count, ®roup);
-
- if (!regions)
- goto bad;
-
- if (!dm_stats_list(dms, NULL))
- goto bad;
-
- /* regroup if there are regions to group */
- if (regroup && (*regions != DM_STATS_REGION_NOT_PRESENT))
- if (!_stats_group_file_regions(dms, regions, count, alias))
- goto bad;
-
- free(bounds);
- free((char *) alias);
- return regions;
-bad:
- _stats_cleanup_region_ids(dms, regions, count);
- free(bounds);
- free(regions);
-out:
- free((char *) alias);
- return NULL;
-}
-#else /* !HAVE_LINUX_FIEMAP */
-uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
- int group, int precise,
- struct dm_histogram *bounds,
- const char *alias)
-{
- log_error("File mapping requires FIEMAP ioctl support.");
- return 0;
-}
-
-uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
- uint64_t group_id)
-{
- log_error("File mapping requires FIEMAP ioctl support.");
- return 0;
-}
-#endif /* HAVE_LINUX_FIEMAP */
-
-#ifdef DMFILEMAPD
-static const char *_filemapd_mode_names[] = {
- "inode",
- "path",
- NULL
-};
-
-dm_filemapd_mode_t dm_filemapd_mode_from_string(const char *mode_str)
-{
- dm_filemapd_mode_t mode = DM_FILEMAPD_FOLLOW_INODE;
- const char **mode_name;
-
- if (mode_str) {
- for (mode_name = _filemapd_mode_names; *mode_name; mode_name++)
- if (!strcmp(*mode_name, mode_str))
- break;
- if (*mode_name)
- mode = DM_FILEMAPD_FOLLOW_INODE
- + (mode_name - _filemapd_mode_names);
- else {
- log_error("Could not parse dmfilemapd mode: %s",
- mode_str);
- return DM_FILEMAPD_FOLLOW_NONE;
- }
- }
- return mode;
-}
-
-#define DM_FILEMAPD "dmfilemapd"
-#define NR_FILEMAPD_ARGS 7 /* includes argv[0] */
-/*
- * Start dmfilemapd to monitor the specified file descriptor, and to
- * update the group given by 'group_id' when the file's allocation
- * changes.
- *
- * usage: dmfilemapd <fd> <group_id> <mode> [<foreground>[<log_level>]]
- */
-int dm_stats_start_filemapd(int fd, uint64_t group_id, const char *path,
- dm_filemapd_mode_t mode, unsigned foreground,
- unsigned verbose)
-{
- char fd_str[8], group_str[8], fg_str[2], verb_str[2];
- const char *mode_str = _filemapd_mode_names[mode];
- char *args[NR_FILEMAPD_ARGS + 1];
- pid_t pid = 0;
- int argc = 0;
-
- if (fd < 0) {
- log_error("dmfilemapd file descriptor must be "
- "non-negative: %d", fd);
- return 0;
- }
-
- if (path[0] != '/') {
- log_error("Path argument must specify an absolute path.");
- return 0;
- }
-
- if (mode > DM_FILEMAPD_FOLLOW_PATH) {
- log_error("Invalid dmfilemapd mode argument: "
- "Must be DM_FILEMAPD_FOLLOW_INODE or "
- "DM_FILEMAPD_FOLLOW_PATH");
- return 0;
- }
-
- if (foreground > 1) {
- log_error("Invalid dmfilemapd foreground argument. "
- "Must be 0 or 1: %d.", foreground);
- return 0;
- }
-
- if (verbose > 3) {
- log_error("Invalid dmfilemapd verbose argument. "
- "Must be 0..3: %d.", verbose);
- return 0;
- }
-
- /* set argv[0] */
- args[argc++] = (char *) DM_FILEMAPD;
-
- /* set <fd> */
- if ((dm_snprintf(fd_str, sizeof(fd_str), "%d", fd)) < 0) {
- log_error("Could not format fd argument.");
- return 0;
- }
- args[argc++] = fd_str;
-
- /* set <group_id> */
- if ((dm_snprintf(group_str, sizeof(group_str), FMTu64, group_id)) < 0) {
- log_error("Could not format group_id argument.");
- return 0;
- }
- args[argc++] = group_str;
-
- /* set <path> */
- args[argc++] = (char *) path;
-
- /* set <mode> */
- args[argc++] = (char *) mode_str;
-
- /* set <foreground> */
- if ((dm_snprintf(fg_str, sizeof(fg_str), "%u", foreground)) < 0) {
- log_error("Could not format foreground argument.");
- return 0;
- }
- args[argc++] = fg_str;
-
- /* set <verbose> */
- if ((dm_snprintf(verb_str, sizeof(verb_str), "%u", verbose)) < 0) {
- log_error("Could not format verbose argument.");
- return 0;
- }
- args[argc++] = verb_str;
-
- /* terminate args[argc] */
- args[argc] = NULL;
-
- log_very_verbose("Spawning daemon as '%s %d " FMTu64 " %s %s %u %u'",
- *args, fd, group_id, path, mode_str,
- foreground, verbose);
-
- if (!foreground && ((pid = fork()) < 0)) {
- log_error("Failed to fork dmfilemapd process.");
- return 0;
- }
-
- if (pid > 0) {
- log_very_verbose("Forked dmfilemapd process as pid %d", pid);
- return 1;
- }
-
- execvp(args[0], args);
- log_sys_error("execvp", args[0]);
- if (!foreground)
- _exit(127);
- return 0;
-}
-# else /* !DMFILEMAPD */
-dm_filemapd_mode_t dm_filemapd_mode_from_string(const char *mode_str)
-{
- return 0;
-};
-
-int dm_stats_start_filemapd(int fd, uint64_t group_id, const char *path,
- dm_filemapd_mode_t mode, unsigned foreground,
- unsigned verbose)
-{
- log_error("dmfilemapd support disabled.");
- return 0;
-}
-#endif /* DMFILEMAPD */
-
-/*
- * Backward compatible dm_stats_create_region() implementations.
- *
- * Keep these at the end of the file to avoid adding clutter around the
- * current dm_stats_create_region() version.
- */
-
-#if defined(__GNUC__)
-int dm_stats_create_region_v1_02_106(struct dm_stats *dms, uint64_t *region_id,
- uint64_t start, uint64_t len, int64_t step,
- int precise, const char *program_id,
- const char *aux_data);
-int dm_stats_create_region_v1_02_106(struct dm_stats *dms, uint64_t *region_id,
- uint64_t start, uint64_t len, int64_t step,
- int precise, const char *program_id,
- const char *aux_data)
-{
- /* 1.02.106 lacks histogram argument. */
- return _stats_create_region(dms, region_id, start, len, step, precise,
- NULL, program_id, aux_data);
-}
-
-int dm_stats_create_region_v1_02_104(struct dm_stats *dms, uint64_t *region_id,
- uint64_t start, uint64_t len, int64_t step,
- const char *program_id, const char *aux_data);
-int dm_stats_create_region_v1_02_104(struct dm_stats *dms, uint64_t *region_id,
- uint64_t start, uint64_t len, int64_t step,
- const char *program_id, const char *aux_data)
-{
- /* 1.02.104 lacks histogram and precise arguments. */
- return _stats_create_region(dms, region_id, start, len, step, 0, NULL,
- program_id, aux_data);
-}
-#endif
5 years, 10 months
master - dmfilemapd: Move to libdm/dm-tools
by Joe Thornber
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=fededfbbbc5ed79c509...
Commit: fededfbbbc5ed79c509a34a0f8a681fa27e49533
Parent: 0524829af69935070b7b15c1bede8f7f4afc9a13
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Thu Jun 14 14:27:19 2018 +0100
Committer: Joe Thornber <ejt(a)redhat.com>
CommitterDate: Thu Jun 14 14:27:19 2018 +0100
dmfilemapd: Move to libdm/dm-tools
No longer uses any lvm code.
---
configure | 3 +-
configure.ac | 1 -
daemons/Makefile.in | 6 +-
daemons/dmfilemapd/.gitignore | 1 -
daemons/dmfilemapd/Makefile.in | 65 ---
daemons/dmfilemapd/dmfilemapd.c | 836 ---------------------------------------
libdm/dm-tools/.gitignore | 1 +
libdm/dm-tools/Makefile.in | 42 ++-
libdm/dm-tools/dmfilemapd.c | 835 ++++++++++++++++++++++++++++++++++++++
9 files changed, 876 insertions(+), 914 deletions(-)
diff --git a/configure b/configure
index ed3a315..4536b03 100755
--- a/configure
+++ b/configure
@@ -13611,7 +13611,7 @@ _ACEOF
################################################################################
-ac_config_files="$ac_config_files Makefile make.tmpl libdm/make.tmpl daemons/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile 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 daemons/lvmlockd/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/Makefile lib/Makefile include/lvm-version.h libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/dm-tools/Makefile libdm/libdevmapper.pc man/Makefile po/Makefile scri
pts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile tools/Makefile udev/Makefile"
+ac_config_files="$ac_config_files Makefile make.tmpl libdm/make.tmpl daemons/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/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 daemons/lvmlockd/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/Makefile lib/Makefile include/lvm-version.h libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/dm-tools/Makefile libdm/libdevmapper.pc man/Makefile po/Makefile scripts/blkdeactivate.sh scripts
/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile tools/Makefile udev/Makefile"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
@@ -14319,7 +14319,6 @@ do
"daemons/dmeventd/plugins/mirror/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/mirror/Makefile" ;;
"daemons/dmeventd/plugins/snapshot/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/snapshot/Makefile" ;;
"daemons/dmeventd/plugins/thin/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/thin/Makefile" ;;
- "daemons/dmfilemapd/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmfilemapd/Makefile" ;;
"daemons/lvmdbusd/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/lvmdbusd/Makefile" ;;
"daemons/lvmdbusd/lvmdbusd") CONFIG_FILES="$CONFIG_FILES daemons/lvmdbusd/lvmdbusd" ;;
"daemons/lvmdbusd/lvmdb.py") CONFIG_FILES="$CONFIG_FILES daemons/lvmdbusd/lvmdb.py" ;;
diff --git a/configure.ac b/configure.ac
index 42f19fd..05b13e0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1764,7 +1764,6 @@ 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
diff --git a/daemons/Makefile.in b/daemons/Makefile.in
index 8edaefa..5226a5e 100644
--- a/daemons/Makefile.in
+++ b/daemons/Makefile.in
@@ -44,12 +44,8 @@ ifeq ("@BUILD_LVMDBUSD@", "yes")
SUBDIRS += lvmdbusd
endif
-ifeq ("@BUILD_DMFILEMAPD@", "yes")
- SUBDIRS += dmfilemapd
-endif
-
ifeq ($(MAKECMDGOALS),distclean)
- SUBDIRS = cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd dmfilemapd
+ SUBDIRS = cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd
endif
include $(top_builddir)/make.tmpl
diff --git a/daemons/dmfilemapd/.gitignore b/daemons/dmfilemapd/.gitignore
deleted file mode 100644
index 6dcde30..0000000
--- a/daemons/dmfilemapd/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-dmfilemapd
diff --git a/daemons/dmfilemapd/Makefile.in b/daemons/dmfilemapd/Makefile.in
deleted file mode 100644
index 1afd6b8..0000000
--- a/daemons/dmfilemapd/Makefile.in
+++ /dev/null
@@ -1,65 +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)
-
-dmfilemapd: $(LIB_SHARED) dmfilemapd.o
- $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
- -o $@ dmfilemapd.o $(DL_LIBS) $(LIBS)
-
-dmfilemapd.static: $(LIB_STATIC) dmfilemapd.o
- $(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
diff --git a/daemons/dmfilemapd/dmfilemapd.c b/daemons/dmfilemapd/dmfilemapd.c
deleted file mode 100644
index 726ad6e..0000000
--- a/daemons/dmfilemapd/dmfilemapd.c
+++ /dev/null
@@ -1,836 +0,0 @@
-/*
- * Copyright (C) 2016 Red Hat, Inc. All rights reserved.
- *
- * This file is part of the device-mapper userspace tools.
- *
- * It includes tree drawing code based on pstree: http://psmisc.sourceforge.net/
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU General Public License v.2.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "tools/tool.h"
-
-#include "device_mapper/misc/dm-logging.h"
-
-#include "lib/config/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 = 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);
- }
- free((void *) fm->program_id);
- 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];
- }
- 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 = strdup(program_id);
- else
- fm->program_id = NULL;
- dm_stats_set_program_id(dms, 1, program_id);
-
- do {
- if (!dm_stats_group_present(dms, fm->group_id)) {
- log_info("Filemap group removed: exiting.");
- running = 0;
- continue;
- }
-
- if ((check = _filemap_monitor_get_events(fm)) < 0)
- goto bad;
-
- if (!check)
- goto wait;
-
- if ((check = _filemap_fd_check_changed(fm)) < 0)
- goto bad;
-
- if (check && !_update_regions(dms, fm))
- goto bad;
-
- running = !!fm->nr_regions;
- if (!running)
- continue;
-
-wait:
- _filemap_monitor_wait(FILEMAPD_WAIT_USECS);
-
- /* mode=inode termination condions */
- if (fm->mode == DM_FILEMAPD_FOLLOW_INODE) {
- if (!_filemap_monitor_check_file_unlinked(fm))
- goto bad;
- if (fm->deleted && !(open = _is_open(fm->path))) {
- log_info("File unlinked and closed: exiting.");
- running = 0;
- } else if (fm->deleted && open)
- log_verbose("File unlinked and open: "
- "continuing.");
- }
-
- if (!dm_stats_list(dms, NULL)) {
- log_error("Failed to list stats handle.");
- goto bad;
- }
-
- } while (running);
-
- _filemap_monitor_destroy(fm);
- dm_stats_destroy(dms);
- return 0;
-
-bad:
- _filemap_monitor_destroy(fm);
- dm_stats_destroy(dms);
- log_error("Exiting");
- return 1;
-}
-
-static const char * const _mode_names[] = {
- "inode",
- "path"
-};
-
-/*
- * dmfilemapd <fd> <group_id> <path> <mode> [<foreground>[<log_level>]]
- */
-int main(int argc, char **argv)
-{
- struct filemap_monitor fm;
-
- memset(&fm, 0, sizeof(fm));
-
- if (!_parse_args(argc, argv, &fm)) {
- 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)) {
- free(fm.path);
- return 1;
- }
-
- return _dmfilemapd(&fm);
-}
diff --git a/libdm/dm-tools/.gitignore b/libdm/dm-tools/.gitignore
index c42a5e0..5274cda 100644
--- a/libdm/dm-tools/.gitignore
+++ b/libdm/dm-tools/.gitignore
@@ -1 +1,2 @@
dmsetup
+dmfilemapd
diff --git a/libdm/dm-tools/Makefile.in b/libdm/dm-tools/Makefile.in
index b8d83ac..39d411d 100644
--- a/libdm/dm-tools/Makefile.in
+++ b/libdm/dm-tools/Makefile.in
@@ -16,8 +16,6 @@ srcdir = @srcdir@
top_srcdir = @top_srcdir@
top_builddir = @top_builddir@
-SOURCES=\
-
SOURCES2=\
dmsetup.c
@@ -36,8 +34,6 @@ CLEAN_TARGETS = $(TARGETS_DM) \
include $(top_builddir)/libdm/make.tmpl
-device-mapper: $(TARGETS_DM)
-
CFLAGS_dmsetup.o += $(UDEV_CFLAGS) $(EXTRA_EXEC_CFLAGS)
dmsetup: dmsetup.o
@@ -67,3 +63,41 @@ install_dmsetup_static: dmsetup.static
install_device-mapper: $(INSTALL_DMSETUP_TARGETS)
install: install_device-mapper
+
+# dmfilemapd support
+ifeq ("@BUILD_DMFILEMAPD@", "yes")
+ SOURCES += dmfilemapd.c
+ TARGETS_DM += dmfilemapd
+ .PHONY: install_dmfilemapd install_dmfilemapd_static
+ INSTALL_DMFILEMAPD_TARGETS = install_dmfilemapd_dynamic
+ ifeq ("@STATIC_LINK@", "yes")
+ TARGETS_DM += dmfilemapd.static
+ else
+ TARGETS_DM += dmfilemapd
+ endif
+ CLEAN_TARGETS+= dmfilemapd.static
+
+ CFLAGS_dmfilemapd.o += $(EXTRA_EXEC_CFLAGS)
+
+ dmfilemapd: $(LIB_SHARED) dmfilemapd.o
+ $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
+ -o $@ dmfilemapd.o -L$(top_builddir)/libdm -ldevmapper $(DL_LIBS) $(LIBS)
+
+ dmfilemapd.static: $(LIB_STATIC) dmfilemapd.o
+ $(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -static -L$(interfacebuilddir) \
+ -o $@ dmfilemapd.o $(DL_LIBS) $(LIBS) $(STATIC_LIBS)
+
+ 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_device-mapper: install_dmfilemapd
+
+ install: install_dmfilemapd
+endif
+
+device-mapper: $(TARGETS_DM)
diff --git a/libdm/dm-tools/dmfilemapd.c b/libdm/dm-tools/dmfilemapd.c
new file mode 100644
index 0000000..0b17c2b
--- /dev/null
+++ b/libdm/dm-tools/dmfilemapd.c
@@ -0,0 +1,835 @@
+/*
+ * 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 "util.h"
+#include "libdm/misc/dm-logging.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 "libdm/misc/kdev_t.h"
+#else
+# define MAJOR(x) major((x))
+# define MINOR(x) minor((x))
+# define MKDEV(x,y) makedev((x),(y))
+#endif
+
+#define DEFAULT_PROC_DIR "/proc"
+
+/* limit to two updates/sec */
+#define FILEMAPD_WAIT_USECS 500000
+
+/* 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 = 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);
+ }
+ free((void *) fm->program_id);
+ 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];
+ }
+ 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 = strdup(program_id);
+ else
+ fm->program_id = NULL;
+ dm_stats_set_program_id(dms, 1, program_id);
+
+ do {
+ if (!dm_stats_group_present(dms, fm->group_id)) {
+ log_info("Filemap group removed: exiting.");
+ running = 0;
+ continue;
+ }
+
+ if ((check = _filemap_monitor_get_events(fm)) < 0)
+ goto bad;
+
+ if (!check)
+ goto wait;
+
+ if ((check = _filemap_fd_check_changed(fm)) < 0)
+ goto bad;
+
+ if (check && !_update_regions(dms, fm))
+ goto bad;
+
+ running = !!fm->nr_regions;
+ if (!running)
+ continue;
+
+wait:
+ _filemap_monitor_wait(FILEMAPD_WAIT_USECS);
+
+ /* mode=inode termination condions */
+ if (fm->mode == DM_FILEMAPD_FOLLOW_INODE) {
+ if (!_filemap_monitor_check_file_unlinked(fm))
+ goto bad;
+ if (fm->deleted && !(open = _is_open(fm->path))) {
+ log_info("File unlinked and closed: exiting.");
+ running = 0;
+ } else if (fm->deleted && open)
+ log_verbose("File unlinked and open: "
+ "continuing.");
+ }
+
+ if (!dm_stats_list(dms, NULL)) {
+ log_error("Failed to list stats handle.");
+ goto bad;
+ }
+
+ } while (running);
+
+ _filemap_monitor_destroy(fm);
+ dm_stats_destroy(dms);
+ return 0;
+
+bad:
+ _filemap_monitor_destroy(fm);
+ dm_stats_destroy(dms);
+ log_error("Exiting");
+ return 1;
+}
+
+static const char * const _mode_names[] = {
+ "inode",
+ "path"
+};
+
+/*
+ * dmfilemapd <fd> <group_id> <path> <mode> [<foreground>[<log_level>]]
+ */
+int main(int argc, char **argv)
+{
+ struct filemap_monitor fm;
+
+ memset(&fm, 0, sizeof(fm));
+
+ if (!_parse_args(argc, argv, &fm)) {
+ 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)) {
+ free(fm.path);
+ return 1;
+ }
+
+ return _dmfilemapd(&fm);
+}
5 years, 10 months
master - dmsetup: move to libdm/dm-tools/dmsetup
by Joe Thornber
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=0524829af69935070b7...
Commit: 0524829af69935070b7b15c1bede8f7f4afc9a13
Parent: e53cfc6a88d7acafde6ba3fa293bcf15b9a033bc
Author: Joe Thornber <ejt(a)redhat.com>
AuthorDate: Thu Jun 14 13:08:33 2018 +0100
Committer: Joe Thornber <ejt(a)redhat.com>
CommitterDate: Thu Jun 14 13:10:17 2018 +0100
dmsetup: move to libdm/dm-tools/dmsetup
links against libdevmapper again.
no longer includes code from lvm.
---
configure | 6 +-
configure.ac | 1 +
libdm/Makefile.in | 2 +
libdm/dm-tools/.gitignore | 1 +
libdm/dm-tools/Makefile.in | 69 +
libdm/dm-tools/dmsetup.c | 7463 ++++++++++++++++++++++++++++++++++++++++++++
libdm/dm-tools/util.h | 79 +
tools/Makefile.in | 40 +-
tools/dmsetup.c | 7460 -------------------------------------------
9 files changed, 7623 insertions(+), 7498 deletions(-)
diff --git a/configure b/configure
index 323ab4a..ed3a315 100755
--- a/configure
+++ b/configure
@@ -11527,7 +11527,7 @@ if ${am_cv_pathless_PYTHON+:} false; then :
$as_echo_n "(cached) " >&6
else
- for am_cv_pathless_PYTHON in python python2 python3 python3.9 python3.8 python3.7 python3.6 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do
+ for am_cv_pathless_PYTHON in python python2 python3 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do
test "$am_cv_pathless_PYTHON" = none && break
prog="import sys
# split strings by '.' and convert to numeric. Append some zeros
@@ -13611,7 +13611,7 @@ _ACEOF
################################################################################
-ac_config_files="$ac_config_files Makefile make.tmpl libdm/make.tmpl daemons/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile 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 daemons/lvmlockd/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/Makefile lib/Makefile include/lvm-version.h libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc man/Makefile po/Makefile scripts/blkdeactivate.sh scr
ipts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_lvmlocking_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile tools/Makefile udev/Makefile"
+ac_config_files="$ac_config_files Makefile make.tmpl libdm/make.tmpl daemons/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile 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 daemons/lvmlockd/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/Makefile lib/Makefile include/lvm-version.h libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/dm-tools/Makefile libdm/libdevmapper.pc man/Makefile po/Makefile scri
pts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile tools/Makefile udev/Makefile"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
@@ -14340,6 +14340,7 @@ do
"libdaemon/client/Makefile") CONFIG_FILES="$CONFIG_FILES libdaemon/client/Makefile" ;;
"libdaemon/server/Makefile") CONFIG_FILES="$CONFIG_FILES libdaemon/server/Makefile" ;;
"libdm/Makefile") CONFIG_FILES="$CONFIG_FILES libdm/Makefile" ;;
+ "libdm/dm-tools/Makefile") CONFIG_FILES="$CONFIG_FILES libdm/dm-tools/Makefile" ;;
"libdm/libdevmapper.pc") CONFIG_FILES="$CONFIG_FILES libdm/libdevmapper.pc" ;;
"man/Makefile") CONFIG_FILES="$CONFIG_FILES man/Makefile" ;;
"po/Makefile") CONFIG_FILES="$CONFIG_FILES po/Makefile" ;;
@@ -14359,7 +14360,6 @@ do
"scripts/lvm2_lvmpolld_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmpolld_systemd_red_hat.service" ;;
"scripts/lvm2_lvmpolld_systemd_red_hat.socket") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmpolld_systemd_red_hat.socket" ;;
"scripts/lvm2_lvmlockd_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmlockd_systemd_red_hat.service" ;;
- "scripts/lvm2_lvmlocking_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmlocking_systemd_red_hat.service" ;;
"scripts/lvm2_monitoring_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_monitoring_init_red_hat" ;;
"scripts/lvm2_monitoring_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_monitoring_systemd_red_hat.service" ;;
"scripts/lvm2_pvscan_systemd_red_hat@.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_pvscan_systemd_red_hat@.service" ;;
diff --git a/configure.ac b/configure.ac
index 4c431e4..42f19fd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1785,6 +1785,7 @@ libdaemon/Makefile
libdaemon/client/Makefile
libdaemon/server/Makefile
libdm/Makefile
+libdm/dm-tools/Makefile
libdm/libdevmapper.pc
man/Makefile
po/Makefile
diff --git a/libdm/Makefile.in b/libdm/Makefile.in
index eeef080..6472da7 100644
--- a/libdm/Makefile.in
+++ b/libdm/Makefile.in
@@ -16,6 +16,8 @@ srcdir = @srcdir@
top_srcdir = @top_srcdir@
top_builddir = @top_builddir@
+SUBDIRS=dm-tools
+
SOURCES =\
datastruct/bitset.c \
datastruct/hash.c \
diff --git a/libdm/dm-tools/.gitignore b/libdm/dm-tools/.gitignore
new file mode 100644
index 0000000..c42a5e0
--- /dev/null
+++ b/libdm/dm-tools/.gitignore
@@ -0,0 +1 @@
+dmsetup
diff --git a/libdm/dm-tools/Makefile.in b/libdm/dm-tools/Makefile.in
new file mode 100644
index 0000000..b8d83ac
--- /dev/null
+++ b/libdm/dm-tools/Makefile.in
@@ -0,0 +1,69 @@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES=\
+
+SOURCES2=\
+ dmsetup.c
+
+TARGETS_DM = dmsetup
+
+INSTALL_DMSETUP_TARGETS = install_dmsetup_dynamic
+
+ifeq ("@STATIC_LINK@", "yes")
+ TARGETS_DM += dmsetup.static
+ INSTALL_DMSETUP_TARGETS += install_dmsetup_static
+endif
+
+CLEAN_TARGETS = $(TARGETS_DM) \
+ dmsetup.static \
+ $(LDDEPS) .exported_symbols_generated
+
+include $(top_builddir)/libdm/make.tmpl
+
+device-mapper: $(TARGETS_DM)
+
+CFLAGS_dmsetup.o += $(UDEV_CFLAGS) $(EXTRA_EXEC_CFLAGS)
+
+dmsetup: dmsetup.o
+ @echo " [CC] $@"
+ $(Q) $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
+ -o $@ $+ $(LIBS) -L$(top_builddir)/libdm -ldevmapper -lm
+
+dmsetup.static: dmsetup.o $(INTERNAL_LIBS)
+ @echo " [CC] $@"
+ $(Q) $(CC) $(CFLAGS) $(LDFLAGS) -static -L$(interfacebuilddir) \
+ -o $@ $+ $(M_LIBS) $(PTHREAD_LIBS) $(STATIC_LIBS) $(LIBS)
+
+all: device-mapper
+
+.PHONY: install_dmsetup_dynamic install_dmsetup_static
+
+install_dmsetup_dynamic: dmsetup
+ @echo " [INSTALL] $<"
+ $(Q) $(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
+ $(Q) $(LN_S) -f $(<F) $(sbindir)/dmstats
+
+install_dmsetup_static: dmsetup.static
+ @echo " [INSTALL] $<"
+ $(Q) $(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
+ $(Q) $(LN_S) -f $(<F) $(staticdir)/dmstats
+
+install_device-mapper: $(INSTALL_DMSETUP_TARGETS)
+
+install: install_device-mapper
diff --git a/libdm/dm-tools/dmsetup.c b/libdm/dm-tools/dmsetup.c
new file mode 100644
index 0000000..83786f4
--- /dev/null
+++ b/libdm/dm-tools/dmsetup.c
@@ -0,0 +1,7463 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2005-2007 NEC Corporation
+ *
+ * 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
+ */
+
+// For canonicalize_file_name()
+#define _GNU_SOURCE
+#include <stdlib.h>
+
+#include "libdm/misc/dm-logging.h"
+#include "libdm/dm-tools/util.h"
+#include "configure.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <langinfo.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef UDEV_SYNC_SUPPORT
+# include <sys/types.h>
+# include <sys/ipc.h>
+# include <sys/sem.h>
+# include <libudev.h>
+#endif
+
+/* FIXME Unused so far */
+#undef HAVE_SYS_STATVFS_H
+
+#ifdef HAVE_SYS_STATVFS_H
+# include <sys/statvfs.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_TIMERFD_H
+# include <sys/timerfd.h>
+#endif
+
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+
+#ifdef HAVE_GETOPTLONG
+# include <getopt.h>
+# define GETOPTLONG_FN(a, b, c, d, e) getopt_long((a), (b), (c), (d), (e))
+# define OPTIND_INIT 0
+#else
+struct option {
+};
+# define GETOPTLONG_FN(a, b, c, d, e) getopt((a), (b), (c))
+# define OPTIND_INIT 1
+#endif
+
+#ifndef TEMP_FAILURE_RETRY
+# define TEMP_FAILURE_RETRY(expression) \
+ (__extension__ \
+ ({ long int __result; \
+ do __result = (long int) (expression); \
+ while (__result == -1L && errno == EINTR); \
+ __result; }))
+#endif
+
+#ifdef __linux__
+# include "libdm/misc/kdev_t.h"
+#else
+# define MAJOR(x) major((x))
+# define MINOR(x) minor((x))
+# define MKDEV(x,y) makedev((x),(y))
+#endif
+
+#define LINE_SIZE 4096
+#define ARGS_MAX 256
+#define LOOP_TABLE_SIZE (PATH_MAX + 255)
+
+#define DEFAULT_DM_DEV_DIR "/dev/"
+
+#define DM_DEV_DIR_ENV_VAR_NAME "DM_DEV_DIR"
+#define DM_UDEV_COOKIE_ENV_VAR_NAME "DM_UDEV_COOKIE"
+
+/* FIXME Should be imported */
+#ifndef DM_MAX_TYPE_NAME
+# define DM_MAX_TYPE_NAME 16
+#endif
+
+/* FIXME Should be elsewhere */
+#define SECTOR_SHIFT 9L
+
+/* program_id used for dmstats-managed statistics regions */
+#define DM_STATS_PROGRAM_ID "dmstats"
+
+/*
+ * Basic commands this code implments.
+ */
+typedef enum {
+ DMSETUP_CMD = 0,
+ LOSETUP_CMD = 1,
+ DMLOSETUP_CMD = 2,
+ DMSTATS_CMD = 3,
+ DMSETUP_STATS_CMD = 4,
+ DEVMAP_NAME_CMD = 5
+} cmd_name_t;
+
+typedef enum {
+ DMSETUP_TYPE = 0,
+ LOSETUP_TYPE = 1,
+ STATS_TYPE = 2,
+ DEVMAP_NAME_TYPE = 3
+} cmd_type_t;
+
+#define DMSETUP_CMD_NAME "dmsetup"
+#define LOSETUP_CMD_NAME "losetup"
+#define DMLOSETUP_CMD_NAME "dmlosetup"
+#define DMSTATS_CMD_NAME "dmstats"
+#define DMSETUP_STATS_CMD_NAME "dmsetup stats"
+#define DEVMAP_NAME_CMD_NAME "devmap_name"
+
+static const struct {
+ cmd_name_t command;
+ const char name[14];
+ cmd_type_t type;
+} _base_commands[] = {
+ { DMSETUP_CMD, DMSETUP_CMD_NAME, DMSETUP_TYPE },
+ { LOSETUP_CMD, LOSETUP_CMD_NAME, LOSETUP_TYPE },
+ { DMLOSETUP_CMD, DMLOSETUP_CMD_NAME, LOSETUP_TYPE },
+ { DMSTATS_CMD, DMSTATS_CMD_NAME, STATS_TYPE },
+ { DMSETUP_STATS_CMD, DMSETUP_STATS_CMD_NAME, STATS_TYPE },
+ { DEVMAP_NAME_CMD, DEVMAP_NAME_CMD_NAME, DEVMAP_NAME_TYPE },
+};
+
+static const int _num_base_commands = DM_ARRAY_SIZE(_base_commands);
+
+/*
+ * We have only very simple switches ATM.
+ */
+enum {
+ READ_ONLY = 0,
+ ADD_NODE_ON_CREATE_ARG,
+ ADD_NODE_ON_RESUME_ARG,
+ ALIAS_ARG,
+ ALL_DEVICES_ARG,
+ ALL_PROGRAMS_ARG,
+ ALL_REGIONS_ARG,
+ AREA_ARG,
+ AREAS_ARG,
+ AREA_SIZE_ARG,
+ CONCISE_ARG,
+ BOUNDS_ARG,
+ CHECKS_ARG,
+ CLEAR_ARG,
+ COLS_ARG,
+ COUNT_ARG,
+ DEFERRED_ARG,
+ SELECT_ARG,
+ EXEC_ARG,
+ FILEMAP_ARG,
+ FOLLOW_ARG,
+ FORCE_ARG,
+ FOREGROUND_ARG,
+ GID_ARG,
+ GROUP_ARG,
+ GROUP_ID_ARG,
+ HELP_ARG,
+ HISTOGRAM_ARG,
+ INACTIVE_ARG,
+ INTERVAL_ARG,
+ LENGTH_ARG,
+ MANGLENAME_ARG,
+ MAJOR_ARG,
+ REGIONS_ARG,
+ MINOR_ARG,
+ MODE_ARG,
+ NAMEPREFIXES_ARG,
+ NOFLUSH_ARG,
+ NOGROUP_ARG,
+ NOHEADINGS_ARG,
+ NOLOCKFS_ARG,
+ NOOPENCOUNT_ARG,
+ NOSUFFIX_ARG,
+ NOTABLE_ARG,
+ NOTIMESUFFIX_ARG,
+ UDEVCOOKIE_ARG,
+ NOMONITOR_ARG,
+ NOUDEVRULES_ARG,
+ NOUDEVSYNC_ARG,
+ OPTIONS_ARG,
+ PRECISE_ARG,
+ PROGRAM_ID_ARG,
+ RAW_ARG,
+ READAHEAD_ARG,
+ REGION_ARG,
+ REGION_ID_ARG,
+ RELATIVE_ARG,
+ RETRY_ARG,
+ ROWS_ARG,
+ SEPARATOR_ARG,
+ SETUUID_ARG,
+ SHOWKEYS_ARG,
+ SORT_ARG,
+ START_ARG,
+ TABLE_ARG,
+ TARGET_ARG,
+ SEGMENTS_ARG,
+ TREE_ARG,
+ UID_ARG,
+ UNBUFFERED_ARG,
+ UNITS_ARG,
+ UNQUOTED_ARG,
+ USER_DATA_ARG,
+ UUID_ARG,
+ VERBOSE_ARG,
+ VERIFYUDEV_ARG,
+ VERSION_ARG,
+ YES_ARG,
+ NUM_SWITCHES
+};
+
+typedef enum {
+ DR_TASK = 1,
+ DR_INFO = 2,
+ DR_DEPS = 4,
+ DR_TREE = 8, /* Complete dependency tree required */
+ DR_NAME = 16,
+ DR_STATS = 32, /* Requires populated stats handle. */
+ DR_STATS_META = 64, /* Requires listed stats handle. */
+} report_type_t;
+
+typedef enum {
+ DN_DEVNO, /* Major and minor number pair */
+ DN_BLK, /* Block device name (e.g. dm-0) */
+ DN_MAP /* Map name (for dm devices only, equal to DN_BLK otherwise) */
+} dev_name_t;
+
+static cmd_name_t _base_command = DMSETUP_CMD; /* Default command is 'dmsetup' */
+static cmd_type_t _base_command_type = DMSETUP_TYPE;
+static int _switches[NUM_SWITCHES];
+static int _int_args[NUM_SWITCHES];
+static char *_string_args[NUM_SWITCHES];
+static int _num_devices;
+static char *_uuid;
+static char *_table;
+static char *_target;
+static char *_command_to_exec; /* --exec <command> */
+static const char *_command; /* dmsetup <command> */
+static uint32_t _read_ahead_flags;
+static uint32_t _udev_cookie;
+static int _udev_only;
+static struct dm_tree *_dtree;
+static struct dm_report *_report;
+static report_type_t _report_type;
+static dev_name_t _dev_name_type;
+static uint64_t _count = 1; /* count of repeating reports */
+static struct dm_timestamp *_initial_timestamp = NULL;
+static uint64_t _disp_factor = 512; /* display sizes in sectors */
+static char _disp_units = 's';
+const char *_program_id = DM_STATS_PROGRAM_ID; /* program_id used for reports. */
+static uint64_t _statstype = 0; /* stats objects to report */
+static int _concise_output_produced = 0; /* Was any concise output already printed? */
+struct command;
+static const struct command *_selection_cmd = NULL; /* Command to run against each device select with -S */
+
+/* string names for stats object types */
+const char *_stats_types[] = {
+ "all",
+ "area",
+ "region",
+ "group",
+ NULL
+};
+
+/* report timekeeping */
+static struct dm_timestamp *_cycle_timestamp = NULL;
+#ifndef HAVE_SYS_TIMERFD_H
+static struct dm_timestamp *_start_timestamp = NULL;
+#else /* HAVE_SYS_TIMERFD_H */
+static int _timer_fd = -1; /* timerfd file descriptor. */
+#endif /* !HAVE_SYS_TIMERFD_H */
+
+static uint64_t _interval = 0; /* configured interval in nsecs */
+static uint64_t _new_interval = 0; /* flag top-of-interval */
+static uint64_t _last_interval = 0; /* approx. measured interval in nsecs */
+
+/* Invalid fd value used to signal end-of-reporting. */
+#define TIMER_STOPPED (-2)
+
+#define NSEC_PER_USEC UINT64_C(1000)
+#define NSEC_PER_MSEC UINT64_C(1000000)
+#define NSEC_PER_SEC UINT64_C(1000000000)
+
+/*
+ * Commands
+ */
+
+#define CMD_ARGS const struct command *cmd, const char *subcommand, int argc, char **argv, struct dm_names *names, int multiple_devices
+typedef int (*command_fn) (CMD_ARGS);
+
+struct command {
+ const char *name;
+ const char *help;
+ int min_args;
+ int max_args;
+ int repeatable_cmd; /* Repeat to process device list? */
+ /* 2 means --select is also supported */
+ int has_subcommands; /* Command implements sub-commands. */
+ command_fn fn;
+};
+
+static int _parse_line(struct dm_task *dmt, char *buffer, const char *file,
+ int line)
+{
+ char ttype[LINE_SIZE], *ptr, *comment;
+ unsigned long long start, size;
+ int n;
+
+ /* trim trailing space */
+ for (ptr = buffer + strlen(buffer) - 1; ptr >= buffer; ptr--)
+ if (!isspace((int) *ptr))
+ break;
+ ptr++;
+ *ptr = '\0';
+
+ /* trim leading space */
+ for (ptr = buffer; *ptr && isspace((int) *ptr); ptr++)
+ ;
+
+ if (!*ptr || *ptr == '#')
+ return 1;
+
+ if (sscanf(ptr, "%llu %llu %s %n",
+ &start, &size, ttype, &n) < 3) {
+ log_error("Invalid format on line %d of table %s.", line, file);
+ return 0;
+ }
+
+ ptr += n;
+ if ((comment = strchr(ptr, (int) '#')))
+ *comment = '\0';
+
+ if (!dm_task_add_target(dmt, start, size, ttype, ptr))
+ return_0;
+
+ return 1;
+}
+
+/* Parse multiple lines of table */
+static int _parse_table_lines(struct dm_task *dmt)
+{
+ char *pos = _table, *next_pos;
+ int line = 0;
+
+ do {
+ /* Identify and terminate each line */
+ if ((next_pos = strchr(_table, '\n')))
+ *next_pos++ = '\0';
+ if (!_parse_line(dmt, pos, "", ++line))
+ return_0;
+ } while ((pos = next_pos));
+
+ return 1;
+}
+
+static int _parse_file(struct dm_task *dmt, const char *file)
+{
+ char *buffer = NULL;
+ size_t buffer_size = 0;
+ FILE *fp;
+ int r = 0, line = 0;
+
+ /* Table on cmdline or from stdin with --concise */
+ if (_table)
+ return _parse_table_lines(dmt);
+
+ /* OK for empty stdin */
+ if (file) {
+ if (!(fp = fopen(file, "r"))) {
+ log_error("Couldn't open '%s' for reading.", file);
+ return 0;
+ }
+ } else
+ fp = stdin;
+
+#ifndef HAVE_GETLINE
+ buffer_size = LINE_SIZE;
+ if (!(buffer = malloc(buffer_size))) {
+ log_error("Failed to malloc line buffer.");
+ return 0;
+ }
+
+ while (fgets(buffer, (int) buffer_size, fp))
+#else
+ while (getline(&buffer, &buffer_size, fp) > 0)
+#endif
+ if (!_parse_line(dmt, buffer, file ? : "on stdin", ++line))
+ goto_out;
+
+ r = 1;
+
+out:
+ memset(buffer, 0, buffer_size);
+#ifndef HAVE_GETLINE
+ free(buffer);
+#else
+ free(buffer);
+#endif
+ if (file && fclose(fp))
+ log_sys_debug("fclose", file);
+
+ return r;
+}
+
+struct dm_split_name {
+ char *subsystem;
+ char *vg_name;
+ char *lv_name;
+ char *lv_layer;
+};
+
+struct dmsetup_report_obj {
+ struct dm_task *task;
+ struct dm_info *info;
+ struct dm_task *deps_task;
+ struct dm_tree_node *tree_node;
+ struct dm_split_name *split_name;
+ struct dm_stats *stats;
+};
+
+static int _task_run(struct dm_task *dmt)
+{
+ int r;
+ uint64_t delta;
+ struct dm_timestamp *ts;
+
+ if (_initial_timestamp)
+ dm_task_set_record_timestamp(dmt);
+
+ r = dm_task_run(dmt);
+
+ if (_initial_timestamp &&
+ (ts = dm_task_get_ioctl_timestamp(dmt))) {
+ delta = dm_timestamp_delta(ts, _initial_timestamp);
+ log_debug("Timestamp: %7" PRIu64 ".%09" PRIu64 " seconds",
+ delta / NSEC_PER_SEC, delta % NSEC_PER_SEC);
+ }
+
+ return r;
+}
+
+static struct dm_task *_get_deps_task(int major, int minor)
+{
+ struct dm_task *dmt;
+ struct dm_info info;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
+ return_NULL;
+
+ if (!dm_task_set_major(dmt, major) ||
+ !dm_task_set_minor(dmt, minor))
+ goto_bad;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto_bad;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto_bad;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_bad;
+
+ if (!_task_run(dmt))
+ goto_bad;
+
+ if (!dm_task_get_info(dmt, &info))
+ goto_bad;
+
+ if (!info.exists)
+ goto_bad;
+
+ return dmt;
+
+bad:
+ dm_task_destroy(dmt);
+ return NULL;
+}
+
+static char *_extract_uuid_prefix(const char *uuid, const int separator)
+{
+ char *ptr = NULL;
+ char *uuid_prefix = NULL;
+ size_t len;
+
+ if (uuid)
+ ptr = strchr(uuid, separator);
+
+ len = ptr ? ptr - uuid : 0;
+ if (!(uuid_prefix = malloc(len + 1))) {
+ log_error("Failed to allocate memory to extract uuid prefix.");
+ return NULL;
+ }
+
+ if (uuid)
+ memcpy(uuid_prefix, uuid, len);
+
+ uuid_prefix[len] = '\0';
+
+ return uuid_prefix;
+}
+
+static struct dm_split_name *_get_split_name(const char *uuid, const char *name,
+ int separator)
+{
+ struct dm_split_name *split_name;
+
+ if (!(split_name = malloc(sizeof(*split_name)))) {
+ log_error("Failed to allocate memory to split device name "
+ "into components.");
+ return NULL;
+ }
+
+ if (!(split_name->subsystem = _extract_uuid_prefix(uuid, separator))) {
+ free(split_name);
+ return_NULL;
+ }
+
+ split_name->vg_name = split_name->lv_name =
+ split_name->lv_layer = (char *) "";
+
+ if (!strcmp(split_name->subsystem, "LVM") &&
+ (!(split_name->vg_name = strdup(name)) ||
+ !dm_split_lvm_name(NULL, NULL, &split_name->vg_name,
+ &split_name->lv_name, &split_name->lv_layer)))
+ log_error("Failed to allocate memory to split LVM name "
+ "into components.");
+
+ return split_name;
+}
+
+static void _destroy_split_name(struct dm_split_name *split_name)
+{
+ /*
+ * lv_name and lv_layer are allocated within the same block
+ * of memory as vg_name so don't need to be freed separately.
+ */
+ if (!strcmp(split_name->subsystem, "LVM"))
+ free(split_name->vg_name);
+
+ free(split_name->subsystem);
+ free(split_name);
+}
+
+/*
+ * Stats clock:
+ *
+ * Use either Linux timerfds or usleep to implement the reporting
+ * interval wait.
+ *
+ * _start_timer() - Start the timer running.
+ * _do_timer_wait() - Wait until the beginning of the next interval.
+ *
+ * _update_interval_times() - Update timestamps and interval estimate.
+ */
+
+/*
+ * Return the current interval number counting upwards from one.
+ */
+static uint64_t _interval_num(void)
+{
+ uint64_t count_arg = _int_args[COUNT_ARG];
+ return ((uint64_t) _int_args[COUNT_ARG] - _count) + !!count_arg;
+}
+
+#ifdef HAVE_SYS_TIMERFD_H
+static int _start_timerfd_timer(void)
+{
+ struct itimerspec interval_timer;
+ time_t secs;
+ long nsecs;
+
+ log_debug("Using timerfd for interval timekeeping.");
+
+ /* timer running? */
+ if (_timer_fd != -1)
+ return 1;
+
+ memset(&interval_timer, 0, sizeof(interval_timer));
+
+ /* Use CLOCK_MONOTONIC to avoid warp on RTC adjustments. */
+ if ((_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)) < 0) {
+ log_error("Could not create timer: %s", strerror(errno));
+ return 0;
+ }
+
+ secs = (time_t) _interval / NSEC_PER_SEC;
+ nsecs = (long) _interval % NSEC_PER_SEC;
+
+ /* Must set interval and value to create an armed periodic timer. */
+ interval_timer.it_interval.tv_sec = secs;
+ interval_timer.it_interval.tv_nsec = nsecs;
+ interval_timer.it_value.tv_sec = secs;
+ interval_timer.it_value.tv_nsec = nsecs;
+
+ log_debug("Setting interval timer to: " FMTu64 "s %ldns", (uint64_t)secs, nsecs);
+ if (timerfd_settime(_timer_fd, 0, &interval_timer, NULL)) {
+ log_error("Could not set interval timer: %s", strerror(errno));
+ return 0;
+ }
+ return 1;
+}
+
+static int _do_timerfd_wait(void)
+{
+ uint64_t expired;
+ ssize_t bytes;
+
+ if (_timer_fd < 0)
+ return_0;
+
+ /* read on timerfd returns a uint64_t in host byte order. */
+ bytes = read(_timer_fd, &expired, sizeof(expired));
+
+ if (bytes < 0) {
+ /* EBADF from invalid timerfd or EINVAL from too small buffer. */
+ log_error("Interval timer wait failed: %s.", strerror(errno));
+ return 0;
+ }
+
+ /* read(2) on a timerfd descriptor is guaranteed to return 8 bytes. */
+ if (bytes != 8)
+ log_error("Unexpected byte count on timerfd read: " FMTssize_t ".", bytes);
+
+ /* FIXME: attempt to rebase clock? */
+ if (expired > 1)
+ log_warn("WARNING: Try increasing --interval ("FMTu64
+ " missed timer events).", expired - 1);
+
+ /* Signal that a new interval has begun. */
+ _new_interval = 1;
+
+ /* Final interval? */
+ if (_count == 2) {
+ if (close(_timer_fd))
+ stack;
+ /* Tell _update_interval_times() to shut down. */
+ _timer_fd = TIMER_STOPPED;
+ }
+
+ return 1;
+}
+
+static int _start_timer(void)
+{
+ return _start_timerfd_timer();
+}
+
+static int _do_timer_wait(void)
+{
+ return _do_timerfd_wait();
+}
+
+static int _timer_running(void)
+{
+ return ((_timer_fd != TIMER_STOPPED) || _cycle_timestamp);
+}
+
+#else /* !HAVE_SYS_TIMERFD_H */
+static int _start_usleep_timer(void)
+{
+ log_debug("Using usleep for interval timekeeping.");
+ _start_timestamp = dm_timestamp_alloc();
+ dm_timestamp_get(_start_timestamp);
+ return 1;
+}
+
+static int _do_usleep_wait(void)
+{
+ static struct dm_timestamp *_now = NULL;
+ uint64_t this_interval;
+ int64_t delta_t;
+
+ /*
+ * Report clock: compensate for time spent in userspace and stats
+ * message ioctls by keeping track of the last wake time and
+ * adjusting the sleep interval accordingly.
+ */
+ if (!_now) {
+ if (!(_now = dm_timestamp_alloc()))
+ return_0;
+ dm_timestamp_get(_now);
+ this_interval = _interval;
+ log_error("Using "FMTu64" as first interval.", this_interval);
+ } else {
+ dm_timestamp_get(_now);
+ delta_t = dm_timestamp_delta(_now, _start_timestamp);
+ log_debug("Interval timer drift: "FMTd64".",
+ (delta_t % _interval));
+
+ /* FIXME: usleep timer drift over large counts. */
+
+ /* adjust for time spent populating and reporting */
+ this_interval = _interval - (delta_t % _interval);
+ log_debug("Using "FMTu64" as interval.", this_interval);
+ }
+
+ /* Signal that a new interval has begun. */
+ _new_interval = 1;
+
+ if (usleep(this_interval / NSEC_PER_USEC)) {
+ if (errno == EINTR)
+ log_error("Report interval interrupted by signal.");
+ else if (errno == EINVAL)
+ log_error("Report interval too short.");
+ else
+ stack; /* other reason */
+ return 0;
+ }
+
+ if (_count == 2) {
+ dm_timestamp_destroy(_start_timestamp);
+ dm_timestamp_destroy(_now);
+ _start_timestamp = _now = NULL;
+ }
+
+ return 1;
+}
+
+static int _start_timer(void)
+{
+ return _start_usleep_timer();
+}
+
+static int _do_timer_wait(void)
+{
+ return _do_usleep_wait();
+}
+
+static int _timer_running(void)
+{
+ return (_start_timestamp != NULL);
+}
+
+#endif /* HAVE_SYS_TIMERFD_H */
+
+static int _update_interval_times(void)
+{
+ static struct dm_timestamp *this_timestamp = NULL;
+ uint64_t delta_t, interval_num = _interval_num();
+ int r = 1;
+
+ /*
+ * Clock shutdown for exit - nothing to do.
+ */
+ if (!_timer_running())
+ goto out;
+
+ /* clock is running */
+ r = 0;
+
+ /*
+ * Current timestamp. If _new_interval is set this is used as
+ * the new cycle start timestamp.
+ */
+ if (!this_timestamp) {
+ if (!(this_timestamp = dm_timestamp_alloc()))
+ return_0;
+ }
+
+ /*
+ * Take cycle timstamp as close as possible to ioctl return.
+ *
+ * FIXME: use per-region timestamp deltas for interval estimate.
+ */
+ if (!dm_timestamp_get(this_timestamp))
+ goto_out;
+
+ /*
+ * Stats clock: maintain a single timestamp taken just after the
+ * call to dm_stats_populate() and take a delta between the current
+ * and last value to determine the sampling interval.
+ *
+ * A new interval is started when the _new_interval flag is set
+ * on return from _do_report_wait().
+ *
+ * The first interval is treated as a special case: since the
+ * time since the last clear of the counters is unknown (no
+ * previous timestamp exists) the duration is assumed to be the
+ * configured value.
+ */
+ if (_cycle_timestamp)
+ /* Current delta_t: time from start of cycle to now. */
+ delta_t = dm_timestamp_delta(this_timestamp, _cycle_timestamp);
+ else {
+ _cycle_timestamp = dm_timestamp_alloc();
+ if (!_cycle_timestamp) {
+ log_error("Could not allocate timestamp object.");
+ goto out;
+ }
+
+ /* Pretend we have the configured interval. */
+ delta_t = _interval;
+
+ /* start the first cycle */
+ log_debug("Beginning first interval.");
+ _new_interval = 1;
+ }
+
+ log_debug("Interval #%-4"PRIu64" time delta: %12"PRIu64"ns.",
+ interval_num, delta_t);
+
+ if (_new_interval) {
+ /* Update timestamp and interval and clear _new_interval */
+ dm_timestamp_copy(_cycle_timestamp, this_timestamp);
+ _last_interval = delta_t;
+ _new_interval = 0;
+
+ /*
+ * Log interval duration and current error.
+ */
+ log_debug("Interval #%-5"PRIu64" current err: %12"PRIi64"ns.",
+ interval_num, ((int64_t)_last_interval - (int64_t)_interval));
+ log_debug("End interval #%-9"PRIu64" duration: %12"PRIu64"ns.",
+ interval_num, _last_interval);
+ }
+
+ r = 1;
+
+out:
+ /* timer stopped or never started */
+ if (!r || !_timer_running()) {
+ /* The _cycle_timestamp has not yet been allocated if we
+ * fail to obtain this_timestamp on the first interval.
+ */
+ if (_cycle_timestamp)
+ dm_timestamp_destroy(_cycle_timestamp);
+ dm_timestamp_destroy(this_timestamp);
+
+ /* Clear timestamp pointers to signal shutdown. */
+ _cycle_timestamp = this_timestamp = NULL;
+ }
+ return r;
+}
+
+static int _display_info_cols(struct dm_task *dmt, struct dm_info *info)
+{
+ struct dmsetup_report_obj obj;
+ uint64_t walk_flags = _statstype;
+ int r = 0;
+ int selected;
+ char *device_name;
+
+ obj.task = dmt;
+ obj.info = info;
+ obj.deps_task = NULL;
+ obj.split_name = NULL;
+ obj.stats = NULL;
+
+ if (_report_type & DR_TREE)
+ if (!(obj.tree_node = dm_tree_find_node(_dtree, info->major, info->minor))) {
+ log_error("Cannot find node %d:%d.", info->major, info->minor);
+ goto out;
+ }
+
+ if (_report_type & DR_DEPS)
+ if (!(obj.deps_task = _get_deps_task(info->major, info->minor))) {
+ log_error("Cannot get deps for %d:%d.", info->major, info->minor);
+ goto out;
+ }
+
+ if (_report_type & DR_NAME)
+ if (!(obj.split_name = _get_split_name(dm_task_get_uuid(dmt),
+ dm_task_get_name(dmt), '-')))
+ goto_out;
+
+ if (!(_report_type & (DR_STATS | DR_STATS_META))) {
+ /*
+ * If _selection_cmd is set we are applying -S to some other command, so suppress
+ * output and run that other command if the device matches the criteria.
+ */
+ if (!dm_report_object_is_selected(_report, &obj, _selection_cmd ? 0 : 1, &selected))
+ goto_out;
+ if (_selection_cmd && selected) {
+ device_name = (char*) dm_task_get_name(dmt);
+ if (!_selection_cmd->fn(_selection_cmd, NULL, 1, &device_name, NULL, 1))
+ goto_out;
+ }
+ r = 1;
+ goto out;
+ }
+
+ /*
+ * Obtain statistics for the current reporting object and set
+ * the interval estimate used for stats rate conversion.
+ */
+ if (_report_type & DR_STATS) {
+ if (!(obj.stats = dm_stats_create(DM_STATS_PROGRAM_ID)))
+ goto_out;
+
+ if (!dm_stats_get_nr_regions(obj.stats)) {
+ log_debug("Skipping %s with no regions.", dm_task_get_name(dmt));
+ r = 1;
+ goto out;
+ }
+
+ dm_stats_bind_devno(obj.stats, info->major, info->minor);
+
+ if (!dm_stats_populate(obj.stats, _program_id, DM_STATS_REGIONS_ALL))
+ goto_out;
+
+ /* Update timestamps and handle end-of-interval accounting. */
+ _update_interval_times();
+
+ log_debug("Adjusted sample interval duration: %12"PRIu64"ns.", _last_interval);
+ /* use measured approximation for calculations */
+ dm_stats_set_sampling_interval_ns(obj.stats, _last_interval);
+ } else if (!obj.stats && (_report_type & DR_STATS_META)
+ /* Only a dm_stats_list is needed for DR_STATS_META reports. */
+ && !(_report_type & DR_STATS)) {
+ if (!(obj.stats = dm_stats_create(DM_STATS_PROGRAM_ID)))
+ goto_out;
+
+ dm_stats_bind_devno(obj.stats, info->major, info->minor);
+
+ if (!dm_stats_list(obj.stats, _program_id))
+ goto_out;
+
+ /* No regions to report is not an error */
+ if (!dm_stats_get_nr_regions(obj.stats))
+ goto out;
+ }
+
+ /* group report with no groups? */
+ if ((walk_flags == DM_STATS_WALK_GROUP)
+ && !dm_stats_get_nr_groups(obj.stats))
+ goto out;
+
+ dm_stats_walk_init(obj.stats, walk_flags);
+ dm_stats_walk_do(obj.stats) {
+ if (!dm_report_object(_report, &obj))
+ goto_out;
+ dm_stats_walk_next(obj.stats);
+ } dm_stats_walk_while(obj.stats);
+
+ r = 1;
+
+out:
+ if (obj.deps_task)
+ dm_task_destroy(obj.deps_task);
+ if (obj.split_name)
+ _destroy_split_name(obj.split_name);
+ if (obj.stats)
+ dm_stats_destroy(obj.stats);
+ return r;
+}
+
+static void _display_info_long(struct dm_task *dmt, struct dm_info *info)
+{
+ const char *uuid;
+ uint32_t read_ahead;
+
+ printf("Name: %s\n", dm_task_get_name(dmt));
+
+ printf("State: %s%s%s\n",
+ info->suspended ? "SUSPENDED" : "ACTIVE",
+ info->read_only ? " (READ-ONLY)" : "",
+ info->deferred_remove ? " (DEFERRED REMOVE)" : "");
+
+ /* FIXME Old value is being printed when it's being changed. */
+ if (dm_task_get_read_ahead(dmt, &read_ahead))
+ printf("Read Ahead: %" PRIu32 "\n", read_ahead);
+
+ if (!info->live_table && !info->inactive_table)
+ printf("Tables present: None\n");
+ else
+ printf("Tables present: %s%s%s\n",
+ info->live_table ? "LIVE" : "",
+ info->live_table && info->inactive_table ? " & " : "",
+ info->inactive_table ? "INACTIVE" : "");
+
+ if (info->open_count != -1)
+ printf("Open count: %d\n", info->open_count);
+
+ printf("Event number: %" PRIu32 "\n", info->event_nr);
+ printf("Major, minor: %d, %d\n", info->major, info->minor);
+
+ if (info->target_count != -1)
+ printf("Number of targets: %d\n", info->target_count);
+
+ if ((uuid = dm_task_get_uuid(dmt)) && *uuid)
+ printf("UUID: %s\n", uuid);
+
+ putchar('\n');
+}
+
+static int _display_info(struct dm_task *dmt)
+{
+ struct dm_info info;
+ int r = 1;
+
+ if (!dm_task_get_info(dmt, &info))
+ return_0;
+
+ if (!info.exists) {
+ log_error("Device does not exist.");
+ return 0;
+ }
+
+ if (!_switches[COLS_ARG])
+ _display_info_long(dmt, &info);
+ else
+ r = _display_info_cols(dmt, &info);
+
+ return r;
+}
+
+static int _set_task_device(struct dm_task *dmt, const char *name, int optional)
+{
+ if (name) {
+ if (!dm_task_set_name(dmt, name))
+ return_0;
+ } else if (_switches[UUID_ARG]) {
+ if (!dm_task_set_uuid(dmt, _uuid))
+ return_0;
+ } else if (_switches[MAJOR_ARG] && _switches[MINOR_ARG]) {
+ if (!dm_task_set_major(dmt, _int_args[MAJOR_ARG]) ||
+ !dm_task_set_minor(dmt, _int_args[MINOR_ARG]))
+ return_0;
+ } else if (!optional) {
+ log_error("No device specified.");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _set_task_add_node(struct dm_task *dmt)
+{
+ if (!dm_task_set_add_node(dmt, DEFAULT_DM_ADD_NODE))
+ return_0;
+
+ if (_switches[ADD_NODE_ON_RESUME_ARG] &&
+ !dm_task_set_add_node(dmt, DM_ADD_NODE_ON_RESUME))
+ return_0;
+
+ if (_switches[ADD_NODE_ON_CREATE_ARG] &&
+ !dm_task_set_add_node(dmt, DM_ADD_NODE_ON_CREATE))
+ return_0;
+
+ return 1;
+}
+
+static int _load(CMD_ARGS)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ const char *file = NULL;
+ const char *name = NULL;
+
+ if (_switches[NOTABLE_ARG]) {
+ log_error("--notable only available when creating new device.");
+ return 0;
+ }
+
+ if (!_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
+ if (!argc) {
+ log_error("Please specify device.");
+ return 0;
+ }
+ name = argv[0];
+ argc--;
+ argv++;
+ } else if (argc > 1) {
+ log_error("Too many command line arguments.");
+ return 0;
+ }
+
+ if (argc == 1)
+ file = argv[0];
+
+ if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
+ return_0;
+
+ if (!_set_task_device(dmt, name, 0))
+ goto_out;
+
+ if (!_switches[NOTABLE_ARG] && !_parse_file(dmt, file))
+ goto_out;
+
+ if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
+ goto_out;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto_out;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto_out;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ if (!_task_run(dmt))
+ goto_out;
+
+ r = 1;
+
+ if (_switches[VERBOSE_ARG])
+ r = _display_info(dmt);
+
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _create_one_device(const char *name, const char *file)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ uint32_t cookie = 0;
+ uint16_t udev_flags = 0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_CREATE)))
+ return_0;
+
+ if (!dm_task_set_name(dmt, name))
+ goto_out;
+
+ if (_switches[UUID_ARG] && !dm_task_set_uuid(dmt, _uuid))
+ goto_out;
+
+ if (!_switches[NOTABLE_ARG] && !_parse_file(dmt, file))
+ goto_out;
+
+ if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
+ goto_out;
+
+ if (_switches[MAJOR_ARG] && !dm_task_set_major(dmt, _int_args[MAJOR_ARG]))
+ goto_out;
+
+ if (_switches[MINOR_ARG] && !dm_task_set_minor(dmt, _int_args[MINOR_ARG]))
+ goto_out;
+
+ if (_switches[UID_ARG] && !dm_task_set_uid(dmt, _int_args[UID_ARG]))
+ goto_out;
+
+ if (_switches[GID_ARG] && !dm_task_set_gid(dmt, _int_args[GID_ARG]))
+ goto_out;
+
+ if (_switches[MODE_ARG] && !dm_task_set_mode(dmt, _int_args[MODE_ARG]))
+ goto_out;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto_out;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto_out;
+
+ if (_switches[READAHEAD_ARG] &&
+ !dm_task_set_read_ahead(dmt, _int_args[READAHEAD_ARG],
+ _read_ahead_flags))
+ goto_out;
+
+ if (_switches[NOTABLE_ARG])
+ dm_udev_set_sync_support(0);
+
+ if (_switches[NOUDEVRULES_ARG])
+ udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
+ DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ if (!_set_task_add_node(dmt))
+ goto_out;
+
+ if (_udev_cookie)
+ cookie = _udev_cookie;
+
+ if (_udev_only)
+ udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
+
+ if (!dm_task_set_cookie(dmt, &cookie, udev_flags) ||
+ !_task_run(dmt))
+ goto_out;
+
+ r = 1;
+
+out:
+ if (!_udev_cookie)
+ (void) dm_udev_wait(cookie);
+
+ if (r && _switches[VERBOSE_ARG])
+ r = _display_info(dmt);
+
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+#define DEFAULT_BUF_SIZE 4096
+
+static char *_slurp_stdin(void)
+{
+ char *buf, *pos;
+ size_t bufsize = DEFAULT_BUF_SIZE;
+ size_t total = 0;
+ ssize_t n = 0;
+
+ if (!(buf = malloc(bufsize))) {
+ log_error("Buffer memory allocation failed.");
+ return NULL;
+ }
+
+ pos = buf;
+ do {
+ do
+ n = read(STDIN_FILENO, pos, (size_t) bufsize - total - 1);
+ while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
+
+ if (n < 0) {
+ log_error("Read from stdin aborted: %s", strerror(errno));
+ free(buf);
+ return NULL;
+ }
+
+ if (!n)
+ break;
+
+ total += n;
+ pos += n;
+ if (total == bufsize - 1) {
+ bufsize *= 2;
+ if (!(buf = realloc(buf, bufsize))) {
+ log_error("Buffer memory extension to %" PRIsize_t " bytes failed.", bufsize);
+ return NULL;
+ }
+ }
+ } while (1);
+
+ buf[total] = '\0';
+
+ return buf;
+}
+
+static int _create_concise(const struct command *cmd, int argc, char **argv)
+{
+ char *concise_format;
+ char *c, *n;
+ char *fields[5] = { NULL }; /* name,uuid,minor,flags,table */
+ int f = 0;
+
+ if (_switches[TABLE_ARG] || _switches[MINOR_ARG] || _switches[UUID_ARG] ||
+ _switches[NOTABLE_ARG] || _switches[INACTIVE_ARG]){
+ log_error("--concise is incompatible with --[no]table, --minor, --uuid and --inactive.");
+ return 0;
+ }
+
+ if (argc)
+ concise_format = argv[0];
+ else if (!(concise_format = _slurp_stdin()))
+ return_0;
+
+ /* Work through input string c, parsing into sets of 5 fields. */
+ /* Strip out any characters quoted by backslashes in-place. */
+ /* Read characters from c and prepare them in situ for final processing at n */
+ c = n = fields[f] = concise_format;
+
+ while (*c) {
+ /* Quoted character? Skip past quote. */
+ if (*c == '\\') {
+ if (!*(++c)) {
+ log_error("Backslash must be followed by another character at end of string.");
+ *n = '\0';
+ log_error("Parsed %d fields: name: %s uuid: %s minor: %s flags: %s table: %s",
+ f + 1, fields[0], fields[1], fields[2], fields[3], fields[4]);
+ goto out;
+ }
+
+ /* Don't interpret next character */
+ *n++ = *c++;
+
+ continue;
+ }
+
+ /* Comma marking end of field? */
+ if (*c == ',' && f < 4) {
+ /* Terminate string */
+ *n++ = '\0', c++;
+
+ /* Store start of next field */
+ fields[++f] = n;
+
+ /* Skip any whitespace after field-separating commas */
+ while(isspace(*c))
+ c++;
+
+ continue;
+ }
+
+ /* Comma marking end of a table line? */
+ if (*c == ',' && f >= 4) {
+ /* Replace comma with newline to match standard table input format */
+ *n++ = '\n', c++;
+
+ continue;
+ }
+
+ /* Semi-colon marking end of device? */
+ if (*c == ';' || *(c + 1) == '\0') {
+ /* End of input? */
+ if (*c != ';')
+ /* Copy final character */
+ *n++ = *c;
+
+ /* Terminate string */
+ *n++ = '\0', c++;
+
+ if (f != 4) {
+ log_error("Five comma-separated fields are required for each device");
+ log_error("Parsed %d fields: name: %s uuid: %s minor: %s flags: %s table: %s",
+ f + 1, fields[0], fields[1], fields[2], fields[3], fields[4]);
+ goto out;
+ }
+
+ /* Set up parameters the same way as when specified directly on command line */
+ if (*fields[1]) {
+ _switches[UUID_ARG] = 1;
+ _uuid = fields[1];
+ }
+
+ if (*fields[2]) {
+ _switches[MINOR_ARG] = 1;
+ _int_args[MINOR_ARG] = atoi(fields[2]);
+ }
+
+ if (!strcmp(fields[3], "ro"))
+ _switches[READ_ONLY] = 1;
+ else if (*fields[3] && strcmp(fields[3], "rw")) {
+ log_error("Invalid flags parameter '%s' must be 'ro' or 'rw' or empty.", fields[3]);
+ _uuid = NULL;
+ goto out;
+ }
+
+ _table = fields[4];
+
+ /* Create the device */
+ if (!_create_one_device(fields[0], NULL)) {
+ _uuid = _table = NULL;
+ goto out;
+ }
+
+ /* Clear parameters ready for any further devices */
+ _switches[UUID_ARG] = 0;
+ _switches[MINOR_ARG] = 0;
+ _switches[READ_ONLY] = 0;
+ _uuid = _table = NULL;
+
+ f = 0;
+ fields[0] = n;
+ fields[1] = fields[2] = fields[3] = fields[4] = NULL;
+
+ /* Skip any whitespace after semi-colons */
+ while(isspace(*c))
+ c++;
+
+ continue;
+ }
+
+ /* Normal character */
+ *n++ = *c++;
+ }
+
+ if (fields[0] != n) {
+ *n = '\0';
+ log_error("Incomplete entry: five comma-separated fields are required for each device");
+ log_error("Parsed %d fields: name: %s uuid: %s minor: %s flags: %s table: %s",
+ f + 1, fields[0], fields[1], fields[2], fields[3], fields[4]);
+ goto out;
+ }
+
+ return 1;
+
+out:
+ if (!argc)
+ free(concise_format);
+
+ return 0;
+}
+
+static int _create(CMD_ARGS)
+{
+ const char *name;
+ const char *file = NULL;
+
+ if (_switches[CONCISE_ARG]) {
+ if (argc > 1) {
+ log_error("dmsetup create --concise takes at most one argument");
+ return 0;
+ }
+ return _create_concise(cmd, argc, argv);
+ }
+
+ if (!argc) {
+ log_error("Please provide a name for the new device.");
+ return 0;
+ }
+
+ name = argv[0];
+ if (argc == 2)
+ file = argv[1];
+
+ return _create_one_device(name, file);
+}
+
+static int _do_rename(const char *name, const char *new_name, const char *new_uuid) {
+ int r = 0;
+ struct dm_task *dmt;
+ uint32_t cookie = 0;
+ uint16_t udev_flags = 0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_RENAME)))
+ return_0;
+
+ /* FIXME Kernel doesn't support uuid or device number here yet */
+ if (!_set_task_device(dmt, name, 0))
+ goto_out;
+
+ if (new_uuid) {
+ if (!dm_task_set_newuuid(dmt, new_uuid))
+ goto_out;
+ } else if (!new_name || !dm_task_set_newname(dmt, new_name))
+ goto_out;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto_out;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto_out;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ if (_switches[NOUDEVRULES_ARG])
+ udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
+ DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
+
+ if (_udev_cookie)
+ cookie = _udev_cookie;
+
+ if (_udev_only)
+ udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
+
+ if (!dm_task_set_cookie(dmt, &cookie, udev_flags) ||
+ !_task_run(dmt))
+ goto_out;
+
+ r = 1;
+
+out:
+ if (!_udev_cookie)
+ (void) dm_udev_wait(cookie);
+
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _rename(CMD_ARGS)
+{
+ const char *name = (argc == 2) ? argv[0] : NULL;
+
+ return _switches[SETUUID_ARG] ? _do_rename(name, NULL, argv[argc - 1]) :
+ _do_rename(name, argv[argc - 1], NULL);
+
+}
+
+static int _message(CMD_ARGS)
+{
+ int r = 0, i;
+ size_t sz = 1;
+ struct dm_task *dmt;
+ char *str;
+ const char *response;
+ uint64_t sector;
+ char *endptr;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
+ return_0;
+
+ if (_switches[UUID_ARG] || _switches[MAJOR_ARG]) {
+ if (!_set_task_device(dmt, NULL, 0))
+ goto_out;
+ } else {
+ if (!_set_task_device(dmt, argv[0], 0))
+ goto_out;
+ argc--;
+ argv++;
+ }
+
+ errno = 0;
+ sector = strtoull(argv[0], &endptr, 10);
+ if (errno || *endptr || endptr == argv[0]) {
+ log_error("Invalid sector.");
+ goto out;
+ }
+ if (!dm_task_set_sector(dmt, sector))
+ goto_out;
+
+ argc--;
+ argv++;
+
+ if (argc <= 0)
+ log_error("No message supplied.");
+
+ for (i = 0; i < argc; i++)
+ sz += strlen(argv[i]) + 1;
+
+ if (!(str = dm_zalloc(sz))) {
+ log_error("Message string allocation failed.");
+ goto out;
+ }
+
+ for (i = 0; i < argc; i++) {
+ if (i)
+ strcat(str, " ");
+ strcat(str, argv[i]);
+ }
+
+ i = dm_task_set_message(dmt, str);
+
+ free(str);
+
+ if (!i)
+ goto_out;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto_out;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto_out;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ if (!_task_run(dmt))
+ goto_out;
+
+ if ((response = dm_task_get_message_response(dmt))) {
+ if (!*response || response[strlen(response) - 1] == '\n')
+ fputs(response, stdout);
+ else
+ puts(response);
+ }
+
+ r = 1;
+
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _setgeometry(CMD_ARGS)
+{
+ int r = 0;
+ struct dm_task *dmt;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_SET_GEOMETRY)))
+ return_0;
+
+ if (_switches[UUID_ARG] || _switches[MAJOR_ARG]) {
+ if (!_set_task_device(dmt, NULL, 0))
+ goto_out;
+ } else {
+ if (!_set_task_device(dmt, argv[0], 0))
+ goto_out;
+ argc--;
+ argv++;
+ }
+
+ if (!dm_task_set_geometry(dmt, argv[0], argv[1], argv[2], argv[3]))
+ goto_out;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto_out;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto_out;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ /* run the task */
+ if (!_task_run(dmt))
+ goto_out;
+
+ r = 1;
+
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _splitname(CMD_ARGS)
+{
+ struct dmsetup_report_obj obj = { NULL };
+ int r;
+
+ if (!(obj.split_name = _get_split_name((argc == 2) ? argv[1] : "LVM",
+ argv[0], '\0')))
+ return_0;
+
+ r = dm_report_object(_report, &obj);
+ _destroy_split_name(obj.split_name);
+
+ return r;
+}
+
+static uint32_t _get_cookie_value(const char *str_value)
+{
+ unsigned long int value;
+ char *p;
+
+ errno = 0;
+ value = strtoul(str_value, &p, 0);
+
+ if (errno || !value || (*p) || (value > UINT32_MAX)) {
+ log_error("Incorrect cookie value.");
+ return 0;
+ }
+
+ return (uint32_t) value;
+}
+
+static int _udevflags(CMD_ARGS)
+{
+ uint32_t cookie;
+ uint16_t flags;
+ int i;
+ static const char *dm_flag_names[] = {"DISABLE_DM_RULES",
+ "DISABLE_SUBSYSTEM_RULES",
+ "DISABLE_DISK_RULES",
+ "DISABLE_OTHER_RULES",
+ "LOW_PRIORITY",
+ "DISABLE_LIBRARY_FALLBACK",
+ "PRIMARY_SOURCE",
+ 0};
+
+ if (!(cookie = _get_cookie_value(argv[0])))
+ return_0;
+
+ flags = cookie >> DM_UDEV_FLAGS_SHIFT;
+
+ for (i = 0; i < DM_UDEV_FLAGS_SHIFT; i++)
+ if (1 << i & flags) {
+ if (i < DM_UDEV_FLAGS_SHIFT / 2 && dm_flag_names[i])
+ printf("DM_UDEV_%s_FLAG='1'\n", dm_flag_names[i]);
+ else if (i < DM_UDEV_FLAGS_SHIFT / 2)
+ /*
+ * This is just a fallback. Each new DM flag
+ * should have its symbolic name assigned.
+ */
+ printf("DM_UDEV_FLAG%d='1'\n", i);
+ else
+ /*
+ * We can't assign symbolic names to subsystem
+ * flags. Their semantics vary based on the
+ * subsystem that is currently used.
+ */
+ printf("DM_SUBSYSTEM_UDEV_FLAG%d='1'\n",
+ i - DM_UDEV_FLAGS_SHIFT / 2);
+ }
+
+ return 1;
+}
+
+static int _udevcomplete(CMD_ARGS)
+{
+ uint32_t cookie;
+
+ if (!(cookie = _get_cookie_value(argv[0])))
+ return_0;
+
+ /*
+ * Strip flags from the cookie and use cookie magic instead.
+ * If the cookie has non-zero prefix and the base is zero then
+ * this one carries flags to control udev rules only and it is
+ * not meant to be for notification. Return with success in this
+ * situation.
+ */
+ if (!(cookie &= ~DM_UDEV_FLAGS_MASK))
+ return 1;
+
+ cookie |= DM_COOKIE_MAGIC << DM_UDEV_FLAGS_SHIFT;
+
+ return dm_udev_complete(cookie);
+}
+
+#ifndef UDEV_SYNC_SUPPORT
+static const char _cmd_not_supported[] = "Command not supported. Recompile with \"--enable-udev_sync\" to enable.";
+
+static int _udevcreatecookie(CMD_ARGS)
+{
+ log_error(_cmd_not_supported);
+
+ return 0;
+}
+
+static int _udevreleasecookie(CMD_ARGS)
+{
+ log_error(_cmd_not_supported);
+
+ return 0;
+}
+
+static int _udevcomplete_all(CMD_ARGS)
+{
+ log_error(_cmd_not_supported);
+
+ return 0;
+}
+
+static int _udevcookies(CMD_ARGS)
+{
+ log_error(_cmd_not_supported);
+
+ return 0;
+}
+
+#else /* UDEV_SYNC_SUPPORT */
+static int _set_up_udev_support(const char *dev_dir)
+{
+ int dirs_diff;
+ const char *env;
+ size_t len = strlen(dev_dir), udev_dir_len = strlen(DM_UDEV_DEV_DIR);
+
+ if (_switches[NOUDEVSYNC_ARG])
+ dm_udev_set_sync_support(0);
+
+ if (!_udev_cookie) {
+ env = getenv(DM_UDEV_COOKIE_ENV_VAR_NAME);
+ if (env && *env && (_udev_cookie = _get_cookie_value(env)))
+ log_debug("Using udev transaction 0x%08" PRIX32
+ " defined by %s environment variable.",
+ _udev_cookie,
+ DM_UDEV_COOKIE_ENV_VAR_NAME);
+ }
+ else if (_switches[UDEVCOOKIE_ARG])
+ log_debug("Using udev transaction 0x%08" PRIX32
+ " defined by --udevcookie option.",
+ _udev_cookie);
+
+ /*
+ * Normally, there's always a fallback action by libdevmapper if udev
+ * has not done its job correctly, e.g. the nodes were not created.
+ * If using udev transactions by specifying existing cookie value,
+ * we need to disable node creation by libdevmapper completely,
+ * disabling any fallback actions, since any synchronisation happens
+ * at the end of the transaction only. We need to do this to prevent
+ * races between udev and libdevmapper but only in case udev "dev path"
+ * is the same as "dev path" used by libdevmapper.
+ */
+
+
+ /*
+ * DM_UDEV_DEV_DIR always has '/' at its end.
+ * If the dev_dir does not have it, be sure
+ * to make the right comparison without the '/' char!
+ */
+ if (dev_dir[len - 1] != '/')
+ udev_dir_len--;
+
+ dirs_diff = udev_dir_len != len ||
+ strncmp(DM_UDEV_DEV_DIR, dev_dir, len);
+ _udev_only = !dirs_diff && (_udev_cookie || !_switches[VERIFYUDEV_ARG]);
+
+ if (dirs_diff) {
+ log_debug("The path %s used for creating device nodes that is "
+ "set via DM_DEV_DIR environment variable differs from "
+ "the path %s that is used by udev. All warnings "
+ "about udev not working correctly while processing "
+ "particular nodes will be suppressed. These nodes "
+ "and symlinks will be managed in each directory "
+ "separately.", dev_dir, DM_UDEV_DEV_DIR);
+ dm_udev_set_checking(0);
+ }
+
+ return 1;
+}
+
+static int _udevcreatecookie(CMD_ARGS)
+{
+ uint32_t cookie;
+
+ if (!dm_udev_create_cookie(&cookie))
+ return_0;
+
+ if (cookie)
+ printf("0x%08" PRIX32 "\n", cookie);
+
+ return 1;
+}
+
+static int _udevreleasecookie(CMD_ARGS)
+{
+ if (argv[0] && !(_udev_cookie = _get_cookie_value(argv[0])))
+ return_0;
+
+ if (!_udev_cookie) {
+ log_error("No udev transaction cookie given.");
+ return 0;
+ }
+
+ return dm_udev_wait(_udev_cookie);
+}
+
+__attribute__((format(printf, 1, 2)))
+static char _yes_no_prompt(const char *prompt, ...)
+{
+ int c = 0, ret = 0;
+ va_list ap;
+
+ do {
+ if (c == '\n' || !c) {
+ va_start(ap, prompt);
+ vprintf(prompt, ap);
+ va_end(ap);
+ }
+
+ if ((c = getchar()) == EOF) {
+ ret = 'n';
+ break;
+ }
+
+ c = tolower(c);
+ if ((c == 'y') || (c == 'n'))
+ ret = c;
+ } while (!ret || c != '\n');
+
+ if (c != '\n')
+ putchar('\n');
+
+ return ret;
+}
+
+static int _udevcomplete_all(CMD_ARGS)
+{
+ int max_id, id, sid;
+ struct seminfo sinfo;
+ struct semid_ds sdata;
+ int counter = 0;
+ int skipped = 0;
+ unsigned age = 0;
+ time_t t;
+
+ if (argc == 1 && (sscanf(argv[0], "%u", &age) != 1)) {
+ log_error("Failed to read age_in_minutes parameter.");
+ return 0;
+ }
+
+ if (!_switches[YES_ARG]) {
+ log_warn("WARNING: This operation will destroy all semaphores %s%.0d%swith keys "
+ "that have a prefix %" PRIu16 " (0x%" PRIx16 ").",
+ age ? "older than " : "", age, age ? " minutes " : "",
+ DM_COOKIE_MAGIC, DM_COOKIE_MAGIC);
+
+ if (_yes_no_prompt("Do you really want to continue? [y/n]: ") == 'n') {
+ log_print("Semaphores with keys prefixed by %" PRIu16
+ " (0x%" PRIx16 ") NOT destroyed.",
+ DM_COOKIE_MAGIC, DM_COOKIE_MAGIC);
+ return 1;
+ }
+ }
+
+ if ((max_id = semctl(0, 0, SEM_INFO, &sinfo)) < 0) {
+ log_sys_error("semctl", "SEM_INFO");
+ return 0;
+ }
+
+ for (id = 0; id <= max_id; id++) {
+ if ((sid = semctl(id, 0, SEM_STAT, &sdata)) < 0)
+ continue;
+
+ if (sdata.sem_perm.__key >> 16 == DM_COOKIE_MAGIC) {
+ t = time(NULL);
+
+ if (sdata.sem_ctime + age * 60 > t ||
+ sdata.sem_otime + age * 60 > t) {
+ skipped++;
+ continue;
+ }
+ if (semctl(sid, 0, IPC_RMID, 0) < 0) {
+ log_error("Could not cleanup notification semaphore "
+ "with semid %d and cookie value "
+ FMTu32 " (0x" FMTx32 ").", sid,
+ sdata.sem_perm.__key, sdata.sem_perm.__key);
+ continue;
+ }
+
+ counter++;
+ }
+ }
+
+ log_print("%d semaphores with keys prefixed by "
+ FMTu16 " (0x" FMTx16 ") destroyed. %d skipped.",
+ counter, DM_COOKIE_MAGIC, DM_COOKIE_MAGIC, skipped);
+
+ return 1;
+}
+
+static int _udevcookies(CMD_ARGS)
+{
+ int max_id, id, sid;
+ struct seminfo sinfo;
+ struct semid_ds sdata;
+ int val;
+ char otime_str[26], ctime_str[26];
+ char *otimes, *ctimes;
+
+ if ((max_id = semctl(0, 0, SEM_INFO, &sinfo)) < 0) {
+ log_sys_error("sem_ctl", "SEM_INFO");
+ return 0;
+ }
+
+ printf("Cookie Semid Value Last semop time Last change time\n");
+
+ for (id = 0; id <= max_id; id++) {
+ if ((sid = semctl(id, 0, SEM_STAT, &sdata)) < 0)
+ continue;
+
+ if (sdata.sem_perm.__key >> 16 == DM_COOKIE_MAGIC) {
+ if ((val = semctl(sid, 0, GETVAL)) < 0) {
+ log_error("semid %d: sem_ctl failed for "
+ "cookie 0x%" PRIx32 ": %s",
+ sid, sdata.sem_perm.__key,
+ strerror(errno));
+ continue;
+ }
+
+ if ((otimes = ctime_r((const time_t *) &sdata.sem_otime, (char *)&otime_str)))
+ otime_str[strlen(otimes)-1] = '\0';
+ if ((ctimes = ctime_r((const time_t *) &sdata.sem_ctime, (char *)&ctime_str)))
+ ctime_str[strlen(ctimes)-1] = '\0';
+
+ printf("0x%-10x %-10d %-10d %s %s\n", sdata.sem_perm.__key,
+ sid, val, otimes ? : "unknown",
+ ctimes? : "unknown");
+ }
+ }
+
+ return 1;
+}
+#endif /* UDEV_SYNC_SUPPORT */
+
+static int _version(CMD_ARGS)
+{
+ char version[80];
+
+ if (dm_get_library_version(version, sizeof(version)))
+ printf("Library version: %s\n", version);
+
+ if (!dm_driver_version(version, sizeof(version)))
+ return_0;
+
+ printf("Driver version: %s\n", version);
+
+ /* don't output column headings for 'dmstats version'. */
+ if (_report) {
+ dm_report_free(_report);
+ _report = NULL;
+ }
+
+ return 1;
+}
+
+static int _simple(int task, const char *name, uint32_t event_nr, int display)
+{
+ uint32_t cookie = 0;
+ uint16_t udev_flags = 0;
+ int udev_wait_flag = task == DM_DEVICE_RESUME ||
+ task == DM_DEVICE_REMOVE;
+ int r = 0;
+
+ struct dm_task *dmt;
+
+ if (!(dmt = dm_task_create(task)))
+ return_0;
+
+ if (!_set_task_device(dmt, name, 0))
+ goto_out;
+
+ if (event_nr && !dm_task_set_event_nr(dmt, event_nr))
+ goto_out;
+
+ if (_switches[NOFLUSH_ARG] && !dm_task_no_flush(dmt))
+ goto_out;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto_out;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto_out;
+
+ if (_switches[NOLOCKFS_ARG] && !dm_task_skip_lockfs(dmt))
+ goto_out;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ /* FIXME: needs to coperate with udev */
+ if (!_set_task_add_node(dmt))
+ goto_out;
+
+ if (_switches[READAHEAD_ARG] &&
+ !dm_task_set_read_ahead(dmt, _int_args[READAHEAD_ARG],
+ _read_ahead_flags))
+ goto_out;
+
+ if (_switches[NOUDEVRULES_ARG])
+ udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
+ DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
+
+ if (_udev_cookie)
+ cookie = _udev_cookie;
+
+ if (_udev_only)
+ udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
+
+ if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, udev_flags))
+ goto_out;
+
+ if (_switches[RETRY_ARG] && task == DM_DEVICE_REMOVE)
+ dm_task_retry_remove(dmt);
+
+ if (_switches[DEFERRED_ARG] && (task == DM_DEVICE_REMOVE || task == DM_DEVICE_REMOVE_ALL))
+ dm_task_deferred_remove(dmt);
+
+ r = _task_run(dmt);
+
+out:
+ if (!_udev_cookie && udev_wait_flag)
+ (void) dm_udev_wait(cookie);
+
+ if (r && display && _switches[VERBOSE_ARG])
+ r = _display_info(dmt);
+
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _suspend(CMD_ARGS)
+{
+ return _simple(DM_DEVICE_SUSPEND, argc ? argv[0] : NULL, 0, 1);
+}
+
+static int _resume(CMD_ARGS)
+{
+ return _simple(DM_DEVICE_RESUME, argc ? argv[0] : NULL, 0, 1);
+}
+
+static int _clear(CMD_ARGS)
+{
+ return _simple(DM_DEVICE_CLEAR, argc ? argv[0] : NULL, 0, 1);
+}
+
+static int _wait(CMD_ARGS)
+{
+ const char *name = NULL;
+
+ if (!_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
+ if (!argc) {
+ log_error("No device specified.");
+ return 0;
+ }
+ name = argv[0];
+ argc--, argv++;
+ }
+
+ return _simple(DM_DEVICE_WAITEVENT, name,
+ (argc) ? (uint32_t) atoi(argv[argc - 1]) : 0, 1);
+}
+
+static int _process_all(const struct command *cmd, const char *subcommand, int argc, char **argv, int silent,
+ int (*fn) (CMD_ARGS))
+{
+ int r = 1;
+ struct dm_names *names;
+ unsigned next = 0;
+
+ struct dm_task *dmt;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
+ return_0;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ if (!_task_run(dmt)) {
+ r = 0;
+ goto_out;
+ }
+
+ if (!(names = dm_task_get_names(dmt))) {
+ r = 0;
+ goto_out;
+ }
+
+ if (!names->dev) {
+ if (!silent)
+ printf("No devices found\n");
+ goto out;
+ }
+
+ do {
+ names = (struct dm_names *)((char *) names + next);
+ if (!fn(cmd, subcommand, argc, argv, names, 1))
+ r = 0;
+ next = names->next;
+ } while (next);
+
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static uint64_t _get_device_size(const char *name)
+{
+ uint64_t start, length, size = UINT64_C(0);
+ struct dm_info info;
+ char *target_type, *params;
+ struct dm_task *dmt;
+ void *next = NULL;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
+ return_0;
+
+ if (!_set_task_device(dmt, name, 0))
+ goto_out;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto_out;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto_out;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ if (!_task_run(dmt))
+ goto_out;
+
+ if (!dm_task_get_info(dmt, &info) || !info.exists)
+ goto_out;
+
+ do {
+ next = dm_get_next_target(dmt, next, &start, &length,
+ &target_type, ¶ms);
+ size += length;
+ } while (next);
+
+out:
+ dm_task_destroy(dmt);
+ return size;
+}
+
+static int _error_device(CMD_ARGS)
+{
+ struct dm_task *dmt;
+ const char *name;
+ uint64_t size;
+ int r = 0;
+
+ name = names ? names->name : argv[0];
+
+ if (!name || !*name) {
+ log_error("No device specified.");
+ return 0;
+ }
+
+ size = _get_device_size(name);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
+ return_0;
+
+ if (!_set_task_device(dmt, name, 0))
+ goto_bad;
+
+ if (!dm_task_add_target(dmt, UINT64_C(0), size, "error", ""))
+ goto_bad;
+
+ if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
+ goto_bad;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto_bad;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto_bad;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_bad;
+
+ if (!_task_run(dmt))
+ goto_bad;
+
+ if (_switches[FORCE_ARG])
+ /* Avoid hang on flushing with --force */
+ _switches[NOLOCKFS_ARG] = _switches[NOFLUSH_ARG] = 1;
+
+ if (!_simple(DM_DEVICE_RESUME, name, 0, 0)) {
+ _simple(DM_DEVICE_CLEAR, name, 0, 0);
+ goto_bad;
+ }
+
+ r = 1;
+
+bad:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _remove(CMD_ARGS)
+{
+ if (_switches[FORCE_ARG] && argc) {
+ /*
+ * 'remove --force' option is doing 2 operations on the same device
+ * this is not compatible with the use of --udevcookie/DM_UDEV_COOKIE.
+ * Udevd collision could be partially avoided with --retry.
+ */
+ if (_udev_cookie)
+ log_warn("WARNING: Use of cookie and --force is not compatible.");
+ (void) _error_device(cmd, NULL, argc, argv, NULL, 0);
+ }
+
+ return _simple(DM_DEVICE_REMOVE, argc ? argv[0] : NULL, 0, 0);
+}
+
+static int _count_devices(CMD_ARGS)
+{
+ _num_devices++;
+
+ return 1;
+}
+
+static int _remove_all(CMD_ARGS)
+{
+ int r;
+
+ /* Remove all closed devices */
+ r = _simple(DM_DEVICE_REMOVE_ALL, "", 0, 0) | dm_mknodes(NULL);
+
+ if (!_switches[FORCE_ARG])
+ return r;
+
+ _num_devices = 0;
+ r |= _process_all(cmd, NULL, argc, argv, 1, _count_devices);
+
+ /* No devices left? */
+ if (!_num_devices)
+ return r;
+
+ r |= _process_all(cmd, NULL, argc, argv, 1, _error_device);
+ r |= _simple(DM_DEVICE_REMOVE_ALL, "", 0, 0) | dm_mknodes(NULL);
+
+ _num_devices = 0;
+ r |= _process_all(cmd, NULL, argc, argv, 1, _count_devices);
+ if (!_num_devices)
+ return r;
+
+ log_error("Unable to remove %d device(s).", _num_devices);
+
+ return r;
+}
+
+static void _display_dev(struct dm_task *dmt, const char *name)
+{
+ struct dm_info info;
+
+ if (dm_task_get_info(dmt, &info))
+ printf("%s\t(%u, %u)\n", name, info.major, info.minor);
+}
+
+static int _mknodes(CMD_ARGS)
+{
+ return dm_mknodes(argc ? argv[0] : NULL);
+}
+
+static int _exec_command(const char *name)
+{
+ static char path[PATH_MAX];
+ static char *args[ARGS_MAX + 1];
+ static int argc = 0;
+ char *c;
+ pid_t pid;
+
+ if (argc < 0)
+ return_0;
+
+ if (!dm_mknodes(name))
+ return_0;
+
+ if (dm_snprintf(path, sizeof(path), "%s/%s", dm_dir(), name) < 0)
+ return_0;
+
+ if (!argc) {
+ c = _command_to_exec;
+ while (argc < ARGS_MAX) {
+ while (*c && isspace(*c))
+ c++;
+ if (!*c)
+ break;
+ args[argc++] = c;
+ while (*c && !isspace(*c))
+ c++;
+ if (*c)
+ *c++ = '\0';
+ }
+
+ if (!argc) {
+ argc = -1;
+ return_0;
+ }
+
+ if (argc == ARGS_MAX) {
+ log_error("Too many args to --exec.");
+ argc = -1;
+ return 0;
+ }
+
+ args[argc++] = path;
+ args[argc] = NULL;
+ }
+
+ if (!(pid = fork())) {
+ execvp(args[0], args);
+ _exit(127);
+ } else if (pid < (pid_t) 0)
+ return 0;
+
+ TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
+
+ return 1;
+}
+
+/*
+ * Print string s using a backslash to quote each character that has a special
+ * meaning in the concise format - comma, semi-colon and backslash.
+ */
+static void _print_string_quoted(const char *s)
+{
+ while (*s) {
+ if (strchr(",;\\", *s))
+ putchar('\\');
+ putchar(*s++);
+ }
+}
+
+static void hide_key(char *params, const char *name)
+{
+ char *c = strstr(params, name);
+
+ if (!c)
+ return;
+
+ c += strlen(name);
+
+ /* key is optional */
+ c = strpbrk(c, " :");
+ if (!c || *c++ != ':')
+ return;
+
+ while (*c && *c != ' ')
+ *c++ = '0';
+}
+
+static int _status(CMD_ARGS)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ void *next = NULL;
+ uint64_t start, length;
+ char *target_type = NULL;
+ char *params, *c;
+ int cmdno;
+ const char *name = NULL;
+ int matched = 0;
+ int ls_only = 0;
+ int use_concise = 0;
+ struct dm_info info;
+
+ if (names)
+ name = names->name;
+ else {
+ if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
+ /* FIXME Respect deps in concise mode, so they are correctly ordered for recreation */
+ return _process_all(cmd, NULL, argc, argv, 0, _status);
+ name = argv[0];
+ }
+
+ if (!strcmp(cmd->name, "table")) {
+ cmdno = DM_DEVICE_TABLE;
+ /* --concise only applies to 'table' */
+ if (_switches[CONCISE_ARG])
+ use_concise = 1;
+ } else
+ cmdno = DM_DEVICE_STATUS;
+
+ if (!strcmp(cmd->name, "ls"))
+ ls_only = 1;
+
+ if (!(dmt = dm_task_create(cmdno)))
+ return_0;
+
+ if (!_set_task_device(dmt, name, 0))
+ goto_out;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto_out;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto_out;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ if (_switches[NOFLUSH_ARG] && !dm_task_no_flush(dmt))
+ goto_out;
+
+ if (!_task_run(dmt))
+ goto_out;
+
+ if (!dm_task_get_info(dmt, &info))
+ goto_out;
+
+ if (!info.exists) {
+ log_error("Device does not exist.");
+ goto out;
+ }
+
+ if (!name)
+ name = dm_task_get_name(dmt);
+
+ /* Fetch targets and print 'em */
+ do {
+ next = dm_get_next_target(dmt, next, &start, &length,
+ &target_type, ¶ms);
+ /* Skip if target type doesn't match */
+ if (_switches[TARGET_ARG] &&
+ (!target_type || strcmp(target_type, _target)))
+ continue;
+
+ if (ls_only) {
+ if (!_switches[EXEC_ARG] || !_command_to_exec ||
+ _switches[VERBOSE_ARG])
+ _display_dev(dmt, name);
+ next = NULL;
+ } else if (!_switches[EXEC_ARG] || !_command_to_exec ||
+ _switches[VERBOSE_ARG]) {
+ if (!use_concise) {
+ if (!matched && _switches[VERBOSE_ARG])
+ _display_info(dmt);
+ if (multiple_devices && !_switches[VERBOSE_ARG])
+ printf("%s: ", name);
+ } else if (!matched) {
+ /*
+ * Before first target of device in concise output,
+ * print basic device information in the appropriate format.
+ * Separate devices by a semi-colon.
+ */
+ if (_concise_output_produced)
+ putchar(';');
+ _concise_output_produced = 1;
+
+ _print_string_quoted(name);
+ putchar(',');
+ _print_string_quoted(dm_task_get_uuid(dmt));
+ printf(",%u,%s", info.minor, info.read_only ? "ro" : "rw");
+ }
+ /* Next print any target-specific information */
+ if (target_type) {
+ /* Suppress encryption keys */
+ if (!_switches[SHOWKEYS_ARG] &&
+ cmdno == DM_DEVICE_TABLE) {
+ if (!strcmp(target_type, "crypt")) {
+ c = params;
+ while (*c && *c != ' ')
+ c++;
+ if (*c)
+ c++;
+ /*
+ * Do not suppress kernel key references prefixed
+ * with colon ':'. Displaying those references is
+ * harmless. crypt target supports kernel keys
+ * starting with v1.15.0 (merged in kernel 4.10)
+ */
+ if (*c != ':')
+ while (*c && *c != ' ')
+ *c++ = '0';
+ } else if (!strcmp(target_type, "integrity")) {
+ /*
+ * "internal_hash", "journal_crypt" and "journal_mac"
+ * params allow keys optionally in hexbyte
+ * representation.
+ */
+ hide_key(params, "internal_hash:");
+ hide_key(params, "journal_crypt:");
+ hide_key(params, "journal_mac:");
+ }
+ }
+ if (use_concise)
+ putchar(',');
+ printf(FMTu64 " " FMTu64 " %s ", start, length, target_type);
+ if (use_concise)
+ _print_string_quoted(params);
+ else
+ printf("%s", params);
+ }
+ /* --concise places all devices on a single output line */
+ if (!use_concise)
+ putchar('\n');
+ }
+ matched = 1;
+ } while (next);
+
+ if (multiple_devices && _switches[VERBOSE_ARG] && matched && !ls_only && (!use_concise || _switches[VERBOSE_ARG] > 1))
+ putchar('\n');
+
+ if (matched && _switches[EXEC_ARG] && _command_to_exec && !_exec_command(name))
+ goto_out;
+
+ r = 1;
+
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+/* Show target names and their version numbers */
+static int _targets(CMD_ARGS)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ struct dm_versions *target;
+ struct dm_versions *last_target;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
+ return_0;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ if (!_task_run(dmt))
+ goto_out;
+
+ target = dm_task_get_versions(dmt);
+
+ /* Fetch targets and print 'em */
+ do {
+ last_target = target;
+
+ printf("%-16s v%d.%d.%d\n", target->name, target->version[0],
+ target->version[1], target->version[2]);
+
+ target = (struct dm_versions *)((char *) target + target->next);
+ } while (last_target != target);
+
+ r = 1;
+
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _info(CMD_ARGS)
+{
+ int r = 0;
+
+ struct dm_task *dmt;
+ char *name = NULL;
+
+ if (names)
+ name = names->name;
+ else {
+ if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
+ return _process_all(cmd, NULL, argc, argv, 0, _info);
+ name = argv[0];
+ }
+
+ if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+ return_0;
+
+ if (!_set_task_device(dmt, name, 0))
+ goto_out;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto_out;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto_out;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ if (!_task_run(dmt))
+ goto_out;
+
+ r = _display_info(dmt);
+
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _deps(CMD_ARGS)
+{
+ int r = 0;
+ uint32_t i;
+ struct dm_deps *deps;
+ struct dm_task *dmt;
+ struct dm_info info;
+ char *name = NULL;
+ char dev_name[PATH_MAX];
+ int major, minor;
+
+ if (names)
+ name = names->name;
+ else {
+ if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
+ return _process_all(cmd, NULL, argc, argv, 0, _deps);
+ name = argv[0];
+ }
+
+ if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
+ return_0;
+
+ if (!_set_task_device(dmt, name, 0))
+ goto_out;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto_out;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto_out;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ if (!_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) {
+ printf("Device does not exist.\n");
+ r = 1;
+ goto out;
+ }
+
+ if (_switches[VERBOSE_ARG])
+ _display_info(dmt);
+
+ if (multiple_devices && !_switches[VERBOSE_ARG])
+ printf("%s: ", name);
+ printf("%d dependencies\t:", deps->count);
+
+ for (i = 0; i < deps->count; i++) {
+ major = (int) MAJOR(deps->device[i]);
+ minor = (int) MINOR(deps->device[i]);
+
+ if ((_dev_name_type == DN_BLK || _dev_name_type == DN_MAP) &&
+ dm_device_get_name(major, minor, _dev_name_type == DN_BLK,
+ dev_name, PATH_MAX))
+ printf(" (%s)", dev_name);
+ else
+ printf(" (%d, %d)", major, minor);
+ }
+ putchar('\n');
+
+ if (multiple_devices && _switches[VERBOSE_ARG])
+ putchar('\n');
+
+ r = 1;
+
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _display_name(CMD_ARGS)
+{
+ char dev_name[PATH_MAX];
+
+ if (!names)
+ return 1;
+
+ if ((_dev_name_type == DN_BLK || _dev_name_type == DN_MAP) &&
+ dm_device_get_name((int) MAJOR(names->dev), (int) MINOR(names->dev),
+ _dev_name_type == DN_BLK, dev_name, PATH_MAX))
+ printf("%s\t(%s)\n", names->name, dev_name);
+ else
+ printf("%s\t(%d:%d)\n", names->name,
+ (int) MAJOR(names->dev),
+ (int) MINOR(names->dev));
+
+ return 1;
+}
+
+/*
+ * Tree drawing code
+ */
+
+enum {
+ TR_DEVICE=0, /* display device major:minor number */
+ TR_BLKDEVNAME, /* display device kernel name */
+ TR_TABLE,
+ TR_STATUS,
+ TR_ACTIVE,
+ TR_RW,
+ TR_OPENCOUNT,
+ TR_UUID,
+ TR_COMPACT,
+ TR_TRUNCATE,
+ TR_BOTTOMUP,
+ NUM_TREEMODE,
+};
+
+static int _tree_switches[NUM_TREEMODE];
+
+#define TR_PRINT_ATTRIBUTE ( _tree_switches[TR_ACTIVE] || \
+ _tree_switches[TR_RW] || \
+ _tree_switches[TR_OPENCOUNT] || \
+ _tree_switches[TR_UUID] )
+
+#define TR_PRINT_TARGETS ( _tree_switches[TR_TABLE] || \
+ _tree_switches[TR_STATUS] )
+
+/* Compact - fewer newlines */
+#define TR_PRINT_COMPACT (_tree_switches[TR_COMPACT] && \
+ !TR_PRINT_ATTRIBUTE && \
+ !TR_PRINT_TARGETS)
+
+/* FIXME Get rid of this */
+#define MAX_DEPTH 100
+
+/* Drawing character definition from pstree */
+/* [pstree comment] UTF-8 defines by Johan Myreen, updated by Ben Winslow */
+#define UTF_V "\342\224\202" /* U+2502, Vertical line drawing char */
+#define UTF_VR "\342\224\234" /* U+251C, Vertical and right */
+#define UTF_H "\342\224\200" /* U+2500, Horizontal */
+#define UTF_UR "\342\224\224" /* U+2514, Up and right */
+#define UTF_HD "\342\224\254" /* U+252C, Horizontal and down */
+
+#define VT_BEG "\033(0\017" /* use graphic chars */
+#define VT_END "\033(B" /* back to normal char set */
+#define VT_V "x" /* see UTF definitions above */
+#define VT_VR "t"
+#define VT_H "q"
+#define VT_UR "m"
+#define VT_HD "w"
+
+static struct {
+ const char *empty_2; /* */
+ const char *branch_2; /* |- */
+ const char *vert_2; /* | */
+ const char *last_2; /* `- */
+ const char *single_3; /* --- */
+ const char *first_3; /* -+- */
+}
+_tsym_ascii = {
+ " ",
+ "|-",
+ "| ",
+ "`-",
+ "---",
+ "-+-"
+},
+_tsym_utf = {
+ " ",
+ UTF_VR UTF_H,
+ UTF_V " ",
+ UTF_UR UTF_H,
+ UTF_H UTF_H UTF_H,
+ UTF_H UTF_HD UTF_H
+},
+_tsym_vt100 = {
+ " ",
+ VT_BEG VT_VR VT_H VT_END,
+ VT_BEG VT_V VT_END " ",
+ VT_BEG VT_UR VT_H VT_END,
+ VT_BEG VT_H VT_H VT_H VT_END,
+ VT_BEG VT_H VT_HD VT_H VT_END
+},
+*_tsym = &_tsym_ascii;
+
+/*
+ * Tree drawing functions.
+ */
+/* FIXME Get rid of these statics - use dynamic struct */
+/* FIXME Explain what these vars are for */
+static int _tree_width[MAX_DEPTH], _tree_more[MAX_DEPTH];
+static int _termwidth = 80; /* Maximum output width */
+static int _cur_x = 1; /* Current horizontal output position */
+static char _last_char = 0;
+
+static void _out_char(const unsigned c)
+{
+ /* Only first UTF-8 char counts */
+ _cur_x += ((c & 0xc0) != 0x80);
+
+ if (!_tree_switches[TR_TRUNCATE]) {
+ putchar((int) c);
+ return;
+ }
+
+ /* Truncation? */
+ if (_cur_x <= _termwidth)
+ putchar((int) c);
+
+ if (_cur_x == _termwidth + 1 && ((c & 0xc0) != 0x80)) {
+ if (_last_char || (c & 0x80)) {
+ putchar('.');
+ putchar('.');
+ putchar('.');
+ } else {
+ _last_char = c;
+ _cur_x--;
+ }
+ }
+}
+
+static void _out_string(const char *str)
+{
+ while (*str)
+ _out_char((unsigned char) *str++);
+}
+
+/* non-negative integers only */
+static unsigned _out_int(unsigned num)
+{
+ unsigned digits = 0;
+ unsigned divi;
+
+ if (!num) {
+ _out_char('0');
+ return 1;
+ }
+
+ /* non zero case */
+ for (divi = 1; num / divi; divi *= 10)
+ digits++;
+
+ for (divi /= 10; divi; divi /= 10)
+ _out_char('0' + (num / divi) % 10);
+
+ return digits;
+}
+
+static void _out_newline(void)
+{
+ if (_last_char && _cur_x == _termwidth)
+ putchar(_last_char);
+ _last_char = 0;
+ putchar('\n');
+ _cur_x = 1;
+}
+
+static void _out_prefix(unsigned depth)
+{
+ unsigned x, d;
+
+ for (d = 0; d < depth; d++) {
+ for (x = _tree_width[d] + 1; x > 0; x--)
+ _out_char(' ');
+
+ _out_string(d == depth - 1 ?
+ !_tree_more[depth] ? _tsym->last_2 : _tsym->branch_2
+ : _tree_more[d + 1] ?
+ _tsym->vert_2 : _tsym->empty_2);
+ }
+}
+
+/*
+ * Display tree
+ */
+static void _display_tree_attributes(struct dm_tree_node *node)
+{
+ int attr = 0;
+ const char *uuid;
+ const struct dm_info *info;
+
+ uuid = dm_tree_node_get_uuid(node);
+ info = dm_tree_node_get_info(node);
+
+ if (!info->exists)
+ return;
+
+ if (_tree_switches[TR_ACTIVE]) {
+ _out_string(attr++ ? ", " : " [");
+ _out_string(info->suspended ? "SUSPENDED" : "ACTIVE");
+ }
+
+ if (_tree_switches[TR_RW]) {
+ _out_string(attr++ ? ", " : " [");
+ _out_string(info->read_only ? "RO" : "RW");
+ }
+
+ if (_tree_switches[TR_OPENCOUNT]) {
+ _out_string(attr++ ? ", " : " [");
+ (void) _out_int((unsigned) info->open_count);
+ }
+
+ if (_tree_switches[TR_UUID]) {
+ _out_string(attr++ ? ", " : " [");
+ _out_string(uuid && *uuid ? uuid : "");
+ }
+
+ if (attr)
+ _out_char(']');
+}
+
+/* FIXME Display table or status line. (Disallow both?) */
+static void _display_tree_targets(struct dm_tree_node *node, unsigned depth)
+{
+}
+
+static void _display_tree_node(struct dm_tree_node *node, unsigned depth,
+ unsigned first_child __attribute__((unused)),
+ unsigned last_child, unsigned has_children)
+{
+ int offset;
+ const char *name;
+ const struct dm_info *info;
+ int first_on_line = 0;
+ char dev_name[PATH_MAX];
+
+ /* Sub-tree for targets has 2 more depth */
+ if (depth + 2 > MAX_DEPTH)
+ return;
+
+ name = dm_tree_node_get_name(node);
+
+ if ((!name || !*name) &&
+ (!_tree_switches[TR_DEVICE] && !_tree_switches[TR_BLKDEVNAME]))
+ return;
+
+ /* Indicate whether there are more nodes at this depth */
+ _tree_more[depth] = !last_child;
+ _tree_width[depth] = 0;
+
+ if (_cur_x == 1)
+ first_on_line = 1;
+
+ if (!TR_PRINT_COMPACT || first_on_line)
+ _out_prefix(depth);
+
+ /* Remember the starting point for compact */
+ offset = _cur_x;
+
+ if (TR_PRINT_COMPACT && !first_on_line)
+ _out_string(_tree_more[depth] ? _tsym->first_3 : _tsym->single_3);
+
+ /* display node */
+ if (name)
+ _out_string(name);
+
+ info = dm_tree_node_get_info(node);
+
+ if (_tree_switches[TR_BLKDEVNAME] &&
+ dm_device_get_name(info->major, info->minor, 1, dev_name, PATH_MAX)) {
+ _out_string(name ? " <" : "<");
+ _out_string(dev_name);
+ _out_char('>');
+ }
+
+ if (_tree_switches[TR_DEVICE]) {
+ _out_string(name ? " (" : "(");
+ (void) _out_int(info->major);
+ _out_char(':');
+ (void) _out_int(info->minor);
+ _out_char(')');
+ }
+
+ /* display additional info */
+ if (TR_PRINT_ATTRIBUTE)
+ _display_tree_attributes(node);
+
+ if (TR_PRINT_COMPACT)
+ _tree_width[depth] = _cur_x - offset;
+
+ if (!TR_PRINT_COMPACT || !has_children)
+ _out_newline();
+
+ if (TR_PRINT_TARGETS) {
+ _tree_more[depth + 1] = has_children;
+ _display_tree_targets(node, depth + 2);
+ }
+}
+
+/*
+ * Walk the dependency tree
+ */
+static void _display_tree_walk_children(struct dm_tree_node *node,
+ unsigned depth)
+{
+ struct dm_tree_node *child, *next_child;
+ void *handle = NULL;
+ uint32_t inverted = _tree_switches[TR_BOTTOMUP];
+ unsigned first_child = 1;
+ unsigned has_children;
+
+ next_child = dm_tree_next_child(&handle, node, inverted);
+
+ while ((child = next_child)) {
+ next_child = dm_tree_next_child(&handle, node, inverted);
+ has_children =
+ dm_tree_node_num_children(child, inverted) ? 1 : 0;
+
+ _display_tree_node(child, depth, first_child,
+ next_child ? 0U : 1U, has_children);
+
+ if (has_children)
+ _display_tree_walk_children(child, depth + 1);
+
+ first_child = 0;
+ }
+}
+
+static int _add_dep(CMD_ARGS)
+{
+ if (names &&
+ !dm_tree_add_dev(_dtree, (unsigned) MAJOR(names->dev), (unsigned) MINOR(names->dev)))
+ return_0;
+
+ return 1;
+}
+
+/*
+ * Create and walk dependency tree
+ *
+ * An incomplete _dtree may still be used by the caller,
+ * but the error must be reported.
+ */
+static int _build_whole_deptree(const struct command *cmd)
+{
+ if (_dtree)
+ return 1;
+
+ if (!(_dtree = dm_tree_create()))
+ return_0;
+
+ if (!_process_all(cmd, NULL, 0, NULL, 0, _add_dep))
+ return_0;
+
+ return 1;
+}
+
+static int _display_tree(CMD_ARGS)
+{
+ int r;
+
+ r = _build_whole_deptree(cmd);
+
+ if (_dtree)
+ _display_tree_walk_children(dm_tree_find_node(_dtree, 0, 0), 0);
+
+ return r;
+}
+
+/*
+ * Report device information
+ */
+
+/* dm specific display functions */
+
+static int _int32_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const int32_t value = *(const int32_t *)data;
+
+ return dm_report_field_int32(rh, field, &value);
+}
+
+static int _uint32_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const uint32_t value = *(const int32_t *)data;
+
+ return dm_report_field_uint32(rh, field, &value);
+}
+
+static int _show_units(void)
+{
+ /* --nosuffix overrides --units */
+ if (_switches[NOSUFFIX_ARG])
+ return_0;
+
+ return (_int_args[UNITS_ARG]) ? 1 : 0;
+}
+
+static int _dm_name_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const char *name = dm_task_get_name((const struct dm_task *) data);
+
+ return dm_report_field_string(rh, field, &name);
+}
+
+static int _dm_mangled_name_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ char *name;
+ int r = 0;
+
+ if ((name = dm_task_get_name_mangled((const struct dm_task *) data))) {
+ r = dm_report_field_string(rh, field, (const char * const *) &name);
+ free(name);
+ }
+
+ return r;
+}
+
+static int _dm_unmangled_name_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ char *name;
+ int r = 0;
+
+ if ((name = dm_task_get_name_unmangled((const struct dm_task *) data))) {
+ r = dm_report_field_string(rh, field, (const char * const *) &name);
+ free(name);
+ }
+
+ return r;
+}
+
+static int _dm_uuid_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ const char *uuid = dm_task_get_uuid((const struct dm_task *) data);
+
+ if (!uuid || !*uuid)
+ uuid = "";
+
+ return dm_report_field_string(rh, field, &uuid);
+}
+
+static int _dm_mangled_uuid_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ char *uuid;
+ int r = 0;
+
+ if ((uuid = dm_task_get_uuid_mangled((const struct dm_task *) data))) {
+ r = dm_report_field_string(rh, field, (const char * const *) &uuid);
+ free(uuid);
+ }
+
+ return r;
+}
+
+static int _dm_unmangled_uuid_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ char *uuid;
+ int r = 0;
+
+ if ((uuid = dm_task_get_uuid_unmangled((const struct dm_task *) data))) {
+ r = dm_report_field_string(rh, field, (const char * const *) &uuid);
+ free(uuid);
+ }
+
+ return r;
+}
+
+static int _dm_read_ahead_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ uint32_t value;
+
+ if (!dm_task_get_read_ahead((const struct dm_task *) data, &value))
+ value = 0;
+
+ return dm_report_field_uint32(rh, field, &value);
+}
+
+static int _dm_blk_name_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ char dev_name[PATH_MAX];
+ const char *s = dev_name;
+ const struct dm_info *info = data;
+
+ if (!dm_device_get_name(info->major, info->minor, 1, dev_name, PATH_MAX)) {
+ log_error("Could not resolve block device name for %d:%d.",
+ info->major, info->minor);
+ return 0;
+ }
+
+ return dm_report_field_string(rh, field, &s);
+}
+
+static int _dm_info_status_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ char buf[5];
+ const char *s = buf;
+ const struct dm_info *info = data;
+
+ buf[0] = info->live_table ? 'L' : '-';
+ buf[1] = info->inactive_table ? 'I' : '-';
+ buf[2] = info->suspended ? 's' : '-';
+ buf[3] = info->read_only ? 'r' : 'w';
+ buf[4] = '\0';
+
+ return dm_report_field_string(rh, field, &s);
+}
+
+static int _dm_info_table_loaded_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field,
+ const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_info *info = data;
+
+ if (info->live_table) {
+ if (info->inactive_table)
+ dm_report_field_set_value(field, "Both", NULL);
+ else
+ dm_report_field_set_value(field, "Live", NULL);
+ return 1;
+ }
+
+ if (info->inactive_table)
+ dm_report_field_set_value(field, "Inactive", NULL);
+ else
+ dm_report_field_set_value(field, "None", NULL);
+
+ return 1;
+}
+
+static int _dm_info_suspended_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field,
+ const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_info *info = data;
+
+ if (info->suspended)
+ dm_report_field_set_value(field, "Suspended", NULL);
+ else
+ dm_report_field_set_value(field, "Active", NULL);
+
+ return 1;
+}
+
+static int _dm_info_read_only_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field,
+ const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_info *info = data;
+
+ if (info->read_only)
+ dm_report_field_set_value(field, "Read-only", NULL);
+ else
+ dm_report_field_set_value(field, "Writeable", NULL);
+
+ return 1;
+}
+
+
+static int _dm_info_devno_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field, const void *data,
+ void *private)
+{
+ char buf[PATH_MAX], *repstr;
+ const struct dm_info *info = data;
+
+ if (!dm_pool_begin_object(mem, 8)) {
+ log_error("dm_pool_begin_object failed");
+ return 0;
+ }
+
+ if (private) {
+ if (!dm_device_get_name(info->major, info->minor,
+ 1, buf, PATH_MAX)) {
+ stack;
+ goto out_abandon;
+ }
+ }
+ else {
+ if (dm_snprintf(buf, sizeof(buf), "%d:%d",
+ info->major, info->minor) < 0) {
+ log_error("dm_pool_alloc failed");
+ goto out_abandon;
+ }
+ }
+
+ if (!dm_pool_grow_object(mem, buf, strlen(buf) + 1)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+
+ repstr = dm_pool_end_object(mem);
+ dm_report_field_set_value(field, repstr, repstr);
+ return 1;
+
+ out_abandon:
+ dm_pool_abandon_object(mem);
+ return 0;
+}
+
+static int _dm_tree_names(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field, const void *data,
+ void *private, unsigned inverted)
+{
+ const struct dm_tree_node *node = data;
+ struct dm_tree_node *parent;
+ void *t = NULL;
+ const char *name;
+ int first_node = 1;
+ char *repstr;
+
+ if (!dm_pool_begin_object(mem, 16)) {
+ log_error("dm_pool_begin_object failed");
+ return 0;
+ }
+
+ while ((parent = dm_tree_next_child(&t, node, inverted))) {
+ name = dm_tree_node_get_name(parent);
+ if (!name || !*name)
+ continue;
+ if (!first_node && !dm_pool_grow_object(mem, ",", 1)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+ if (!dm_pool_grow_object(mem, name, 0)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+ if (first_node)
+ first_node = 0;
+ }
+
+ if (!dm_pool_grow_object(mem, "\0", 1)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+
+ repstr = dm_pool_end_object(mem);
+ dm_report_field_set_value(field, repstr, repstr);
+ return 1;
+
+ out_abandon:
+ dm_pool_abandon_object(mem);
+ return 0;
+}
+
+static int _dm_deps_names_disp(struct dm_report *rh,
+ struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ return _dm_tree_names(rh, mem, field, data, private, 0);
+}
+
+static int _dm_tree_parents_names_disp(struct dm_report *rh,
+ struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ return _dm_tree_names(rh, mem, field, data, private, 1);
+}
+
+static int _dm_tree_parents_devs_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct dm_tree_node *node = data;
+ struct dm_tree_node *parent;
+ void *t = NULL;
+ const struct dm_info *info;
+ int first_node = 1;
+ char buf[DM_MAX_TYPE_NAME], *repstr;
+
+ if (!dm_pool_begin_object(mem, 16)) {
+ log_error("dm_pool_begin_object failed");
+ return 0;
+ }
+
+ while ((parent = dm_tree_next_child(&t, node, 1))) {
+ info = dm_tree_node_get_info(parent);
+ if (!info->major && !info->minor)
+ continue;
+ if (!first_node && !dm_pool_grow_object(mem, ",", 1)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+ if (dm_snprintf(buf, sizeof(buf), "%d:%d",
+ info->major, info->minor) < 0) {
+ log_error("dm_snprintf failed");
+ goto out_abandon;
+ }
+ if (!dm_pool_grow_object(mem, buf, 0)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+ if (first_node)
+ first_node = 0;
+ }
+
+ if (!dm_pool_grow_object(mem, "\0", 1)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+
+ repstr = dm_pool_end_object(mem);
+ dm_report_field_set_value(field, repstr, repstr);
+ return 1;
+
+ out_abandon:
+ dm_pool_abandon_object(mem);
+ return 0;
+}
+
+static int _dm_tree_parents_count_disp(struct dm_report *rh,
+ struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct dm_tree_node *node = data;
+ int num_parent = dm_tree_node_num_children(node, 1);
+
+ return dm_report_field_int(rh, field, &num_parent);
+}
+
+static int _dm_deps_disp_common(struct dm_report *rh, struct dm_pool*mem,
+ struct dm_report_field *field, const void *data,
+ void *private, int disp_blk_dev_names)
+{
+ const struct dm_deps *deps = data;
+ char buf[PATH_MAX], *repstr;
+ int major, minor;
+ unsigned i;
+
+ if (!dm_pool_begin_object(mem, 16)) {
+ log_error("dm_pool_begin_object failed");
+ return 0;
+ }
+
+ for (i = 0; i < deps->count; i++) {
+ major = (int) MAJOR(deps->device[i]);
+ minor = (int) MINOR(deps->device[i]);
+
+ if (disp_blk_dev_names) {
+ if (!dm_device_get_name(major, minor, 1, buf, PATH_MAX)) {
+ log_error("Could not resolve block device "
+ "name for %d:%d.", major, minor);
+ goto out_abandon;
+ }
+ }
+ else if (dm_snprintf(buf, sizeof(buf), "%d:%d",
+ major, minor) < 0) {
+ log_error("dm_snprintf failed");
+ goto out_abandon;
+ }
+
+ if (!dm_pool_grow_object(mem, buf, 0)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+
+ if (i + 1 < deps->count && !dm_pool_grow_object(mem, ",", 1)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+ }
+
+ if (!dm_pool_grow_object(mem, "\0", 1)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+
+ repstr = dm_pool_end_object(mem);
+ dm_report_field_set_value(field, repstr, repstr);
+ return 1;
+
+ out_abandon:
+ dm_pool_abandon_object(mem);
+ return 0;
+}
+
+static int _dm_deps_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field, const void *data,
+ void *private)
+{
+ return _dm_deps_disp_common(rh, mem, field, data, private, 0);
+}
+
+static int _dm_deps_blk_names_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ return _dm_deps_disp_common(rh, mem, field, data, private, 1);
+}
+
+static int _dm_subsystem_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ return dm_report_field_string(rh, field, (const char *const *) data);
+}
+
+static int _dm_vg_name_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+
+ return dm_report_field_string(rh, field, (const char *const *) data);
+}
+
+static int _dm_lv_name_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+
+{
+ return dm_report_field_string(rh, field, (const char *const *) data);
+}
+
+static int _dm_lv_layer_name_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+
+{
+ return dm_report_field_string(rh, field, (const char *const *) data);
+}
+
+/**
+ * All _dm_stats_*_disp functions for basic counters are identical:
+ * obtain the value for the current region and area and pass it to
+ * dm_report_field_uint64().
+ */
+#define MK_STATS_COUNTER_DISP_FN(counter) \
+static int _dm_stats_ ## counter ## _disp(struct dm_report *rh, \
+ struct dm_pool *mem __attribute__((unused)), \
+ struct dm_report_field *field, const void *data, \
+ void *private __attribute__((unused))) \
+{ \
+ const struct dm_stats *dms = (const struct dm_stats *) data; \
+ uint64_t value = dm_stats_get_ ## counter(dms, DM_STATS_REGION_CURRENT, \
+ DM_STATS_AREA_CURRENT); \
+ return dm_report_field_uint64(rh, field, &value); \
+}
+
+MK_STATS_COUNTER_DISP_FN(reads)
+MK_STATS_COUNTER_DISP_FN(reads_merged)
+MK_STATS_COUNTER_DISP_FN(read_sectors)
+MK_STATS_COUNTER_DISP_FN(read_nsecs)
+MK_STATS_COUNTER_DISP_FN(writes)
+MK_STATS_COUNTER_DISP_FN(writes_merged)
+MK_STATS_COUNTER_DISP_FN(write_sectors)
+MK_STATS_COUNTER_DISP_FN(write_nsecs)
+MK_STATS_COUNTER_DISP_FN(io_in_progress)
+MK_STATS_COUNTER_DISP_FN(io_nsecs)
+MK_STATS_COUNTER_DISP_FN(weighted_io_nsecs)
+MK_STATS_COUNTER_DISP_FN(total_read_nsecs)
+MK_STATS_COUNTER_DISP_FN(total_write_nsecs)
+#undef MK_STATS_COUNTER_DISP_FN
+
+static int _dm_stats_region_id_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ uint64_t group_id, region_id = dm_stats_get_current_region(dms);
+ char *group_buf = NULL, *repstr;
+
+ if (dm_stats_current_object_type(dms) == DM_STATS_OBJECT_TYPE_GROUP) {
+ group_id = dm_stats_get_group_id(dms, dm_stats_get_current_region(dms));
+ if (!dm_stats_get_group_descriptor(dms, group_id, &group_buf))
+ return 0;
+ /* group_buf will disappear with the current handle */
+ repstr = dm_pool_strdup(mem, group_buf);
+ dm_report_field_set_value(field, repstr, &group_id);
+ return 1;
+ }
+
+ return dm_report_field_uint64(rh, field, ®ion_id);
+}
+
+static int _dm_stats_region_start_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ uint64_t region_start;
+ const char *repstr;
+ double *sortval;
+ char units = _disp_units;
+ uint64_t factor = _disp_factor;
+
+ if (!dm_stats_get_current_region_start(dms, ®ion_start))
+ return_0;
+
+ if (!(repstr = dm_size_to_string(mem, region_start, units, 1, factor,
+ _show_units(), DM_SIZE_UNIT)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = (double) region_start;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static int _dm_stats_region_len_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ uint64_t region_length;
+ const char *repstr;
+ double *sortval;
+ char units = _disp_units;
+ uint64_t factor = _disp_factor;
+
+ if (!dm_stats_get_current_region_len(dms, ®ion_length))
+ return_0;
+
+ if (!(repstr = dm_size_to_string(mem, region_length, units, 1, factor,
+ _show_units(), DM_SIZE_UNIT)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = (double) region_length;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static int _dm_stats_area_id_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ uint64_t area_id = dm_stats_get_current_area(dms);
+
+ if (dm_stats_current_object_type(dms) == DM_STATS_OBJECT_TYPE_GROUP)
+ area_id = 0;
+
+ return dm_report_field_uint64(rh, field, &area_id);
+}
+
+static int _dm_stats_area_start_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ uint64_t area_start;
+ const char *repstr;
+ double *sortval;
+ char units = _disp_units;
+ uint64_t factor = _disp_factor;
+
+ if (!dm_stats_get_current_area_start(dms, &area_start))
+ return_0;
+
+ if (!(repstr = dm_size_to_string(mem, area_start, units, 1, factor,
+ _show_units(), DM_SIZE_UNIT)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = (double) area_start;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static int _dm_stats_area_offset_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ uint64_t area_offset;
+ const char *repstr;
+ double *sortval;
+ char units = _disp_units;
+ uint64_t factor = _disp_factor;
+
+ if (!dm_stats_get_current_area_offset(dms, &area_offset))
+ return_0;
+
+ if (!(repstr = dm_size_to_string(mem, area_offset, units, 1, factor,
+ _show_units(), DM_SIZE_UNIT)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = (double) area_offset;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static int _dm_stats_area_len_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ uint64_t area_len;
+ const char *repstr;
+ double *sortval;
+ char units = _disp_units;
+ uint64_t factor = _disp_factor;
+
+ if (!dm_stats_get_current_area_len(dms, &area_len))
+ return_0;
+
+ if (!(repstr = dm_size_to_string(mem, area_len, units, 1, factor,
+ _show_units(), DM_SIZE_UNIT)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = (double) area_len;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static int _dm_stats_area_count_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ uint64_t area_count, region;
+
+ region = dm_stats_get_current_region(dms);
+ if (!(area_count = dm_stats_get_region_nr_areas(dms, region)))
+ return_0;
+
+ return dm_report_field_uint64(rh, field, &area_count);
+}
+
+static int _dm_stats_group_id_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ uint64_t group_id;
+
+ group_id = dm_stats_get_group_id(dms,
+ dm_stats_get_current_region(dms));
+
+ if (!dm_stats_group_present(dms, group_id)) {
+ dm_report_field_set_value(field, "-", &group_id);
+ return 1;
+ }
+
+ return dm_report_field_uint64(rh, field, &group_id);
+}
+
+static int _dm_stats_program_id_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ const char *program_id;
+ if (!(program_id = dm_stats_get_current_region_program_id(dms)))
+ return_0;
+ return dm_report_field_string(rh, field, (const char * const *) &program_id);
+}
+
+static int _dm_stats_user_data_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ const char *user_data;
+ if (!(user_data = dm_stats_get_current_region_aux_data(dms)))
+ return_0;
+ return dm_report_field_string(rh, field, (const char * const *) &user_data);
+}
+
+static int _dm_stats_name_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ const char *stats_name;
+ if (!(stats_name = dm_stats_get_alias(dms, DM_STATS_REGION_CURRENT)))
+ return_0;
+
+ return dm_report_field_string(rh, field, (const char * const *) &stats_name);
+}
+
+static int _dm_stats_object_type_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ int type = dm_stats_current_object_type(dms);
+
+ return dm_report_field_string(rh, field, (const char * const *) &_stats_types[type]);
+}
+
+static int _dm_stats_precise_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ int precise;
+ precise = dm_stats_get_current_region_precise_timestamps(dms);
+ return dm_report_field_int(rh, field, (const int *) &precise);
+}
+
+static const char *_get_histogram_string(const struct dm_stats *dms, int rel,
+ int vals, int bounds)
+{
+ const struct dm_histogram *dmh;
+ int flags = 0, width = (_switches[NOHEADINGS_ARG]) ? -1 : 0;
+
+ if (!(dmh = dm_stats_get_histogram(dms, DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT)))
+ return ""; /* No histogram. */
+
+ flags |= (vals) ? DM_HISTOGRAM_VALUES
+ : 0;
+
+ flags |= bounds;
+
+ flags |= (rel) ? DM_HISTOGRAM_PERCENT
+ : 0;
+
+ flags |= (_switches[NOTIMESUFFIX_ARG]) ? 0 : DM_HISTOGRAM_SUFFIX;
+
+ /* FIXME: make unit conversion optional. */
+ return dm_histogram_to_string(dmh, -1, width, flags);
+}
+
+static int _stats_hist_count_disp(struct dm_report *rh,
+ struct dm_report_field *field, const void *data,
+ int bounds)
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ const char *histogram;
+
+ histogram = _get_histogram_string(dms, 0, 1, bounds); /* counts */
+
+ if (!histogram)
+ return_0;
+
+ return dm_report_field_string(rh, field, (const char * const *) &histogram);
+}
+
+static int _dm_stats_hist_count_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ return _stats_hist_count_disp(rh, field, data, 0);
+}
+
+static int _dm_stats_hist_count_bounds_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ return _stats_hist_count_disp(rh, field, data, DM_HISTOGRAM_BOUNDS_LOWER);
+}
+
+static int _dm_stats_hist_count_ranges_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ return _stats_hist_count_disp(rh, field, data, DM_HISTOGRAM_BOUNDS_RANGE);
+}
+
+static int _stats_hist_percent_disp(struct dm_report *rh,
+ struct dm_report_field *field, const void *data,
+ int bounds)
+{
+
+ /* FIXME: configurable to-string options. */
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ const char *histogram;
+
+ histogram = _get_histogram_string(dms, 1, 1, bounds); /* relative values */
+
+ if (!histogram)
+ return_0;
+
+ return dm_report_field_string(rh, field, (const char * const *) &histogram);
+}
+
+static int _dm_stats_hist_percent_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ return _stats_hist_percent_disp(rh, field, data, 0);
+}
+
+static int _dm_stats_hist_percent_bounds_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ return _stats_hist_percent_disp(rh, field, data, DM_HISTOGRAM_BOUNDS_LOWER);
+}
+
+static int _dm_stats_hist_percent_ranges_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ return _stats_hist_percent_disp(rh, field, data, DM_HISTOGRAM_BOUNDS_RANGE);
+}
+
+static int _stats_hist_bounds_disp(struct dm_report *rh,
+ struct dm_report_field *field, const void *data,
+ int bounds)
+{
+ /* FIXME: configurable to-string options. */
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ const char *histogram;
+
+ histogram = _get_histogram_string(dms, 0, 0, bounds);
+
+ if (!histogram)
+ return_0;
+
+ return dm_report_field_string(rh, field, (const char * const *) &histogram);
+}
+
+static int _dm_stats_hist_bounds_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ return _stats_hist_bounds_disp(rh, field, data, DM_HISTOGRAM_BOUNDS_LOWER);
+}
+
+static int _dm_stats_hist_ranges_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ return _stats_hist_bounds_disp(rh, field, data, DM_HISTOGRAM_BOUNDS_RANGE);
+}
+
+static int _dm_stats_hist_bins_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ int bins;
+ bins = dm_stats_get_region_nr_histogram_bins(dms, DM_STATS_REGION_CURRENT);
+ return dm_report_field_int(rh, field, (const int *) &bins);
+}
+
+static int _dm_stats_rrqm_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ char buf[64];
+ char *repstr;
+ double *sortval, rrqm;
+
+ if (!dm_stats_get_rd_merges_per_sec(dms, &rrqm,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+ if (dm_snprintf(buf, sizeof(buf), "%.2f", rrqm) < 0)
+ return_0;
+
+ if (!(repstr = dm_pool_strdup(mem, buf)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = rrqm;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+
+}
+
+static int _dm_stats_wrqm_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ char buf[64];
+ char *repstr;
+ double *sortval, wrqm;
+
+ if (!dm_stats_get_wr_merges_per_sec(dms, &wrqm,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+ if (dm_snprintf(buf, sizeof(buf), "%.2f", wrqm) < 0)
+ return_0;
+
+ if (!(repstr = dm_pool_strdup(mem, buf)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = wrqm;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+
+}
+
+static int _dm_stats_rs_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ char buf[64];
+ char *repstr;
+ double *sortval, rs;
+
+ if (!dm_stats_get_reads_per_sec(dms, &rs,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+ if (dm_snprintf(buf, sizeof(buf), "%.2f", rs) < 0)
+ return_0;
+
+ if (!(repstr = dm_pool_strdup(mem, buf)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = rs;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+
+}
+
+static int _dm_stats_ws_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ char buf[64];
+ char *repstr;
+ double *sortval, ws;
+
+ if (!dm_stats_get_writes_per_sec(dms, &ws,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+ if (dm_snprintf(buf, sizeof(buf), "%.2f", ws) < 0)
+ return_0;
+
+ if (!(repstr = dm_pool_strdup(mem, buf)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = ws;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+
+}
+
+static int _dm_stats_read_secs_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ const char *repstr;
+ double *sortval, rsec;
+ char units = _disp_units;
+ uint64_t factor = _disp_factor;
+
+ if (!dm_stats_get_read_sectors_per_sec(dms, &rsec,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+ if (!(repstr = dm_size_to_string(mem, (uint64_t) rsec, units, 1,
+ factor, _show_units(), DM_SIZE_UNIT)))
+
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = rsec;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static int _dm_stats_write_secs_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ const char *repstr;
+ double *sortval, wsec;
+ char units = _disp_units;
+ uint64_t factor = _disp_factor;
+
+ if (!dm_stats_get_write_sectors_per_sec(dms, &wsec,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+ if (!(repstr = dm_size_to_string(mem, (uint64_t) wsec, units, 1,
+ factor, _show_units(), DM_SIZE_UNIT)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = wsec;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static int _dm_stats_arqsz_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ const char *repstr;
+ double *sortval, arqsz;
+ char units = _disp_units;
+ uint64_t factor = _disp_factor;
+
+ if (!dm_stats_get_average_request_size(dms, &arqsz,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+
+ if (!(repstr = dm_size_to_string(mem, (uint64_t) arqsz, units, 1,
+ factor, _show_units(), DM_SIZE_UNIT)))
+
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = arqsz;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static int _dm_stats_qusz_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ char buf[64];
+ char *repstr;
+ double *sortval, qusz;
+
+ if (!dm_stats_get_average_queue_size(dms, &qusz,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+ if (dm_snprintf(buf, sizeof(buf), "%.2f", qusz) < 0)
+ return_0;
+
+ if (!(repstr = dm_pool_strdup(mem, buf)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = qusz;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static int _dm_stats_await_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ char buf[64];
+ char *repstr;
+ double *sortval, await;
+
+ if (!dm_stats_get_average_wait_time(dms, &await,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+ /* FIXME: make scale configurable */
+ /* display in msecs */
+ await /= NSEC_PER_MSEC;
+
+ if (dm_snprintf(buf, sizeof(buf), "%.2f", await) < 0)
+ return_0;
+
+ if (!(repstr = dm_pool_strdup(mem, buf)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = await;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static int _dm_stats_r_await_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ char buf[64];
+ char *repstr;
+ double *sortval, r_await;
+
+ if (!dm_stats_get_average_rd_wait_time(dms, &r_await,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+ /* FIXME: make scale configurable */
+ /* display in msecs */
+ r_await /= NSEC_PER_MSEC;
+
+ if (dm_snprintf(buf, sizeof(buf), "%.2f", r_await) < 0)
+ return_0;
+
+ if (!(repstr = dm_pool_strdup(mem, buf)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = r_await;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static int _dm_stats_w_await_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ char buf[64];
+ char *repstr;
+ double *sortval, w_await;
+
+ if (!dm_stats_get_average_wr_wait_time(dms, &w_await,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+ /* FIXME: make scale configurable */
+ /* display in msecs */
+ w_await /= NSEC_PER_MSEC;
+
+ if (dm_snprintf(buf, sizeof(buf), "%.2f", w_await) < 0)
+ return_0;
+
+ if (!(repstr = dm_pool_strdup(mem, buf)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = w_await;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static int _dm_stats_tput_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ char buf[64];
+ char *repstr;
+ double *sortval, tput;
+
+ if (!dm_stats_get_throughput(dms, &tput,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+ if (dm_snprintf(buf, sizeof(buf), "%.2f", tput) < 0)
+ return_0;
+
+ if (!(repstr = dm_pool_strdup(mem, buf)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = tput;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static int _dm_stats_svctm_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ char buf[64];
+ char *repstr;
+ double *sortval, svctm;
+
+ if (!dm_stats_get_service_time(dms, &svctm,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+ /* FIXME: make scale configurable */
+ /* display in msecs */
+ svctm /= NSEC_PER_MSEC;
+
+ if (dm_snprintf(buf, sizeof(buf), "%.2f", svctm) < 0)
+ return_0;
+
+ if (!(repstr = dm_pool_strdup(mem, buf)))
+ return_0;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
+ return_0;
+
+ *sortval = svctm;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+
+}
+
+static int _dm_stats_util_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ const struct dm_stats *dms = (const struct dm_stats *) data;
+ dm_percent_t util;
+
+ if (!dm_stats_get_utilization(dms, &util,
+ DM_STATS_REGION_CURRENT,
+ DM_STATS_AREA_CURRENT))
+ return_0;
+
+ dm_report_field_percent(rh, field, &util);
+ return 1;
+}
+
+static int _dm_stats_sample_interval_ns_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ /* FIXME: use internal interval estimate when supported by libdm */
+ return dm_report_field_uint64(rh, field, &_last_interval);
+}
+
+static int _dm_stats_sample_interval_disp(struct dm_report *rh,
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field, const void *data,
+ void *private __attribute__((unused)))
+{
+ char buf[64];
+ char *repstr;
+ double *sortval;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(*sortval))))
+ return_0;
+
+ *sortval = (double)_last_interval / (double) NSEC_PER_SEC;
+
+ if (dm_snprintf(buf, sizeof(buf), "%2.6f", *sortval) < 0)
+ return_0;
+
+ if (!(repstr = dm_pool_strdup(mem, buf)))
+ return_0;
+
+ dm_report_field_set_value(field, repstr, sortval);
+ return 1;
+}
+
+static void *_task_get_obj(void *obj)
+{
+ return ((struct dmsetup_report_obj *)obj)->task;
+}
+
+static void *_info_get_obj(void *obj)
+{
+ return ((struct dmsetup_report_obj *)obj)->info;
+}
+
+static void *_deps_get_obj(void *obj)
+{
+ return dm_task_get_deps(((struct dmsetup_report_obj *)obj)->deps_task);
+}
+
+static void *_tree_get_obj(void *obj)
+{
+ return ((struct dmsetup_report_obj *)obj)->tree_node;
+}
+
+static void *_split_name_get_obj(void *obj)
+{
+ return ((struct dmsetup_report_obj *)obj)->split_name;
+}
+
+static void *_stats_get_obj(void *obj)
+{
+ return ((struct dmsetup_report_obj *)obj)->stats;
+}
+
+static const struct dm_report_object_type _report_types[] = {
+ { DR_TASK, "Mapped Device Name", "name_", _task_get_obj },
+ { DR_INFO, "Mapped Device Information", "info_", _info_get_obj },
+ { DR_DEPS, "Mapped Device Relationship Information", "deps_", _deps_get_obj },
+ { DR_TREE, "Mapped Device Relationship Information", "tree_", _tree_get_obj },
+ { DR_NAME, "Mapped Device Name Components", "splitname_", _split_name_get_obj },
+ { DR_STATS, "Mapped Device Statistics","stats_", _stats_get_obj },
+ { DR_STATS_META, "Mapped Device Statistics Region Information","region_", _stats_get_obj },
+ { 0, "", "", NULL }
+};
+
+/* Column definitions */
+/* N.B. Field names must not contain the substring 'help' as this will disable --count. */
+#define OFFSET_OF(strct, field) (((char*)&((struct strct*)0)->field) - (char*)0)
+#define STR (DM_REPORT_FIELD_TYPE_STRING)
+#define NUM (DM_REPORT_FIELD_TYPE_NUMBER)
+#define SIZ (DM_REPORT_FIELD_TYPE_SIZE)
+#define TIM (DM_REPORT_FIELD_TYPE_TIME)
+#define FIELD_O(type, strct, sorttype, head, field, width, func, id, desc) {DR_ ## type, sorttype, OFFSET_OF(strct, field), width, id, head, &_ ## func ## _disp, desc},
+#define FIELD_F(type, sorttype, head, width, func, id, desc) {DR_ ## type, sorttype, 0, width, id, head, &_ ## func ## _disp, desc},
+
+static const struct dm_report_field_type _report_fields[] = {
+/* *INDENT-OFF* */
+FIELD_F(TASK, STR, "Name", 16, dm_name, "name", "Name of mapped device.")
+FIELD_F(TASK, STR, "MangledName", 16, dm_mangled_name, "mangled_name", "Mangled name of mapped device.")
+FIELD_F(TASK, STR, "UnmangledName", 16, dm_unmangled_name, "unmangled_name", "Unmangled name of mapped device.")
+FIELD_F(TASK, STR, "UUID", 32, dm_uuid, "uuid", "Unique (optional) identifier for mapped device.")
+FIELD_F(TASK, STR, "MangledUUID", 32, dm_mangled_uuid, "mangled_uuid", "Mangled unique (optional) identifier for mapped device.")
+FIELD_F(TASK, STR, "UnmangledUUID", 32, dm_unmangled_uuid, "unmangled_uuid", "Unmangled unique (optional) identifier for mapped device.")
+
+/* FIXME Next one should be INFO */
+FIELD_F(TASK, NUM, "RAhead", 6, dm_read_ahead, "read_ahead", "Read ahead value.")
+
+FIELD_F(INFO, STR, "BlkDevName", 16, dm_blk_name, "blkdevname", "Name of block device.")
+FIELD_F(INFO, STR, "Stat", 4, dm_info_status, "attr", "(L)ive, (I)nactive, (s)uspended, (r)ead-only, read-(w)rite.")
+FIELD_F(INFO, STR, "Tables", 6, dm_info_table_loaded, "tables_loaded", "Which of the live and inactive table slots are filled.")
+FIELD_F(INFO, STR, "Suspended", 9, dm_info_suspended, "suspended", "Whether the device is suspended.")
+FIELD_F(INFO, STR, "Read-only", 9, dm_info_read_only, "readonly", "Whether the device is read-only or writeable.")
+FIELD_F(INFO, STR, "DevNo", 5, dm_info_devno, "devno", "Device major and minor numbers")
+FIELD_O(INFO, dm_info, NUM, "Maj", major, 3, int32, "major", "Block device major number.")
+FIELD_O(INFO, dm_info, NUM, "Min", minor, 3, int32, "minor", "Block device minor number.")
+FIELD_O(INFO, dm_info, NUM, "Open", open_count, 4, int32, "open", "Number of references to open device, if requested.")
+FIELD_O(INFO, dm_info, NUM, "Targ", target_count, 4, int32, "segments", "Number of segments in live table, if present.")
+FIELD_O(INFO, dm_info, NUM, "Event", event_nr, 6, uint32, "events", "Number of most recent event.")
+
+FIELD_O(DEPS, dm_deps, NUM, "#Devs", count, 5, int32, "device_count", "Number of devices used by this one.")
+FIELD_F(TREE, STR, "DevNamesUsed", 16, dm_deps_names, "devs_used", "List of names of mapped devices used by this one.")
+FIELD_F(DEPS, STR, "DevNosUsed", 16, dm_deps, "devnos_used", "List of device numbers of devices used by this one.")
+FIELD_F(DEPS, STR, "BlkDevNamesUsed", 16, dm_deps_blk_names, "blkdevs_used", "List of names of block devices used by this one.")
+
+FIELD_F(TREE, NUM, "#Refs", 5, dm_tree_parents_count, "device_ref_count", "Number of mapped devices referencing this one.")
+FIELD_F(TREE, STR, "RefNames", 8, dm_tree_parents_names, "names_using_dev", "List of names of mapped devices using this one.")
+FIELD_F(TREE, STR, "RefDevNos", 9, dm_tree_parents_devs, "devnos_using_dev", "List of device numbers of mapped devices using this one.")
+
+FIELD_O(NAME, dm_split_name, STR, "Subsys", subsystem, 6, dm_subsystem, "subsystem", "Userspace subsystem responsible for this device.")
+FIELD_O(NAME, dm_split_name, STR, "VG", vg_name, 4, dm_vg_name, "vg_name", "LVM Volume Group name.")
+FIELD_O(NAME, dm_split_name, STR, "LV", lv_name, 4, dm_lv_name, "lv_name", "LVM Logical Volume name.")
+FIELD_O(NAME, dm_split_name, STR, "LVLayer", lv_layer, 7, dm_lv_layer_name, "lv_layer", "LVM device layer.")
+
+/* basic stats counters */
+FIELD_F(STATS, NUM, "#Reads", 6, dm_stats_reads, "read_count", "Count of reads completed.")
+FIELD_F(STATS, NUM, "#RdMrgs", 7, dm_stats_reads_merged, "reads_merged_count", "Count of read requests merged.")
+FIELD_F(STATS, NUM, "#RdSectors", 10, dm_stats_read_sectors, "read_sector_count", "Count of sectors read.")
+FIELD_F(STATS, NUM, "AccRdTime", 11, dm_stats_read_nsecs, "read_time", "Accumulated duration of all read requests (ns).")
+FIELD_F(STATS, NUM, "#Writes", 7, dm_stats_writes, "write_count", "Count of writes completed.")
+FIELD_F(STATS, NUM, "#WrMrgs", 7, dm_stats_writes_merged, "writes_merged_count", "Count of write requests merged.")
+FIELD_F(STATS, NUM, "#WrSectors", 10, dm_stats_write_sectors, "write_sector_count", "Count of sectors written.")
+FIELD_F(STATS, NUM, "AccWrTime", 11, dm_stats_write_nsecs, "write_time", "Accumulated duration of all writes (ns).")
+FIELD_F(STATS, NUM, "#InProg", 7, dm_stats_io_in_progress, "in_progress_count", "Count of requests currently in progress.")
+FIELD_F(STATS, NUM, "IoTicks", 7, dm_stats_io_nsecs, "io_ticks", "Nanoseconds spent servicing requests.")
+FIELD_F(STATS, NUM, "QueueTicks", 10, dm_stats_weighted_io_nsecs, "queue_ticks", "Total nanoseconds spent in queue.")
+FIELD_F(STATS, NUM, "RdTicks", 7, dm_stats_total_read_nsecs, "read_ticks", "Nanoseconds spent servicing reads.")
+FIELD_F(STATS, NUM, "WrTicks", 7, dm_stats_total_write_nsecs, "write_ticks", "Nanoseconds spent servicing writes.")
+
+/* Stats derived metrics */
+FIELD_F(STATS, NUM, "RMrg/s", 6, dm_stats_rrqm, "reads_merged_per_sec", "Read requests merged per second.")
+FIELD_F(STATS, NUM, "WMrg/s", 6, dm_stats_wrqm, "writes_merged_per_sec", "Write requests merged per second.")
+FIELD_F(STATS, NUM, "R/s", 3, dm_stats_rs, "reads_per_sec", "Reads per second.")
+FIELD_F(STATS, NUM, "W/s", 3, dm_stats_ws, "writes_per_sec", "Writes per second.")
+FIELD_F(STATS, NUM, "RSz/s", 5, dm_stats_read_secs, "read_size_per_sec", "Size of data read per second.")
+FIELD_F(STATS, NUM, "WSz/s", 5, dm_stats_write_secs, "write_size_per_sec", "Size of data written per second.")
+FIELD_F(STATS, NUM, "AvgRqSz", 7, dm_stats_arqsz, "avg_request_size", "Average request size.")
+FIELD_F(STATS, NUM, "QSize", 5, dm_stats_qusz, "queue_size", "Average queue size.")
+FIELD_F(STATS, NUM, "AWait", 5, dm_stats_await, "await", "Averate wait time.")
+FIELD_F(STATS, NUM, "RdAWait", 7, dm_stats_r_await, "read_await", "Averate read wait time.")
+FIELD_F(STATS, NUM, "WrAWait", 7, dm_stats_w_await, "write_await", "Averate write wait time.")
+FIELD_F(STATS, NUM, "Throughput", 10, dm_stats_tput, "throughput", "Throughput.")
+FIELD_F(STATS, NUM, "SvcTm", 5, dm_stats_svctm, "service_time", "Service time.")
+FIELD_F(STATS, NUM, "Util%", 5, dm_stats_util, "util", "Utilization.")
+
+/* Histogram fields */
+FIELD_F(STATS, STR, "Histogram Counts", 16, dm_stats_hist_count, "hist_count", "Latency histogram counts.")
+FIELD_F(STATS, STR, "Histogram Counts", 16, dm_stats_hist_count_bounds, "hist_count_bounds", "Latency histogram counts with bin boundaries.")
+FIELD_F(STATS, STR, "Histogram Counts", 16, dm_stats_hist_count_ranges, "hist_count_ranges", "Latency histogram counts with bin ranges.")
+FIELD_F(STATS, STR, "Histogram%", 10, dm_stats_hist_percent, "hist_percent", "Relative latency histogram.")
+FIELD_F(STATS, STR, "Histogram%", 10, dm_stats_hist_percent_bounds, "hist_percent_bounds", "Relative latency histogram with bin boundaries.")
+FIELD_F(STATS, STR, "Histogram%", 10, dm_stats_hist_percent_ranges, "hist_percent_ranges", "Relative latency histogram with bin ranges.")
+
+/* Stats interval duration estimates */
+FIELD_F(STATS, NUM, "IntervalNs", 10, dm_stats_sample_interval_ns, "interval_ns", "Sampling interval in nanoseconds.")
+FIELD_F(STATS, NUM, "Interval", 8, dm_stats_sample_interval, "interval", "Sampling interval.")
+
+/* Stats report meta-fields */
+FIELD_F(STATS_META, NUM, "RgID", 4, dm_stats_region_id, "region_id", "Region ID.")
+FIELD_F(STATS_META, SIZ, "RgStart", 7, dm_stats_region_start, "region_start", "Region start.")
+FIELD_F(STATS_META, SIZ, "RgSize", 6, dm_stats_region_len, "region_len", "Region length.")
+FIELD_F(STATS_META, NUM, "ArID", 4, dm_stats_area_id, "area_id", "Area ID.")
+FIELD_F(STATS_META, SIZ, "ArStart", 7, dm_stats_area_start, "area_start", "Area offset from start of device.")
+FIELD_F(STATS_META, SIZ, "ArSize", 6, dm_stats_area_len, "area_len", "Area length.")
+FIELD_F(STATS_META, SIZ, "ArOff", 5, dm_stats_area_offset, "area_offset", "Area offset from start of region.")
+FIELD_F(STATS_META, NUM, "#Areas", 6, dm_stats_area_count, "area_count", "Area count.")
+FIELD_F(STATS_META, NUM, "GrpID", 5, dm_stats_group_id, "group_id", "Group ID.")
+FIELD_F(STATS_META, STR, "ProgID", 6, dm_stats_program_id, "program_id", "Program ID.")
+FIELD_F(STATS_META, STR, "UserData", 8, dm_stats_user_data, "user_data", "Auxiliary data.")
+FIELD_F(STATS_META, STR, "Precise", 7, dm_stats_precise, "precise", "Set if nanosecond precision counters are enabled.")
+FIELD_F(STATS_META, STR, "#Bins", 9, dm_stats_hist_bins, "hist_bins", "The number of histogram bins configured.")
+FIELD_F(STATS_META, STR, "Histogram Bounds", 16, dm_stats_hist_bounds, "hist_bounds", "Latency histogram bin boundaries.")
+FIELD_F(STATS_META, STR, "Histogram Ranges", 16, dm_stats_hist_ranges, "hist_ranges", "Latency histogram bin ranges.")
+FIELD_F(STATS_META, STR, "Name", 16, dm_stats_name, "stats_name", "Stats name of current object.")
+FIELD_F(STATS_META, STR, "ObjType", 7, dm_stats_object_type, "obj_type", "Type of stats object being reported.")
+{0, 0, 0, 0, "", "", NULL, NULL},
+/* *INDENT-ON* */
+};
+
+#undef FIELD_O
+#undef FIELD_F
+
+#undef STR
+#undef NUM
+#undef SIZ
+
+static const char *_default_report_options = "name,major,minor,attr,open,segments,events,uuid";
+static const char *_splitname_report_options = "vg_name,lv_name,lv_layer";
+
+/* Stats counters & derived metrics. */
+#define RD_COUNTERS "read_count,reads_merged_count,read_sector_count,read_time,read_ticks"
+#define WR_COUNTERS "write_count,writes_merged_count,write_sector_count,write_time,write_ticks"
+#define IO_COUNTERS "in_progress_count,io_ticks,queue_ticks"
+#define COUNTERS RD_COUNTERS "," WR_COUNTERS "," IO_COUNTERS
+
+#define METRICS "reads_merged_per_sec,writes_merged_per_sec," \
+ "reads_per_sec,writes_per_sec," \
+ "read_size_per_sec,write_size_per_sec," \
+ "avg_request_size,queue_size,util," \
+ "await,read_await,write_await"
+
+/* Device, region and area metadata. */
+#define STATS_DEV_INFO "statsname,group_id,region_id,obj_type"
+#define STATS_AREA_INFO "area_id,area_start,area_len"
+#define STATS_AREA_INFO_FULL STATS_DEV_INFO ",region_start,region_len,area_count,area_id,area_start,area_len"
+#define STATS_REGION_INFO STATS_DEV_INFO ",region_start,region_len,area_count,area_len"
+
+/* Minimal set of fields for histogram report. */
+#define STATS_HIST STATS_REGION_INFO ",util,await"
+
+/* Default stats report options. */
+static const char *_stats_default_report_options = STATS_DEV_INFO "," STATS_AREA_INFO "," METRICS;
+static const char *_stats_raw_report_options = STATS_DEV_INFO "," STATS_AREA_INFO "," COUNTERS;
+static const char *_stats_list_options = STATS_REGION_INFO ",program_id";
+static const char *_stats_area_list_options = STATS_AREA_INFO_FULL ",program_id";
+static const char *_stats_hist_list_options = STATS_REGION_INFO ",hist_bins,hist_bounds";
+static const char *_stats_hist_area_list_options = STATS_AREA_INFO_FULL ",hist_bins,hist_bounds";
+static const char *_stats_hist_options = STATS_HIST ",hist_count_bounds";
+static const char *_stats_hist_relative_options = STATS_HIST ",hist_percent_bounds";
+
+static int _report_init(const struct command *cmd, const char *subcommand)
+{
+ char *options = (char *) _default_report_options;
+ char *opt_fields = NULL; /* optional fields from command line */
+ const char *keys = "";
+ const char *separator = " ";
+ const char *selection = NULL;
+ int aligned = 1, headings = 1, buffered = 1, field_prefixes = 0;
+ int quoted = 1, columns_as_rows = 0;
+ uint32_t flags = 0;
+ size_t len = 0;
+ int r = 0;
+
+ if (cmd && !strcmp(cmd->name, "splitname")) {
+ options = (char *) _splitname_report_options;
+ _report_type |= DR_NAME;
+ }
+
+ if (cmd && !strcmp(cmd->name, "stats")) {
+ _report_type |= DR_STATS_META;
+ if (!strcmp(subcommand, "list")) {
+ if (!_switches[HISTOGRAM_ARG])
+ options = (char *) ((_switches[VERBOSE_ARG])
+ ? _stats_area_list_options
+ : _stats_list_options);
+ else
+ options = (char *) ((_switches[VERBOSE_ARG])
+ ? _stats_hist_area_list_options
+ : _stats_hist_list_options);
+ } else {
+ if (_switches[HISTOGRAM_ARG])
+ options = (char *) ((_switches[RELATIVE_ARG])
+ ? _stats_hist_relative_options
+ : _stats_hist_options);
+ else
+ options = (char *) ((!_switches[RAW_ARG])
+ ? _stats_default_report_options
+ : _stats_raw_report_options);
+ _report_type |= DR_STATS;
+ }
+ }
+
+ if (cmd && !strcmp(cmd->name, "list")) {
+ options = (char *) _stats_list_options;
+ _report_type |= DR_STATS_META;
+ }
+
+ /* emulate old dmsetup behaviour */
+ if (_switches[NOHEADINGS_ARG]) {
+ separator = ":";
+ aligned = 0;
+ headings = 0;
+ }
+
+ if (_switches[UNBUFFERED_ARG])
+ buffered = 0;
+
+ if (_switches[ROWS_ARG])
+ columns_as_rows = 1;
+
+ if (_switches[UNQUOTED_ARG])
+ quoted = 0;
+
+ if (_switches[NAMEPREFIXES_ARG]) {
+ aligned = 0;
+ field_prefixes = 1;
+ }
+
+ if (_switches[OPTIONS_ARG] && _string_args[OPTIONS_ARG]) {
+ /* Count & interval forbidden for help. */
+ /* FIXME Detect "help" correctly and exit */
+ if (strstr(_string_args[OPTIONS_ARG], "help")) {
+ _switches[COUNT_ARG] = 0;
+ _count = 1;
+ _switches[INTERVAL_ARG] = 0;
+ headings = 0;
+ }
+
+ if (*_string_args[OPTIONS_ARG] != '+')
+ options = _string_args[OPTIONS_ARG];
+ else {
+ char *tmpopts;
+ opt_fields = _string_args[OPTIONS_ARG] + 1;
+ len = strlen(options) + strlen(opt_fields) + 2;
+ if (!(tmpopts = malloc(len))) {
+ log_error("Failed to allocate option string.");
+ return 0;
+ }
+ if (dm_snprintf(tmpopts, len, "%s,%s",
+ options, opt_fields) < 0) {
+ free(tmpopts);
+ return 0;
+ }
+ options = tmpopts;
+ }
+ }
+
+ if (_switches[SORT_ARG] && _string_args[SORT_ARG]) {
+ keys = _string_args[SORT_ARG];
+ buffered = 1;
+ if (cmd && (!strcmp(cmd->name, "status") || !strcmp(cmd->name, "table"))) {
+ log_error("--sort is not yet supported with status and table.");
+ goto out;
+ }
+ }
+
+ if (_switches[SEPARATOR_ARG] && _string_args[SEPARATOR_ARG]) {
+ separator = _string_args[SEPARATOR_ARG];
+ aligned = 0;
+ }
+
+ if (_switches[SELECT_ARG] && _string_args[SELECT_ARG])
+ selection = _string_args[SELECT_ARG];
+
+ if (aligned)
+ flags |= DM_REPORT_OUTPUT_ALIGNED;
+
+ if (buffered)
+ flags |= DM_REPORT_OUTPUT_BUFFERED;
+
+ if (headings)
+ flags |= DM_REPORT_OUTPUT_HEADINGS;
+
+ if (field_prefixes)
+ flags |= DM_REPORT_OUTPUT_FIELD_NAME_PREFIX;
+
+ if (!quoted)
+ flags |= DM_REPORT_OUTPUT_FIELD_UNQUOTED;
+
+ if (columns_as_rows)
+ flags |= DM_REPORT_OUTPUT_COLUMNS_AS_ROWS;
+
+ if (!(_report = dm_report_init_with_selection(&_report_type, _report_types,
+ _report_fields, options, separator, flags, keys,
+ selection, NULL, NULL)))
+ goto_out;
+
+ r = 1;
+
+ if ((_report_type & DR_TREE) && cmd) {
+ r = _build_whole_deptree(cmd);
+ if (!_dtree) {
+ log_error("Internal device dependency tree creation failed.");
+ goto out;
+ }
+ }
+
+ if (!_switches[INTERVAL_ARG])
+ _int_args[INTERVAL_ARG] = 1; /* 1s default. */
+
+ _interval = NSEC_PER_SEC * (uint64_t) _int_args[INTERVAL_ARG];
+
+ if (field_prefixes)
+ dm_report_set_output_field_name_prefix(_report, "dm_");
+
+out:
+ if (len)
+ free(options);
+
+ return r;
+}
+
+/*
+ * List devices
+ */
+static int _ls(CMD_ARGS)
+{
+ if ((_switches[TARGET_ARG] && _target) ||
+ (_switches[EXEC_ARG] && _command_to_exec))
+ return _status(cmd, NULL, argc, argv, NULL, 0);
+
+ if ((_switches[TREE_ARG]))
+ return _display_tree(cmd, NULL, 0, NULL, NULL, 0);
+
+ return _process_all(cmd, NULL, argc, argv, 0, _display_name);
+}
+
+static int _mangle(CMD_ARGS)
+{
+ const char *name, *uuid;
+ char *new_name = NULL, *new_uuid = NULL;
+ struct dm_task *dmt;
+ struct dm_info info;
+ int r = 0;
+ int target_format;
+
+ if (names)
+ name = names->name;
+ else {
+ if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
+ return _process_all(cmd, NULL, argc, argv, 0, _mangle);
+ name = argv[0];
+ }
+
+ if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
+ return_0;
+
+ if (!(_set_task_device(dmt, name, 0)))
+ goto_out;
+
+ if (!_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ if (!_task_run(dmt))
+ goto_out;
+
+ if (!dm_task_get_info(dmt, &info) || !info.exists)
+ goto_out;
+
+ uuid = dm_task_get_uuid(dmt);
+
+ target_format = _switches[MANGLENAME_ARG] ? _int_args[MANGLENAME_ARG]
+ : DEFAULT_DM_NAME_MANGLING;
+
+ if (target_format == DM_STRING_MANGLING_AUTO) {
+ if (strstr(name, "\\x5cx")) {
+ log_error("The name \"%s\" seems to be mangled more than once. "
+ "Manual intervention required to rename the device.", name);
+ goto out;
+ }
+ if (strstr(uuid, "\\x5cx")) {
+ log_error("The UUID \"%s\" seems to be mangled more than once. "
+ "Manual intervention required to correct the device UUID.", uuid);
+ goto out;
+ }
+ }
+
+ if (target_format == DM_STRING_MANGLING_NONE) {
+ if (!(new_name = dm_task_get_name_unmangled(dmt)))
+ goto_out;
+ if (!(new_uuid = dm_task_get_uuid_unmangled(dmt)))
+ goto_out;
+ }
+ else {
+ if (!(new_name = dm_task_get_name_mangled(dmt)))
+ goto_out;
+ if (!(new_uuid = dm_task_get_uuid_mangled(dmt)))
+ goto_out;
+ }
+
+ /* We can't rename the UUID, the device must be reactivated manually. */
+ if (strcmp(uuid, new_uuid)) {
+ log_error("%s: %s: UUID in incorrect form. ", name, uuid);
+ log_error("Unable to change device UUID. The device must be deactivated first.");
+ r = 0;
+ goto out;
+ }
+
+ /* Nothing to do if the name is in correct form already. */
+ if (!strcmp(name, new_name)) {
+ log_print("%s: %s: name %salready in correct form", name,
+ *uuid ? uuid : "[no UUID]", *uuid ? "and UUID " : "");
+ r = 1;
+ goto out;
+ }
+ else
+ log_print("%s: renaming to %s", name, new_name);
+
+ /* Rename to correct form of the name. */
+ r = _do_rename(name, new_name, NULL);
+
+out:
+ free(new_name);
+ free(new_uuid);
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _stats(CMD_ARGS);
+static int _bind_stats_device(struct dm_stats *dms, const char *name)
+{
+ if (name && !dm_stats_bind_name(dms, name))
+ return_0;
+ else if (_switches[UUID_ARG] && !dm_stats_bind_uuid(dms, _uuid))
+ return_0;
+ else if (_switches[MAJOR_ARG] && _switches[MINOR_ARG]
+ && !dm_stats_bind_devno(dms, _int_args[MAJOR_ARG],
+ _int_args[MINOR_ARG]))
+ return_0;
+
+ return 1;
+}
+
+static int _stats_clear_one_region(struct dm_stats *dms, uint64_t region_id)
+{
+
+ if (!dm_stats_region_present(dms, region_id)) {
+ log_error("No such region: %"PRIu64".", region_id);
+ return 0;
+ }
+ if (!dm_stats_clear_region(dms, region_id)) {
+ log_error("Clearing statistics region %"PRIu64" failed.",
+ region_id);
+ return 0;
+ }
+ log_info("Cleared statistics region %"PRIu64".", region_id);
+ return 1;
+}
+
+static int _stats_clear_regions(struct dm_stats *dms, uint64_t region_id)
+{
+ int allregions = (region_id == DM_STATS_REGIONS_ALL);
+
+ if (!dm_stats_list(dms, NULL))
+ return_0;
+
+ if (!dm_stats_get_nr_regions(dms))
+ return 1;
+
+ if (!allregions)
+ return _stats_clear_one_region(dms, region_id);
+
+ dm_stats_foreach_region(dms) {
+ region_id = dm_stats_get_current_region(dms);
+ if (!_stats_clear_one_region(dms, region_id))
+ return_0;
+ }
+
+ return 1;
+}
+
+static int _stats_clear(CMD_ARGS)
+{
+ struct dm_stats *dms;
+ uint64_t region_id;
+ char *name = NULL;
+ int allregions = _switches[ALL_REGIONS_ARG];
+
+ /* clear does not use a report */
+ if (_report) {
+ dm_report_free(_report);
+ _report = NULL;
+ }
+
+ if (!_switches[REGION_ID_ARG] && !_switches[ALL_REGIONS_ARG]) {
+ log_error("Please specify a --regionid or use --allregions.");
+ return 0;
+ }
+
+ if (names)
+ name = names->name;
+ else {
+ if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
+ return _process_all(cmd, subcommand, argc, argv, 0, _stats_clear);
+ name = argv[0];
+ }
+
+ region_id = (allregions) ? DM_STATS_REGIONS_ALL
+ : (uint64_t) _int_args[REGION_ID_ARG];
+
+ if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
+ return_0;
+
+ if (!_bind_stats_device(dms, name))
+ goto_out;
+
+ if (!_stats_clear_regions(dms, region_id))
+ goto_out;
+
+ dm_stats_destroy(dms);
+ return 1;
+
+out:
+ dm_stats_destroy(dms);
+ return 0;
+}
+
+static uint64_t _factor_from_units(char *argptr, char *unit_type)
+{
+ return dm_units_to_factor(argptr, unit_type, 0, NULL);
+}
+
+/**
+ * Parse a start, length, or area size argument in bytes from a string
+ * using optional units as supported by _factor_from_units().
+ */
+static int _size_from_string(char *argptr, uint64_t *size, const char *name)
+{
+ uint64_t factor;
+ char *endptr = NULL, unit_type;
+ if (!argptr)
+ return_0;
+
+ *size = strtoull(argptr, &endptr, 10);
+ if (endptr == argptr) {
+ *size = 0;
+ log_error("Invalid %s argument: \"%s\"",
+ name, (*argptr) ? argptr : "");
+ return 0;
+ }
+
+ if (*endptr == '\0') {
+ *size *= 512;
+ return 1;
+ }
+
+ factor = _factor_from_units(endptr, &unit_type);
+ if (factor)
+ *size *= factor;
+
+ return 1;
+}
+
+/*
+ * FIXME: expose this from libdm-stats
+ */
+static uint64_t _nr_areas_from_step(uint64_t len, int64_t step)
+{
+ /* Default is one area. */
+ if (!step || !len)
+ return 1;
+
+ /* --areas */
+ if (step < 0)
+ return (uint64_t)(-step);
+
+ /* --areasize - cast step to unsigned as it cannot be -ve here. */
+ return (len / step) + !!(len % (uint64_t) step);
+}
+
+/* maximum length of a string representation of an integer */
+#define max_int_strlen(i) (strlen(#i))
+#define MAX_UINT64_STRLEN max_int_strlen(UINT64_MAX)
+static int _stats_group_segments(struct dm_stats *dms, uint64_t *region_ids,
+ int count, const char *alias)
+{
+ /* NULL, commas, and count * region_id */
+ size_t bufsize = 1 + count + count * MAX_UINT64_STRLEN;
+ char *this_region, *regions = NULL;
+ uint64_t group_id;
+ int r, i;
+
+ this_region = regions = malloc(bufsize);
+
+ if (!regions) {
+ log_error("Could not allocate memory for region_id table.");
+ return 0;
+ }
+
+ for (i = 0; i < count; i++) {
+ /*
+ * We don't expect large numbers of segments (compared to e.g.
+ * --filemap): use a fixed-size buffer based on the number of
+ * region identifiers and do not collapse continuous ranges
+ * of identifiers in the group descriptor argument.
+ */
+ r = dm_snprintf(this_region, bufsize, FMTu64 "%s", region_ids[i],
+ (i < (count - 1)) ? "," : "");
+ if (r < 0)
+ goto_bad;
+ this_region += r;
+ bufsize -= r;
+ }
+
+ /* refresh handle */
+ if (!(r = dm_stats_list(dms, NULL)))
+ goto bad;
+
+ if ((r = dm_stats_create_group(dms, regions, alias, &group_id)))
+ printf("Grouped regions %s as group ID " FMTu64 "%s%s\n",
+ regions, group_id, (alias) ? " with alias " : "",
+ (alias) ? : "");
+ else
+ log_error("Failed to create group for regions %s.", regions);
+
+bad:
+ free(regions);
+ return r;
+}
+
+/*
+ * Create a single region starting at start and spanning len sectors,
+ * or, if the segments argument is no-zero create one region for each
+ * segment present in the mapped device. Passing zero for segments,
+ * start, and length will create a single segment spanning the whole
+ * device.
+ */
+static int _do_stats_create_regions(struct dm_stats *dms,
+ const char *name, uint64_t start,
+ uint64_t len, int64_t step,
+ int segments,
+ const char *program_id,
+ const char *user_data)
+{
+ uint64_t this_start = 0, this_len = len, region_id = UINT64_C(0);
+ const char *devname = NULL, *histogram = _string_args[BOUNDS_ARG];
+ int r = 0, count = 0, precise = _switches[PRECISE_ARG];
+ struct dm_histogram *bounds = NULL; /* histogram bounds */
+ uint64_t *region_ids = NULL; /* segments */
+ char *target_type, *params; /* unused */
+ struct dm_task *dmt;
+ struct dm_info info;
+ void *next = NULL;
+
+ if (_switches[ALIAS_ARG] && _switches[NOGROUP_ARG]) {
+ log_error("Cannot set alias with --nogroup.");
+ dm_stats_destroy(dms);
+ return 0;
+ }
+
+ if (histogram && !(bounds = dm_histogram_bounds_from_string(histogram)))
+ return_0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) {
+ dm_histogram_bounds_destroy(bounds);
+ dm_stats_destroy(dms);
+ return_0;
+ }
+
+ if (!_set_task_device(dmt, name, 0))
+ goto_out;
+
+ if (!dm_task_no_open_count(dmt))
+ goto_out;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ if (!_task_run(dmt))
+ goto_out;
+
+ if (!dm_task_get_info(dmt, &info) || !info.exists)
+ goto_out;
+
+ if (!(devname = dm_task_get_name(dmt)))
+ goto_out;
+
+ if (!segments || (info.target_count == 1))
+ region_ids = ®ion_id;
+ else
+ region_ids = malloc(info.target_count * sizeof(*region_ids));
+
+ do {
+ uint64_t segment_start, segment_len;
+ next = dm_get_next_target(dmt, next, &segment_start, &segment_len,
+ &target_type, ¶ms);
+
+ /* Accumulate whole-device size for nr_areas calculation. */
+ if (!segments && !len)
+ this_len += segment_len;
+
+ /* Segments or whole-device. */
+ if (segments || !next) {
+ /*
+ * this_start and this_len hold the start and length in
+ * sectors of the to-be-created region: this is either the
+ * segment start/len (for --segments), the value of the
+ * --start/--length arguments, or 0/0 for a default
+ * whole-device region).
+ */
+ this_start = (segments) ? segment_start : start;
+ this_len = (segments) ? segment_len : this_len;
+ if (!(r = dm_stats_create_region(dms, ®ion_ids[count],
+ this_start, this_len, step,
+ precise, bounds,
+ program_id, user_data))) {
+ log_error("%s: Could not create statistics region.",
+ devname);
+ goto out;
+ }
+
+ printf("%s: Created new region with "FMTu64" area(s) as "
+ "region ID "FMTu64"\n", devname,
+ _nr_areas_from_step(this_len, step),
+ region_ids[count++]);
+ }
+ } while (next);
+
+ if (!_switches[NOGROUP_ARG] && segments)
+ r = _stats_group_segments(dms, region_ids, count,
+ _string_args[ALIAS_ARG]);
+
+out:
+ if (region_ids != ®ion_id)
+ free(region_ids);
+
+ dm_task_destroy(dmt);
+ dm_stats_destroy(dms);
+ dm_histogram_bounds_destroy(bounds);
+ return r;
+}
+
+/*
+ * Returns the full absolute path, or NULL if the path could
+ * not be resolved.
+ */
+static char *_get_abspath(const char *path)
+{
+ char *_path;
+
+#ifdef HAVE_CANONICALIZE_FILE_NAME
+ _path = canonicalize_file_name(path);
+#else
+ /* FIXME Provide alternative */
+ log_error(INTERNAL_ERROR "Unimplemented _get_abspath.");
+ _path = NULL;
+#endif
+ return _path;
+}
+
+static int _stats_check_filemap_switches(void)
+{
+ if (_switches[AREAS_ARG] || _switches[AREA_SIZE_ARG]) {
+ log_error("--filemap is incompatible with --areas and --area-size.");
+ return 0;
+ }
+
+ if (_switches[START_ARG] || _switches[LENGTH_ARG]) {
+ log_error("--filemap is incompatible with --start and --length.");
+ return 0;
+ }
+
+ if (_switches[SEGMENTS_ARG]) {
+ log_error("--filemap and --segments are incompatible.");
+ return 0;
+ }
+
+ if (_switches[USER_DATA_ARG]) {
+ log_error("--userdata is not yet supported with --filemap.");
+ return 0;
+ }
+
+ if (_switches[UUID_ARG] || _switches[MAJOR_ARG]) {
+ log_error("--uuid and --major are incompatible with --filemap.");
+ return 0;
+ }
+
+ if (_switches[ALL_DEVICES_ARG]) {
+ log_error("--alldevices is incompatible with --filemap.");
+ return 0;
+ }
+
+ return 1;
+}
+
+static dm_filemapd_mode_t _stats_get_filemapd_mode(void)
+{
+ if (_switches[NOMONITOR_ARG] || _switches[NOGROUP_ARG])
+ return DM_FILEMAPD_FOLLOW_NONE;
+ if (!_switches[FOLLOW_ARG])
+ return DM_FILEMAPD_FOLLOW_INODE;
+ return dm_filemapd_mode_from_string(_string_args[FOLLOW_ARG]);
+}
+
+static int _stats_create_file(CMD_ARGS)
+{
+ const char *alias, *program_id = DM_STATS_PROGRAM_ID;
+ const char *bounds_str = _string_args[BOUNDS_ARG];
+ int foreground = _switches[FOREGROUND_ARG];
+ int verbose = _switches[VERBOSE_ARG];
+ uint64_t *regions, *region, count = 0;
+ struct dm_histogram *bounds = NULL;
+ char *path, *abspath = NULL;
+ struct dm_stats *dms = NULL;
+ int group, fd = -1, precise;
+ dm_filemapd_mode_t mode;
+
+ if (names) {
+ log_error("Device names are not compatibile with --filemap.");
+ return 0;
+ }
+
+ if (!_stats_check_filemap_switches())
+ return 0;
+
+ /* _stats_create_file does not use _process_all() */
+ if (!argc) {
+ log_error("--filemap requires a file path argument");
+ return 0;
+ }
+
+ path = argv[0];
+
+ if (_switches[PRECISE_ARG]) {
+ if (!dm_stats_driver_supports_precise()) {
+ log_error("Using --precise requires driver version "
+ "4.32.0 or later.");
+ return 0;
+ }
+ }
+
+ if (_switches[BOUNDS_ARG]) {
+ if (!dm_stats_driver_supports_histogram()) {
+ log_error("Using --bounds requires driver version "
+ "4.32.0 or later.");
+ return 0;
+ }
+ }
+
+ if (!(abspath = _get_abspath(path))) {
+ log_error("Could not canonicalize file name: %s", path);
+ return 0;
+ }
+
+ if (bounds_str
+ && !(bounds = dm_histogram_bounds_from_string(bounds_str))) {
+ free(abspath);
+ return_0;
+ }
+
+ if (_switches[PROGRAM_ID_ARG])
+ program_id = _string_args[PROGRAM_ID_ARG];
+ if (!strlen(program_id) && !_switches[FORCE_ARG])
+ program_id = DM_STATS_PROGRAM_ID;
+
+ precise = _int_args[PRECISE_ARG];
+ group = !_switches[NOGROUP_ARG];
+
+ mode = _stats_get_filemapd_mode();
+ if (!_switches[NOMONITOR_ARG] && (mode == DM_FILEMAPD_FOLLOW_NONE))
+ goto bad;
+
+ if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
+ goto_bad;
+
+ fd = open(abspath, O_RDONLY);
+
+ if (fd < 0) {
+ log_error("Could not open %s for reading", abspath);
+ goto bad;
+ }
+
+ if (!dm_stats_bind_from_fd(dms, fd))
+ goto_bad;
+
+ if (!strlen(program_id))
+ /* force creation of a region with no id */
+ dm_stats_set_program_id(dms, 1, NULL);
+
+ if (group && !_switches[ALIAS_ARG])
+ alias = dm_basename(abspath);
+ else if (group)
+ alias = _string_args[ALIAS_ARG];
+ else if (!_switches[ALIAS_ARG])
+ alias = NULL;
+ else {
+ log_error("Cannot set alias with --nogroup.");
+ goto bad;
+ }
+
+ regions = dm_stats_create_regions_from_fd(dms, fd, group, precise,
+ bounds, alias);
+
+ if (!regions) {
+ log_error("Could not create regions from file %s.", abspath);
+ goto bad;
+ }
+
+ if (!_switches[NOMONITOR_ARG] && group) {
+ if (!dm_stats_start_filemapd(fd, regions[0], abspath, mode,
+ foreground, verbose))
+ log_warn("Failed to start filemap monitoring daemon.");
+ }
+
+ if (close(fd))
+ log_sys_debug("close", abspath);
+
+ fd = -1;
+
+ for (region = regions; *region != DM_STATS_REGIONS_ALL; region++)
+ count++;
+
+ if (group) {
+ printf("%s: Created new group with "FMTu64" region(s) as "
+ "group ID "FMTu64".\n", path, count, regions[0]);
+ } else {
+ region = regions;
+ do
+ printf("%s: Created new region with 1 area as "
+ "region ID "FMTu64".\n", path, *region);
+ while (*(++region) != DM_STATS_REGIONS_ALL);
+ }
+
+ free(regions);
+ free(abspath);
+ free(bounds);
+ dm_stats_destroy(dms);
+ return 1;
+
+bad:
+ free(abspath);
+ free(bounds);
+
+ if ((fd > -1) && close(fd))
+ log_sys_debug("close", path);
+
+ if (dms)
+ dm_stats_destroy(dms);
+
+ return 0;
+}
+
+static int _stats_create(CMD_ARGS)
+{
+ struct dm_stats *dms;
+ const char *name, *user_data = "", *program_id = DM_STATS_PROGRAM_ID;
+ uint64_t start = 0, len = 0, areas = 0, area_size = 0;
+ int64_t step = 0;
+
+ /* create does not use a report */
+ if (_report) {
+ dm_report_free(_report);
+ _report = NULL;
+ }
+
+ if (_switches[ALL_REGIONS_ARG]) {
+ log_error("Cannot use --allregions with create.");
+ return 0;
+ }
+
+ if (_switches[ALL_PROGRAMS_ARG]) {
+ log_error("Cannot use --allprograms with create.");
+ return 0;
+ }
+
+ if (_switches[AREAS_ARG] && _switches[AREA_SIZE_ARG]) {
+ log_error("Please specify one of --areas and --areasize.");
+ return 0;
+ }
+
+ if (_switches[PROGRAM_ID_ARG]
+ && !strlen(_string_args[PROGRAM_ID_ARG]) && !_switches[FORCE_ARG]) {
+ log_error("Creating a region with no program "
+ "id requires --force.");
+ return 0;
+ }
+
+ if (_switches[FILEMAP_ARG])
+ return _stats_create_file(cmd, subcommand, argc, argv,
+ names, multiple_devices);
+
+ if (names)
+ name = names->name;
+ else {
+ if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
+ if (!_switches[ALL_DEVICES_ARG]) {
+ log_error("Please specify device(s) or use "
+ "--alldevices.");
+ return 0;
+ }
+ return _process_all(cmd, subcommand, argc, argv, 0, _stats_create);
+ }
+ name = argv[0];
+ }
+
+ if (_switches[AREAS_ARG])
+ areas = (uint64_t) _int_args[AREAS_ARG];
+
+ if (_switches[AREA_SIZE_ARG])
+ if (!_size_from_string(_string_args[AREA_SIZE_ARG],
+ &area_size, "areasize"))
+ return_0;
+
+ areas = (areas) ? areas : 1;
+ /* bytes to sectors or -(areas): promote to signed before conversion */
+ step = (area_size) ? ((int64_t) area_size / 512) : -((int64_t) areas);
+
+ if (_switches[START_ARG]) {
+ if (!_size_from_string(_string_args[START_ARG],
+ &start, "start"))
+ return_0;
+ }
+
+ /* bytes to sectors */
+ start /= 512;
+
+ if (_switches[LENGTH_ARG]) {
+ if (!_size_from_string(_string_args[LENGTH_ARG],
+ &len, "length"))
+ return_0;
+ }
+
+ /* bytes to sectors */
+ len /= 512;
+
+ if (_switches[PROGRAM_ID_ARG])
+ program_id = _string_args[PROGRAM_ID_ARG];
+ if (!strlen(program_id) && !_switches[FORCE_ARG])
+ program_id = DM_STATS_PROGRAM_ID;
+
+ if (_switches[USER_DATA_ARG])
+ user_data = _string_args[USER_DATA_ARG];
+
+ if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
+ return_0;
+
+ if (!_bind_stats_device(dms, name))
+ goto_bad;
+
+ if (_switches[PRECISE_ARG]) {
+ if (!dm_stats_driver_supports_precise()) {
+ log_error("Using --precise requires driver version "
+ "4.32.0 or later.");
+ goto bad;
+ }
+ }
+
+ if (_switches[BOUNDS_ARG]) {
+ if (!dm_stats_driver_supports_histogram()) {
+ log_error("Using --bounds requires driver version "
+ "4.32.0 or later.");
+ goto bad;
+ }
+ }
+
+ if (!strlen(program_id))
+ /* force creation of a region with no id */
+ dm_stats_set_program_id(dms, 1, NULL);
+
+ return _do_stats_create_regions(dms, name, start, len, step,
+ _switches[SEGMENTS_ARG],
+ program_id, user_data);
+
+bad:
+ dm_stats_destroy(dms);
+ return 0;
+}
+
+static int _stats_delete(CMD_ARGS)
+{
+ struct dm_stats *dms;
+ uint64_t region_id, group_id;
+ char *name = NULL;
+ const char *program_id = DM_STATS_PROGRAM_ID;
+ int allregions = _switches[ALL_REGIONS_ARG];
+ int r = 0;
+
+ /* delete does not use a report */
+ if (_report) {
+ dm_report_free(_report);
+ _report = NULL;
+ }
+
+ if (_switches[REGION_ID_ARG] && _switches[GROUP_ID_ARG]) {
+ log_error("Please use one of --regionid and --groupid.");
+ return 0;
+ }
+
+ if (!_switches[REGION_ID_ARG] && !allregions && !_switches[GROUP_ID_ARG]) {
+ log_error("Please specify a --regionid or --groupid, or use --allregions.");
+ return 0;
+ }
+
+ if (names)
+ name = names->name;
+ else {
+ if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
+ if (!_switches[ALL_DEVICES_ARG]) {
+ log_error("Please specify device(s) or use "
+ "--alldevices.");
+ return 0;
+ }
+ return _process_all(cmd, subcommand, argc, argv, 0, _stats_delete);
+ }
+ name = argv[0];
+ }
+
+ if (_switches[PROGRAM_ID_ARG])
+ program_id = _string_args[PROGRAM_ID_ARG];
+ if (_switches[ALL_PROGRAMS_ARG])
+ program_id = DM_STATS_ALL_PROGRAMS;
+
+ region_id = (uint64_t) _int_args[REGION_ID_ARG];
+ group_id = (uint64_t) _int_args[GROUP_ID_ARG];
+
+ if (!(dms = dm_stats_create(program_id)))
+ return_0;
+
+ if (!_bind_stats_device(dms, name))
+ goto_out;
+
+ /* allregions and group delete require a listed handle */
+ if ((allregions || _switches[GROUP_ID_ARG])
+ && !dm_stats_list(dms, program_id))
+ goto_out;
+
+ if (allregions && !dm_stats_get_nr_regions(dms)) {
+ /* no regions present */
+ r = 1;
+ goto out;
+ }
+
+ if (_switches[GROUP_ID_ARG]) {
+ if (!dm_stats_delete_group(dms, group_id, 1)) {
+ log_error("Could not delete statistics group.");
+ goto out;
+ }
+ printf("Deleted statistics group " FMTu64 ".\n", group_id);
+ } else if (_switches[ALL_REGIONS_ARG]) {
+ dm_stats_foreach_region(dms) {
+ region_id = dm_stats_get_current_region(dms);
+ if (!dm_stats_delete_region(dms, region_id)) {
+ log_error("Could not delete statistics region.");
+ goto out;
+ }
+ log_info("Deleted statistics region %" PRIu64, region_id);
+ }
+ } else {
+ if (!dm_stats_delete_region(dms, region_id)) {
+ log_error("Could not delete statistics region");
+ goto out;
+ }
+ log_info("Deleted statistics region " FMTu64 ".", region_id);
+ }
+
+ r = 1;
+
+out:
+ dm_stats_destroy(dms);
+ return r;
+}
+
+static int _stats_print_one_region(struct dm_stats *dms, int clear,
+ uint64_t region_id)
+{
+ char *stbuff = NULL;
+
+ /*FIXME: line control for large regions */
+ if (!(stbuff = dm_stats_print_region(dms, region_id, 0, 0, clear))) {
+ log_error("Could not print statistics region.");
+ return 0;
+ }
+
+ printf("%s", stbuff);
+ dm_stats_buffer_destroy(dms, stbuff);
+
+ return 1;
+}
+
+static int _stats_print(CMD_ARGS)
+{
+ struct dm_stats *dms;
+ char *name, *stbuff = NULL;
+ uint64_t region_id;
+ unsigned clear = (unsigned) _switches[CLEAR_ARG];
+ int allregions = _switches[ALL_REGIONS_ARG];
+ int r = 0;
+
+ /* print does not use a report */
+ if (_report) {
+ dm_report_free(_report);
+ _report = NULL;
+ }
+
+ if (!_switches[REGION_ID_ARG] && !allregions) {
+ log_error("Please specify a --regionid or use --allregions.");
+ return 0;
+ }
+
+ if (names)
+ name = names->name;
+ else {
+ if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
+ return _process_all(cmd, subcommand, argc, argv, 0, _stats_print);
+ name = argv[0];
+ }
+
+ if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
+ return_0;
+
+ if (!_bind_stats_device(dms, name))
+ goto_out;
+
+ if (!dm_stats_list(dms, NULL))
+ goto_out;
+
+ if (allregions && !dm_stats_get_nr_regions(dms)) {
+ r = 1;
+ goto out;
+ }
+
+ if (!allregions) {
+ region_id = (uint64_t) _int_args[REGION_ID_ARG];
+ if (!_stats_print_one_region(dms, clear, region_id))
+ goto_out;
+ r = 1;
+ goto out;
+ }
+
+ dm_stats_foreach_region(dms) {
+ region_id = dm_stats_get_current_region(dms);
+ if (!_stats_print_one_region(dms, clear, region_id))
+ goto_out;
+
+ /*FIXME: line control for large regions */
+ if (!(stbuff = dm_stats_print_region(dms, region_id, 0, 0, clear))) {
+ log_error("Could not print statistics region.");
+ goto out;
+ }
+
+ printf("%s", stbuff);
+ dm_stats_buffer_destroy(dms, stbuff);
+ }
+
+ r = 1;
+
+out:
+ dm_stats_destroy(dms);
+ return r;
+}
+
+static int _stats_report(CMD_ARGS)
+{
+ int r = 0, objtype_args;
+
+ struct dm_task *dmt;
+ char *name = NULL;
+
+ objtype_args = (_switches[AREA_ARG]
+ || _switches[REGION_ARG]
+ || _switches[GROUP_ARG]);
+
+ if (_switches[PROGRAM_ID_ARG])
+ _program_id = _string_args[PROGRAM_ID_ARG];
+
+ if (_switches[ALL_PROGRAMS_ARG])
+ _program_id = "";
+
+ if (_switches[VERBOSE_ARG] && !strcmp(subcommand, "list"))
+ _statstype |= (DM_STATS_WALK_ALL
+ | DM_STATS_WALK_SKIP_SINGLE_AREA);
+
+ /* suppress duplicates unless the user has requested all regions */
+ if (!strcmp(subcommand, "report") && !objtype_args)
+ /* suppress duplicate rows of output */
+ _statstype |= (DM_STATS_WALK_ALL
+ | DM_STATS_WALK_SKIP_SINGLE_AREA);
+
+ if (names)
+ name = names->name;
+ else {
+ if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
+ return _process_all(cmd, subcommand, argc, argv, 0, _info);
+ name = argv[0];
+ }
+
+ if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+ return_0;
+
+ if (!_set_task_device(dmt, name, 0))
+ goto_out;
+
+ if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
+ goto_out;
+
+ if (!_task_run(dmt))
+ goto_out;
+
+ r = _display_info(dmt);
+
+out:
+ dm_task_destroy(dmt);
+
+ if (!r && _report) {
+ dm_report_free(_report);
+ _report = NULL;
+ }
+
+ return r;
+}
+
+static int _stats_group(CMD_ARGS)
+{
+ char *name, *alias = NULL, *regions = NULL;
+ struct dm_stats *dms;
+ uint64_t group_id;
+ int r = 0;
+
+ /* group does not use a report */
+ if (_report) {
+ dm_report_free(_report);
+ _report = NULL;
+ }
+
+ if (!_switches[REGIONS_ARG]) {
+ log_error("Group requires --regions.");
+ return 0;
+ }
+
+ regions = _string_args[REGIONS_ARG];
+
+ if (names)
+ name = names->name;
+ else {
+ if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
+ if (!_switches[ALL_DEVICES_ARG]) {
+ log_error("Please specify device(s) or use "
+ "--alldevices.");
+ return 0;
+ }
+ return _process_all(cmd, subcommand, argc, argv, 0, _stats_group);
+ }
+ name = argv[0];
+ }
+
+ if (_switches[ALIAS_ARG])
+ alias = _string_args[ALIAS_ARG];
+
+ if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
+ return_0;
+
+ if (!_bind_stats_device(dms, name))
+ goto_out;
+
+ if (!dm_stats_list(dms, NULL))
+ goto_out;
+
+ if(!dm_stats_create_group(dms, regions, alias, &group_id)) {
+ log_error("Could not create group on %s: %s", name, regions);
+ goto out;
+ }
+
+ printf("Grouped regions %s as group ID " FMTu64 " on %s\n",
+ regions, group_id, name);
+
+ r = 1;
+
+out:
+ dm_stats_destroy(dms);
+ return r;
+}
+
+static int _stats_ungroup(CMD_ARGS)
+{
+ struct dm_stats *dms;
+ uint64_t group_id;
+ char *name;
+ int r = 0;
+
+ /* ungroup does not use a report */
+ if (_report) {
+ dm_report_free(_report);
+ _report = NULL;
+ }
+
+ if (!_switches[GROUP_ID_ARG]) {
+ log_error("Please specify group id.");
+ return 0;
+ }
+
+ group_id = (uint64_t) _int_args[GROUP_ID_ARG];
+
+ if (names)
+ name = names->name;
+ else {
+ if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
+ if (!_switches[ALL_DEVICES_ARG]) {
+ log_error("Please specify device(s) or use "
+ "--alldevices.");
+ return 0;
+ }
+ return _process_all(cmd, subcommand, argc, argv, 0, _stats_ungroup);
+ }
+ name = argv[0];
+ }
+
+ if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
+ return_0;
+
+ if (!_bind_stats_device(dms, name))
+ goto_out;
+
+ if (!dm_stats_list(dms, NULL))
+ goto_out;
+
+ if (!(r = dm_stats_delete_group(dms, group_id, 0)))
+ log_error("Could not delete group " FMTu64 " on %s.",
+ group_id, name);
+
+ printf("Removed group ID "FMTu64" on %s\n", group_id, name);
+
+out:
+ dm_stats_destroy(dms);
+ return r;
+}
+
+static int _stats_update_file(CMD_ARGS)
+{
+ uint64_t group_id, *region, *regions = NULL, count = 0;
+ const char *program_id = DM_STATS_PROGRAM_ID;
+ int foreground = _switches[FOREGROUND_ARG];
+ int verbose = _switches[VERBOSE_ARG];
+ char *path, *abspath = NULL;
+ struct dm_stats *dms = NULL;
+ dm_filemapd_mode_t mode;
+ int fd = -1;
+
+
+ if (names) {
+ log_error("Device names are not compatibile with update_filemap.");
+ return 0;
+ }
+
+ if (!_stats_check_filemap_switches())
+ return 0;
+
+ /* _stats_update_file does not use _process_all() */
+ if (!argc) {
+ log_error("update_filemap requires a file path argument");
+ return 0;
+ }
+
+ if (!_switches[GROUP_ID_ARG]) {
+ log_error("--groupid is required to update a filemap group.");
+ return 0;
+ }
+
+ path = argv[0];
+
+ if (!(abspath = _get_abspath(path))) {
+ log_error("Could not canonicalize file name: %s", path);
+ return 0;
+ }
+
+ group_id = (uint64_t) _int_args[GROUP_ID_ARG];
+
+ mode = _stats_get_filemapd_mode();
+ if (!_switches[NOMONITOR_ARG] && (mode == DM_FILEMAPD_FOLLOW_NONE))
+ goto bad;
+
+ if (_switches[PROGRAM_ID_ARG])
+ program_id = _string_args[PROGRAM_ID_ARG];
+ if (!strlen(program_id) && !_switches[FORCE_ARG])
+ program_id = DM_STATS_PROGRAM_ID;
+
+ if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
+ goto_bad;
+
+ fd = open(abspath, O_RDONLY);
+
+ if (fd < 0) {
+ log_error("Could not open %s for reading", abspath);
+ goto bad;
+ }
+
+ if (!dm_stats_bind_from_fd(dms, fd))
+ goto_bad;
+
+ if (!strlen(program_id))
+ /* force creation of a region with no id */
+ dm_stats_set_program_id(dms, 1, NULL);
+
+ /*
+ * Start dmfilemapd - it will test the file descriptor to determine
+ * whether it is necessary to call dm_stats_update_regions_from_fd().
+ *
+ * If starting the daemon fails, fall back to a direct update.
+ */
+ if (!_switches[NOMONITOR_ARG]) {
+ if (dm_stats_start_filemapd(fd, group_id, abspath, mode,
+ foreground, verbose))
+ goto out;
+
+ log_warn("Failed to start filemap monitoring daemon.");
+
+ /* fall back to one-shot update */
+ }
+
+ /*
+ * --nomonitor and fall back case - perform a one-shot update directly
+ * from dmsetup.
+ */
+ regions = dm_stats_update_regions_from_fd(dms, fd, group_id);
+
+ if (!regions) {
+ log_error("Could not update regions from file %s", abspath);
+ goto bad;
+ }
+
+ for (region = regions; *region != DM_STATS_REGIONS_ALL; region++)
+ count++;
+
+ if (group_id != regions[0]) {
+ printf("Group ID changed from " FMTu64 " to " FMTu64,
+ group_id, regions[0]);
+ group_id = regions[0];
+ }
+
+ printf("%s: Updated group ID " FMTu64 " with "FMTu64" region(s).\n",
+ path, group_id, count);
+
+out:
+ if (close(fd))
+ log_sys_debug("close", abspath);
+
+ free(regions);
+ free(abspath);
+ dm_stats_destroy(dms);
+ return 1;
+
+bad:
+ free(abspath);
+
+ if ((fd > -1) && close(fd))
+ log_sys_debug("close", path);
+
+ dm_stats_destroy(dms);
+
+ return 0;
+}
+
+/*
+ * Command dispatch tables and usage.
+ */
+static int _stats_help(CMD_ARGS);
+
+/*
+ * dmsetup stats <cmd> [options] [device_name]
+ * dmstats <cmd> [options] [device_name]
+ *
+ * clear [--allregions|--regionid id] [--alldevices|<device>...]
+ * create [--start <start> [--length <len>]
+ * [--areas <nr_areas>] [--areasize <size>]
+ * [--programid <id>] [--userdata <data> ]
+ * [--bounds histogram_boundaries] [--precise]
+ * [--alldevices|<device>...]
+ * create --filemap [--nogroup] [--nomonitor] [--follow=mode]
+ * [--programid <id>] [--userdata <data> ]
+ * [--bounds histogram_boundaries] [--precise] [<file_path>]
+ * delete [--allprograms|--programid id]
+ * [--allregions|--regionid id]
+ * [--alldevices|<device>...]
+ * group [--alias NAME] --regions <regions>
+ * [--allprograms|--programid id] [--alldevices|<device>...]
+ * list [--allprograms|--programid id] [--allregions|--regionid id]
+ * print [--clear] [--allprograms|--programid id]
+ * [--allregions|--regionid id]
+ * [--alldevices|<device>...]
+ * report [--interval <seconds>] [--count <cnt>]
+ * [--units <u>] [--programid <id>] [--regionid <id>]
+ * [-o <fields>] [-O|--sort <sort_fields>]
+ * [-S|--select <selection>] [--nameprefixes]
+ * [--noheadings] [--separator <separator>]
+ * [--allprograms|--programid id] [<device>...]
+ * ungroup --groupid <id> [--allprograms|--programid id]
+ * [--alldevices|<device>...]
+ */
+
+#define INDENT "\n\t "
+/* groups of commonly used options */
+#define AREA_OPTS "[--areas <nr_areas>] [--areasize <size>] "
+#define REGION_OPTS "[--start <start> [--length <len>]" INDENT AREA_OPTS
+#define ID_OPTS "[--programid <id>] [--userdata <data> ] "
+#define SELECT_OPTS "[--programid <id>] [--regionid <id>] "
+#define HIST_OPTS "[--bounds histogram_boundaries] "
+#define PRECISE_OPTS "[--precise] "
+#define SEGMENTS_OPT "[--segments] "
+#define EXTRA_OPTS HIST_OPTS PRECISE_OPTS
+#define FILE_MONITOR_OPTS "[--nomonitor] [--follow mode]"
+#define GROUP_ID_OPT "--groupid <id> "
+#define ALL_PROGS_OPT "[--allprograms|--programid id] "
+#define ALL_REGIONS_OPT "[--allregions|--regionid id] "
+#define ALL_DEVICES_OPT "[--alldevices|<device>...] "
+#define ALL_PROGS_REGIONS_DEVICES ALL_PROGS_OPT INDENT ALL_REGIONS_OPT INDENT ALL_DEVICES_OPT
+#define FIELD_OPTS "[-o <fields>] [-O|--sort <sort_fields>]"
+#define DM_REPORT_OPTS FIELD_OPTS INDENT "[-S|--select <selection>] [--nameprefixes]" INDENT \
+"[--noheadings] [--separator <separator>]"
+
+/* command options */
+#define CREATE_OPTS REGION_OPTS INDENT ID_OPTS INDENT EXTRA_OPTS INDENT SEGMENTS_OPT
+#define FILEMAP_OPTS "--filemap [--nogroup] " FILE_MONITOR_OPTS INDENT ID_OPTS INDENT EXTRA_OPTS
+#define PRINT_OPTS "[--clear] " ALL_PROGS_REGIONS_DEVICES
+#define REPORT_OPTS "[--interval <seconds>] [--count <cnt>]" INDENT \
+"[--units <u>] " SELECT_OPTS INDENT DM_REPORT_OPTS INDENT ALL_PROGS_OPT
+#define GROUP_OPTS "[--alias NAME] --regions <regions>" INDENT ALL_PROGS_OPT ALL_DEVICES_OPT
+#define UNGROUP_OPTS GROUP_ID_OPT ALL_PROGS_OPT INDENT ALL_DEVICES_OPT
+#define UPDATE_OPTS GROUP_ID_OPT INDENT FILE_MONITOR_OPTS " <file_path>"
+
+/*
+ * The 'create' command has two entries in the table, to allow for the
+ * the fact that 'create' and 'create --filemap' have largely disjoint
+ * sets of options.
+ */
+static struct command _stats_subcommands[] = {
+ {"help", "", 0, 0, 0, 0, _stats_help},
+ {"clear", ALL_REGIONS_OPT ALL_DEVICES_OPT, 0, -1, 1, 0, _stats_clear},
+ {"create", CREATE_OPTS ALL_DEVICES_OPT, 0, -1, 1, 0, _stats_create},
+ {"create", FILEMAP_OPTS "<file_path>", 0, -1, 1, 0, _stats_create},
+ {"delete", ALL_PROGS_REGIONS_DEVICES, 1, -1, 1, 0, _stats_delete},
+ {"group", GROUP_OPTS, 1, -1, 1, 0, _stats_group},
+ {"list", ALL_PROGS_OPT ALL_REGIONS_OPT, 0, -1, 1, 0, _stats_report},
+ {"print", PRINT_OPTS, 0, -1, 1, 0, _stats_print},
+ {"report", REPORT_OPTS "[<device>...]", 0, -1, 1, 0, _stats_report},
+ {"ungroup", UNGROUP_OPTS, 1, -1, 1, 0, _stats_ungroup},
+ {"update_filemap", UPDATE_OPTS, 1, 1, 0, 0, _stats_update_file},
+ {"version", "", 0, -1, 1, 0, _version},
+ {NULL, NULL, 0, 0, 0, 0, NULL}
+};
+
+#undef AREA_OPTS
+#undef REGION_OPTS
+#undef ID_OPTS
+#undef SELECT_OPTS
+#undef HIST_OPTS
+#undef PRECISE_OPTS
+#undef EXTRA_OPTS
+#undef ALL_PROGS_OPT
+#undef ALL_REGIONS_OPT
+#undef ALL_DEVICES_OPT
+#undef ALL_PROGS_REGIONS_DEVICES
+#undef FIELD_OPTS
+#undef DM_REPORT_OPTS
+#undef CREATE_OPTS
+#undef FILEMAP_OPTS
+#undef PRINT_OPTS
+#undef REPORT_OPTS
+#undef GROUP_OPTS
+#undef UNGROUP_OPTS
+
+static int _dmsetup_help(CMD_ARGS);
+
+static struct command _dmsetup_commands[] = {
+ {"help", "[-c|-C|--columns]", 0, 0, 0, 0, _dmsetup_help},
+ {"create", "<dev_name>\n"
+ "\t [-j|--major <major> -m|--minor <minor>]\n"
+ "\t [-U|--uid <uid>] [-G|--gid <gid>] [-M|--mode <octal_mode>]\n"
+ "\t [-u|--uuid <uuid>] [--addnodeonresume|--addnodeoncreate]\n"
+ "\t [--readahead {[+]<sectors>|auto|none}]\n"
+ "\t [-n|--notable|--table {<table>|<table_file>}]\n"
+ "\tcreate --concise [<concise_device_spec_list>]", 0, 2, 0, 0, _create},
+ {"remove", "[--deferred] [-f|--force] [--retry] <device>...", 0, -1, 2, 0, _remove},
+ {"remove_all", "[-f|--force]", 0, 0, 0, 0, _remove_all},
+ {"suspend", "[--noflush] [--nolockfs] <device>...", 0, -1, 2, 0, _suspend},
+ {"resume", "[--noflush] [--nolockfs] <device>...\n"
+ "\t [--addnodeonresume|--addnodeoncreate]\n"
+ "\t [--readahead {[+]<sectors>|auto|none}]", 0, -1, 2, 0, _resume},
+ {"load", "<device> [<table>|<table_file>]", 0, 2, 0, 0, _load},
+ {"clear", "<device>", 0, -1, 2, 0, _clear},
+ {"reload", "<device> [<table>|<table_file>]", 0, 2, 0, 0, _load},
+ {"wipe_table", "[-f|--force] [--noflush] [--nolockfs] <device>...", 0, -1, 2, 0, _error_device},
+ {"rename", "<device> [--setuuid] <new_name_or_uuid>", 1, 2, 0, 0, _rename},
+ {"message", "<device> <sector> <message>", 2, -1, 0, 0, _message},
+ {"ls", "[--target <target_type>] [--exec <command>] [-o <options>] [--tree]", 0, 0, 0, 0, _ls},
+ {"info", "[<device>...]", 0, -1, 1, 0, _info},
+ {"deps", "[-o <options>] [<device>...]", 0, -1, 2, 0, _deps},
+ {"stats", "<command> [<options>] [<device>...]", 1, -1, 1, 1, _stats},
+ {"status", "[<device>...] [--noflush] [--target <target_type>]", 0, -1, 2, 0, _status},
+ {"table", "[<device>...] [--concise] [--target <target_type>] [--showkeys]", 0, -1, 2, 0, _status},
+ {"wait", "<device> [<event_nr>] [--noflush]", 0, 2, 0, 0, _wait},
+ {"mknodes", "[<device>...]", 0, -1, 1, 0, _mknodes},
+ {"mangle", "[<device>...]", 0, -1, 1, 0, _mangle},
+ {"udevcreatecookie", "", 0, 0, 0, 0, _udevcreatecookie},
+ {"udevreleasecookie", "[<cookie>]", 0, 1, 0, 0, _udevreleasecookie},
+ {"udevflags", "<cookie>", 1, 1, 0, 0, _udevflags},
+ {"udevcomplete", "<cookie>", 1, 1, 0, 0, _udevcomplete},
+ {"udevcomplete_all", "[<age_in_minutes>]", 0, 1, 0, 0, _udevcomplete_all},
+ {"udevcookies", "", 0, 0, 0, 0, _udevcookies},
+ {"targets", "", 0, 0, 0, 0, _targets},
+ {"version", "", 0, 0, 0, 0, _version},
+ {"setgeometry", "<device> <cyl> <head> <sect> <start>", 5, 5, 0, 0, _setgeometry},
+ {"splitname", "<device> [<subsystem>]", 1, 2, 0, 0, _splitname},
+ {NULL, NULL, 0, 0, 0, 0, NULL}
+};
+
+/*
+ * Usage and help text.
+ */
+
+static void _devmap_name_usage(FILE *out)
+{
+ fprintf(out, "Usage: " DEVMAP_NAME_CMD_NAME " <major> <minor>\n\n");
+}
+
+static void _stats_usage(FILE *out)
+{
+ int i;
+
+ fprintf(out, "Usage:\n\n"
+ "%s\n"
+ " [-h|--help]\n"
+ " [-v|--verbose [-v|--verbose ...]]\n"
+ " [--areas <nr_areas>] [--areasize <size>]\n"
+ " [--userdata <data>] [--clear]\n"
+ " [--count <count>] [--interval <seconds>]\n"
+ " [-o <fields>] [-O|--sort <sort_fields>]\n"
+ " [--programid <id>]\n"
+ " [--start <start>] [--length <length>]\n"
+ " [--segments] [--units <units>]\n\n",
+ _base_commands[_base_command].name);
+
+ for (i = 0; _stats_subcommands[i].name; i++)
+ fprintf(out, "\t%s %s\n", _stats_subcommands[i].name, _stats_subcommands[i].help);
+
+ fprintf(out, "\n<device> may be device name or (if only one) -u <uuid> or -j <major> -m <minor>\n"
+ "<fields> are comma-separated. Use 'help -c' for list.\n\n");
+}
+
+static void _dmsetup_usage(FILE *out)
+{
+ int i;
+
+ fprintf(out, "Usage:\n\n"
+ "%s\n"
+ " [--version] [-h|--help [-c|-C|--columns]]\n"
+ " [-v|--verbose [-v|--verbose ...]] [-f|--force]\n"
+ " [--checks] [--manglename {none|hex|auto}]\n"
+ " [-r|--readonly] [--noopencount] [--noflush] [--nolockfs] [--inactive]\n"
+ " [--udevcookie <cookie>] [--noudevrules] [--noudevsync] [--verifyudev]\n"
+ " [-y|--yes] [--readahead {[+]<sectors>|auto|none}] [--retry]\n"
+ " [-c|-C|--columns] [-o <fields>] [-O|--sort <sort_fields>]\n"
+ " [-S|--select <selection>] [--nameprefixes] [--noheadings]\n"
+ " [--separator <separator>]\n\n",
+ _base_commands[_base_command].name);
+
+ for (i = 0; _dmsetup_commands[i].name; i++)
+ fprintf(out, "\t%s %s\n", _dmsetup_commands[i].name, _dmsetup_commands[i].help);
+
+ fprintf(out, "\n<device> may be device name or (if only one) -u <uuid> or "
+ "-j <major> -m <minor>\n"
+ "<mangling_mode> is one of 'none', 'auto' and 'hex'.\n"
+ "<fields> are comma-separated. Use 'help -c' for list.\n"
+ "<concise_device_specification> has single-device entries separated by semi-colons:\n"
+ " <name>,<uuid>,<minor>,<flags>,<table>\n"
+ " where <flags> is 'ro' or 'rw' (the default) and any of <uuid>, <minor>\n"
+ " and <flags> may be empty. Separate extra table lines with commas.\n"
+ " E.g.: dev1,,,,0 100 linear 253:1 0,100 100 error;dev2,,,ro,0 1 error\n"
+ "Table_file contents may be supplied on stdin.\n"
+ "Options are: devno, devname, blkdevname.\n"
+ "Tree specific options are: ascii, utf, vt100; compact, inverted, notrunc;\n"
+ " blkdevname, [no]device, active, open, rw and uuid.\n\n");
+}
+
+static void _losetup_usage(FILE *out)
+{
+ fprintf(out,
+ "Usage:\n\n"
+ "%s [-d|-a] [-e encryption] "
+ "[-o offset] [-f|loop_device] [file]\n\n",
+ _base_commands[_base_command].name);
+}
+
+static void _usage(FILE *out)
+{
+ switch (_base_commands[_base_command].type) {
+ case DMSETUP_TYPE:
+ return _dmsetup_usage(out);
+ case LOSETUP_TYPE:
+ return _losetup_usage(out);
+ case STATS_TYPE:
+ return _stats_usage(out);
+ case DEVMAP_NAME_TYPE:
+ return _devmap_name_usage(out);
+ }
+}
+
+static int _stats_help(CMD_ARGS)
+{
+ _usage(stderr);
+
+ if (_switches[COLS_ARG] || (argc && !strcmp(argv[0], "report"))) {
+ _switches[OPTIONS_ARG] = 1;
+ _string_args[OPTIONS_ARG] = (char *) "help";
+ _switches[SORT_ARG] = 0;
+
+ if (_report) {
+ dm_report_free(_report);
+ _report = NULL;
+ }
+
+ (void) _report_init(cmd, "help");
+ if (_report) {
+ dm_report_free(_report);
+ _report = NULL;
+ }
+ }
+
+ return 1;
+}
+
+static int _dmsetup_help(CMD_ARGS)
+{
+ _usage(stderr);
+
+ if (_switches[COLS_ARG]) {
+ _switches[OPTIONS_ARG] = 1;
+ _string_args[OPTIONS_ARG] = (char *) "help";
+ _switches[SORT_ARG] = 0;
+
+ if (_report) {
+ dm_report_free(_report);
+ _report = NULL;
+ }
+ (void) _report_init(cmd, "");
+ if (_report) {
+ dm_report_free(_report);
+ _report = NULL;
+ }
+ }
+
+ return 1;
+}
+
+static const struct command *_find_command(const struct command *commands,
+ const char *name)
+{
+ int i;
+
+ for (i = 0; commands[i].name; i++)
+ if (!strcmp(commands[i].name, name))
+ return commands + i;
+
+ return NULL;
+}
+
+static const struct command *_find_dmsetup_command(const char *name)
+{
+ return _find_command(_dmsetup_commands, name);
+}
+
+static const struct command *_find_stats_subcommand(const char *name)
+{
+ return _find_command(_stats_subcommands, name);
+}
+
+static int _stats(CMD_ARGS)
+{
+ const struct command *stats_cmd;
+
+ if (_switches[AREA_ARG] || _switches[REGION_ARG] || _switches[GROUP_ARG])
+ _statstype = 0; /* switches will OR flags in */
+ else
+ _statstype = DM_STATS_WALK_REGION | DM_STATS_WALK_GROUP;
+
+ if (_switches[AREA_ARG])
+ _statstype |= DM_STATS_WALK_AREA;
+ if (_switches[REGION_ARG])
+ _statstype |= DM_STATS_WALK_REGION;
+ if (_switches[GROUP_ARG])
+ _statstype |= DM_STATS_WALK_GROUP;
+
+ if (!(stats_cmd = _find_stats_subcommand(subcommand))) {
+ log_error("Unknown stats command.");
+ _stats_help(stats_cmd, NULL, argc, argv, NULL, multiple_devices);
+ return 0;
+ }
+
+ if (_switches[ALL_PROGRAMS_ARG] && _switches[PROGRAM_ID_ARG]) {
+ log_error("Please supply one of --allprograms and --programid");
+ return 0;
+ }
+
+ if (_switches[ALL_REGIONS_ARG] && _switches[REGION_ID_ARG]) {
+ log_error("Please supply one of --allregions and --regionid");
+ return 0;
+ }
+
+ if (_switches[FOLLOW_ARG] && _switches[NOMONITOR_ARG]) {
+ log_error("Use of --follow is incompatible with --nomonitor.");
+ return 0;
+ }
+
+ /*
+ * Pass the sub-command through to allow a single function to be
+ * used to implement several distinct sub-commands (e.g. 'report'
+ * and 'list' share a single implementation.
+ */
+ if (!stats_cmd->fn(stats_cmd, subcommand, argc, argv, NULL,
+ multiple_devices))
+ return_0;
+
+ return 1;
+}
+
+static int _process_tree_options(const char *options)
+{
+ const char *s, *end;
+ struct winsize winsz;
+ size_t len;
+
+ /* Symbol set default */
+ if (!strcmp(nl_langinfo(CODESET), "UTF-8"))
+ _tsym = &_tsym_utf;
+ else
+ _tsym = &_tsym_ascii;
+
+ /* Default */
+ _tree_switches[TR_DEVICE] = 1;
+ _tree_switches[TR_TRUNCATE] = 1;
+
+ /* parse */
+ for (s = options; s && *s; s++) {
+ len = 0;
+ for (end = s; *end && *end != ','; end++, len++)
+ ;
+ if (!strncmp(s, "device", len))
+ _tree_switches[TR_DEVICE] = 1;
+ else if (!strncmp(s, "blkdevname", len))
+ _tree_switches[TR_BLKDEVNAME] = 1;
+ else if (!strncmp(s, "nodevice", len))
+ _tree_switches[TR_DEVICE] = 0;
+ else if (!strncmp(s, "status", len))
+ _tree_switches[TR_STATUS] = 1;
+ else if (!strncmp(s, "table", len))
+ _tree_switches[TR_TABLE] = 1;
+ else if (!strncmp(s, "active", len))
+ _tree_switches[TR_ACTIVE] = 1;
+ else if (!strncmp(s, "open", len))
+ _tree_switches[TR_OPENCOUNT] = 1;
+ else if (!strncmp(s, "uuid", len))
+ _tree_switches[TR_UUID] = 1;
+ else if (!strncmp(s, "rw", len))
+ _tree_switches[TR_RW] = 1;
+ else if (!strncmp(s, "utf", len))
+ _tsym = &_tsym_utf;
+ else if (!strncmp(s, "vt100", len))
+ _tsym = &_tsym_vt100;
+ else if (!strncmp(s, "ascii", len))
+ _tsym = &_tsym_ascii;
+ else if (!strncmp(s, "inverted", len))
+ _tree_switches[TR_BOTTOMUP] = 1;
+ else if (!strncmp(s, "compact", len))
+ _tree_switches[TR_COMPACT] = 1;
+ else if (!strncmp(s, "notrunc", len))
+ _tree_switches[TR_TRUNCATE] = 0;
+ else {
+ log_error("Tree options not recognised: %s.", s);
+ return 0;
+ }
+ if (!*end)
+ break;
+ s = end;
+ }
+
+ /* Truncation doesn't work well with vt100 drawing char */
+ if (_tsym != &_tsym_vt100)
+ if (ioctl(1, (unsigned long) TIOCGWINSZ, &winsz) >= 0 && winsz.ws_col > 3)
+ _termwidth = winsz.ws_col - 3;
+
+ return 1;
+}
+
+static char *_parse_loop_device_name(const char *dev, const char *dev_dir)
+{
+ char *buf;
+ char *device = NULL;
+
+ if (!(buf = malloc(PATH_MAX)))
+ return_NULL;
+
+ if (dev[0] == '/') {
+ if (!(device = _get_abspath(dev)))
+ goto_bad;
+
+ if (strncmp(device, dev_dir, strlen(dev_dir)))
+ goto_bad;
+
+ /* If dev_dir does not end in a slash, ensure that the
+ following byte in the device string is "/". */
+ if (dev_dir[strlen(dev_dir) - 1] != '/' &&
+ device[strlen(dev_dir)] != '/')
+ goto_bad;
+
+ if (!dm_strncpy(buf, strrchr(device, '/') + 1, PATH_MAX))
+ goto_bad;
+ free(device);
+ } else {
+ /* check for device number */
+ if (strncmp(dev, "loop", sizeof("loop") - 1))
+ goto_bad;
+
+ if (!dm_strncpy(buf, dev, PATH_MAX))
+ goto_bad;
+ }
+
+ return buf;
+bad:
+ free(device);
+ free(buf);
+
+ return NULL;
+}
+
+/*
+ * create a table for a mapped device using the loop target.
+ */
+static int _loop_table(char *table, size_t tlen, char *file,
+ char *dev __attribute__((unused)), off_t off)
+{
+ struct stat fbuf;
+ off_t size, sectors;
+ int fd = -1;
+#ifdef HAVE_SYS_STATVFS_H
+ struct statvfs fsbuf;
+ off_t blksize;
+#endif
+
+ if (!_switches[READ_ONLY])
+ fd = open(file, O_RDWR);
+
+ if (fd < 0) {
+ _switches[READ_ONLY]++;
+ fd = open(file, O_RDONLY);
+ }
+
+ if (fd < 0)
+ goto_bad;
+
+ if (fstat(fd, &fbuf))
+ goto_bad;
+
+ size = (fbuf.st_size - off);
+ sectors = size >> SECTOR_SHIFT;
+
+ if (_switches[VERBOSE_ARG])
+ log_error(LOSETUP_CMD_NAME ": set loop size to %llukB (%llu sectors).",
+ (long long unsigned) sectors >> 1,
+ (long long unsigned) sectors);
+
+#ifdef HAVE_SYS_STATVFS_H
+ if (fstatvfs(fd, &fsbuf))
+ goto_bad;
+
+ /* FIXME Fragment size currently unused */
+ blksize = fsbuf.f_frsize;
+#endif
+
+ if (close(fd))
+ log_sys_debug("close", file);
+
+ if (dm_snprintf(table, tlen, "%llu %llu loop %s %llu\n", 0ULL,
+ (long long unsigned)sectors, file, (long long unsigned)off) < 0)
+ return_0;
+
+ if (_switches[VERBOSE_ARG] > 1)
+ log_verbose("Table: %s", table);
+
+ return 1;
+
+bad:
+ if (fd > -1 && close(fd))
+ log_sys_error("close", file);
+
+ return 0;
+}
+
+static int _process_losetup_switches(const char *base, int *argcp, char ***argvp,
+ const char *dev_dir)
+{
+ int c;
+ int encrypt_loop = 0, delete = 0, find = 0, show_all = 0;
+ char *device_name = NULL;
+ char *loop_file = NULL;
+ off_t offset = 0;
+
+#ifdef HAVE_GETOPTLONG
+ static struct option long_options[] = {
+ {0, 0, 0, 0}
+ };
+#endif
+
+ optarg = 0;
+ optind = OPTIND_INIT;
+ while ((c = GETOPTLONG_FN(*argcp, *argvp, "ade:fo:v",
+ long_options, NULL)) != -1 ) {
+ if (c == ':' || c == '?')
+ return_0;
+ if (c == 'a')
+ show_all++;
+ if (c == 'd')
+ delete++;
+ if (c == 'e')
+ encrypt_loop++;
+ if (c == 'f')
+ find++;
+ if (c == 'o')
+ offset = atoi(optarg);
+ if (c == 'v')
+ _switches[VERBOSE_ARG]++;
+ }
+
+ *argvp += optind ;
+ *argcp -= optind ;
+
+ if (encrypt_loop){
+ log_error("%s: Sorry, cryptoloop is not yet implemented "
+ "in this version.", base);
+ return 0;
+ }
+
+ if (show_all) {
+ log_error("%s: Sorry, show all is not yet implemented "
+ "in this version.", base);
+ return 0;
+ }
+
+ if (find) {
+ log_error("%s: Sorry, find is not yet implemented "
+ "in this version.", base);
+ if (!*argcp)
+ return 0;
+ }
+
+ if (!*argcp) {
+ log_error("%s: Please specify loop_device.", base);
+ _usage(stderr);
+ return 0;
+ }
+
+ if (!(device_name = _parse_loop_device_name((*argvp)[0], dev_dir))) {
+ log_error("%s: Could not parse loop_device %s", base, (*argvp)[0]);
+ _usage(stderr);
+ return 0;
+ }
+
+ if (delete) {
+ *argcp = 1;
+
+ (*argvp)[0] = device_name;
+ _command = "remove";
+
+ return 1;
+ }
+
+ if (*argcp != 2) {
+ log_error("%s: Too few arguments.", base);
+ _usage(stderr);
+ free(device_name);
+ return 0;
+ }
+
+ /* FIXME move these to make them available to native dmsetup */
+ if (!(loop_file = _get_abspath((*argvp)[(find) ? 0 : 1]))) {
+ log_error("%s: Could not parse loop file name %s.",
+ base, (*argvp)[1]);
+ _usage(stderr);
+ free(device_name);
+ return 0;
+ }
+
+ _table = malloc(LOOP_TABLE_SIZE);
+ if (!_table ||
+ !_loop_table(_table, (size_t) LOOP_TABLE_SIZE, loop_file, device_name, offset)) {
+ log_error("Could not build device-mapper table for %s.", (*argvp)[0]);
+ free(device_name);
+ return 0;
+ }
+ _switches[TABLE_ARG]++;
+
+ _command = "create";
+ (*argvp)[0] = device_name ;
+ *argcp = 1;
+
+ return 1;
+}
+
+static int _process_options(const char *options)
+{
+ const char *s, *end;
+ size_t len;
+
+ /* Tree options are processed separately. */
+ if (_switches[TREE_ARG])
+ return _process_tree_options(_string_args[OPTIONS_ARG]);
+
+ /* Column options are processed separately by _report_init (called later). */
+ if (_switches[COLS_ARG])
+ return 1;
+
+ /* No options specified. */
+ if (!_switches[OPTIONS_ARG])
+ return 1;
+
+ /* Set defaults. */
+ _dev_name_type = DN_DEVNO;
+
+ /* Parse. */
+ for (s = options; s && *s; s++) {
+ len = 0;
+ for (end = s; *end && *end != ','; end++, len++)
+ ;
+ if (!strncmp(s, "devno", len))
+ _dev_name_type = DN_DEVNO;
+ else if (!strncmp(s, "blkdevname", len))
+ _dev_name_type = DN_BLK;
+ else if (!strncmp(s, "devname", len))
+ _dev_name_type = DN_MAP;
+ else {
+ log_error("Option not recognised: %s.", s);
+ return 0;
+ }
+
+ if (!*end)
+ break;
+ s = end;
+ }
+
+ return 1;
+}
+
+static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
+{
+ const char *base;
+ char *namebase, *s;
+ static int ind;
+ int c, r, i;
+
+#ifdef HAVE_GETOPTLONG
+ static struct option long_options[] = {
+ {"readonly", 0, &ind, READ_ONLY},
+ {"alias", 1, &ind, ALIAS_ARG},
+ {"alldevices", 0, &ind, ALL_DEVICES_ARG},
+ {"allprograms", 0, &ind, ALL_PROGRAMS_ARG},
+ {"allregions", 0, &ind, ALL_REGIONS_ARG},
+ {"area", 0, &ind, AREA_ARG},
+ {"areas", 1, &ind, AREAS_ARG},
+ {"areasize", 1, &ind, AREA_SIZE_ARG},
+ {"bounds", 1, &ind, BOUNDS_ARG},
+ {"checks", 0, &ind, CHECKS_ARG},
+ {"clear", 0, &ind, CLEAR_ARG},
+ {"columns", 0, &ind, COLS_ARG},
+ {"concise", 0, &ind, CONCISE_ARG},
+ {"count", 1, &ind, COUNT_ARG},
+ {"deferred", 0, &ind, DEFERRED_ARG},
+ {"select", 1, &ind, SELECT_ARG},
+ {"exec", 1, &ind, EXEC_ARG},
+ {"filemap", 0, &ind, FILEMAP_ARG},
+ {"follow", 1, &ind, FOLLOW_ARG},
+ {"force", 0, &ind, FORCE_ARG},
+ {"foreground", 0, &ind, FOREGROUND_ARG},
+ {"gid", 1, &ind, GID_ARG},
+ {"group", 0, &ind, GROUP_ARG},
+ {"groupid", 1, &ind, GROUP_ID_ARG},
+ {"help", 0, &ind, HELP_ARG},
+ {"histogram", 0, &ind, HISTOGRAM_ARG},
+ {"inactive", 0, &ind, INACTIVE_ARG},
+ {"interval", 1, &ind, INTERVAL_ARG},
+ {"length", 1, &ind, LENGTH_ARG},
+ {"manglename", 1, &ind, MANGLENAME_ARG},
+ {"major", 1, &ind, MAJOR_ARG},
+ {"minor", 1, &ind, MINOR_ARG},
+ {"mode", 1, &ind, MODE_ARG},
+ {"nameprefixes", 0, &ind, NAMEPREFIXES_ARG},
+ {"nogroup", 0, &ind, NOGROUP_ARG},
+ {"noflush", 0, &ind, NOFLUSH_ARG},
+ {"noheadings", 0, &ind, NOHEADINGS_ARG},
+ {"nolockfs", 0, &ind, NOLOCKFS_ARG},
+ {"noopencount", 0, &ind, NOOPENCOUNT_ARG},
+ {"nosuffix", 0, &ind, NOSUFFIX_ARG},
+ {"notable", 0, &ind, NOTABLE_ARG},
+ {"notimesuffix", 0, &ind, NOTIMESUFFIX_ARG},
+ {"udevcookie", 1, &ind, UDEVCOOKIE_ARG},
+ {"nomonitor", 0, &ind, NOMONITOR_ARG},
+ {"noudevrules", 0, &ind, NOUDEVRULES_ARG},
+ {"noudevsync", 0, &ind, NOUDEVSYNC_ARG},
+ {"options", 1, &ind, OPTIONS_ARG},
+ {"precise", 0, &ind, PRECISE_ARG},
+ {"programid", 1, &ind, PROGRAM_ID_ARG},
+ {"raw", 0, &ind, RAW_ARG},
+ {"readahead", 1, &ind, READAHEAD_ARG},
+ {"region", 0, &ind, REGION_ARG},
+ {"regions", 1, &ind, REGIONS_ARG},
+ {"regionid", 1, &ind, REGION_ID_ARG},
+ {"relative", 0, &ind, RELATIVE_ARG},
+ {"retry", 0, &ind, RETRY_ARG},
+ {"rows", 0, &ind, ROWS_ARG},
+ {"segments", 0, &ind, SEGMENTS_ARG},
+ {"separator", 1, &ind, SEPARATOR_ARG},
+ {"setuuid", 0, &ind, SETUUID_ARG},
+ {"showkeys", 0, &ind, SHOWKEYS_ARG},
+ {"sort", 1, &ind, SORT_ARG},
+ {"start", 1, &ind, START_ARG},
+ {"table", 1, &ind, TABLE_ARG},
+ {"target", 1, &ind, TARGET_ARG},
+ {"tree", 0, &ind, TREE_ARG},
+ {"uid", 1, &ind, UID_ARG},
+ {"units", 1, &ind, UNITS_ARG},
+ {"uuid", 1, &ind, UUID_ARG},
+ {"unbuffered", 0, &ind, UNBUFFERED_ARG},
+ {"unquoted", 0, &ind, UNQUOTED_ARG},
+ {"userdata", 1, &ind, USER_DATA_ARG},
+ {"verbose", 1, &ind, VERBOSE_ARG},
+ {"verifyudev", 0, &ind, VERIFYUDEV_ARG},
+ {"version", 0, &ind, VERSION_ARG},
+ {"yes", 0, &ind, YES_ARG},
+ {"addnodeonresume", 0, &ind, ADD_NODE_ON_RESUME_ARG},
+ {"addnodeoncreate", 0, &ind, ADD_NODE_ON_CREATE_ARG},
+ {0, 0, 0, 0}
+ };
+#else
+ struct option long_options;
+#endif
+
+ /*
+ * Zero all the index counts.
+ */
+ memset(&_switches, 0, sizeof(_switches));
+ memset(&_int_args, 0, sizeof(_int_args));
+ _read_ahead_flags = 0;
+
+ if (!(namebase = strdup((*argvp)[0]))) {
+ log_error("Failed to duplicate name.");
+ return 0;
+ }
+
+ base = dm_basename(namebase);
+
+ i = 0;
+ do {
+ if (!strcmp(base, _base_commands[i].name)) {
+ _base_command = _base_commands[i].command;
+ _base_command_type = _base_commands[i].type;
+ break;
+ }
+ } while (++i < _num_base_commands);
+
+ free(namebase);
+
+ if (_base_command_type == DEVMAP_NAME_TYPE) {
+ _switches[COLS_ARG]++;
+ _switches[NOHEADINGS_ARG]++;
+ _switches[OPTIONS_ARG]++;
+ _switches[MAJOR_ARG]++;
+ _switches[MINOR_ARG]++;
+ _string_args[OPTIONS_ARG] = (char *) "name";
+
+ if (*argcp == 3) {
+ _int_args[MAJOR_ARG] = atoi((*argvp)[1]);
+ _int_args[MINOR_ARG] = atoi((*argvp)[2]);
+ *argcp -= 2;
+ *argvp += 2;
+ } else if ((*argcp == 2) &&
+ (2 == sscanf((*argvp)[1], "%i:%i",
+ &_int_args[MAJOR_ARG],
+ &_int_args[MINOR_ARG]))) {
+ *argcp -= 1;
+ *argvp += 1;
+ } else {
+ _usage(stderr);
+ return 0;
+ }
+
+ _command = "info";
+ (*argvp)++;
+ (*argcp)--;
+
+ return 1;
+ }
+
+ if (_base_command_type == LOSETUP_TYPE) {
+ r = _process_losetup_switches(_base_commands[_base_command].name, argcp, argvp, dev_dir);
+ return r;
+ }
+
+ optarg = 0;
+ optind = OPTIND_INIT;
+ while ((ind = -1, c = GETOPTLONG_FN(*argcp, *argvp, "cCfG:hj:m:M:no:O:rS:u:U:vy",
+ long_options, NULL)) != -1) {
+ if (ind == ALIAS_ARG) {
+ _switches[ALIAS_ARG]++;
+ _string_args[ALIAS_ARG] = optarg;
+ }
+ if (ind == ALL_DEVICES_ARG)
+ _switches[ALL_DEVICES_ARG]++;
+ if (ind == ALL_PROGRAMS_ARG)
+ _switches[ALL_PROGRAMS_ARG]++;
+ if (ind == ALL_REGIONS_ARG)
+ _switches[ALL_REGIONS_ARG]++;
+ if (ind == AREA_ARG)
+ _switches[AREA_ARG]++;
+ if (ind == AREAS_ARG) {
+ _switches[AREAS_ARG]++;
+ _int_args[AREAS_ARG] = atoi(optarg);
+ }
+ if (ind == AREA_SIZE_ARG) {
+ _switches[AREA_SIZE_ARG]++;
+ _string_args[AREA_SIZE_ARG] = optarg;
+ }
+ if (ind == USER_DATA_ARG) {
+ _switches[USER_DATA_ARG]++;
+ _string_args[USER_DATA_ARG] = optarg;
+ }
+ if (c == ':' || c == '?')
+ return_0;
+ if (c == 'h' || ind == HELP_ARG)
+ _switches[HELP_ARG]++;
+ if (ind == CONCISE_ARG)
+ _switches[CONCISE_ARG]++;
+ if (ind == BOUNDS_ARG) {
+ _switches[BOUNDS_ARG]++;
+ _string_args[BOUNDS_ARG] = optarg;
+ }
+ if (ind == CLEAR_ARG)
+ _switches[CLEAR_ARG]++;
+ if (c == 'c' || c == 'C' || ind == COLS_ARG)
+ _switches[COLS_ARG]++;
+ if (ind == FILEMAP_ARG)
+ _switches[FILEMAP_ARG]++;
+ if (ind == FOLLOW_ARG) {
+ _switches[FOLLOW_ARG]++;
+ _string_args[FOLLOW_ARG] = optarg;
+ }
+ if (c == 'f' || ind == FORCE_ARG)
+ _switches[FORCE_ARG]++;
+ if (ind == FOREGROUND_ARG)
+ _switches[FOREGROUND_ARG]++;
+ if (c == 'r' || ind == READ_ONLY)
+ _switches[READ_ONLY]++;
+ if (ind == HISTOGRAM_ARG)
+ _switches[HISTOGRAM_ARG]++;
+ if (ind == LENGTH_ARG) {
+ _switches[LENGTH_ARG]++;
+ _string_args[LENGTH_ARG] = optarg;
+ }
+ if (c == 'j' || ind == MAJOR_ARG) {
+ _switches[MAJOR_ARG]++;
+ _int_args[MAJOR_ARG] = atoi(optarg);
+ }
+ if (ind == REGIONS_ARG) {
+ _switches[REGIONS_ARG]++;
+ _string_args[REGIONS_ARG] = optarg;
+ }
+ if (c == 'm' || ind == MINOR_ARG) {
+ _switches[MINOR_ARG]++;
+ _int_args[MINOR_ARG] = atoi(optarg);
+ }
+ if (ind == NOSUFFIX_ARG)
+ _switches[NOSUFFIX_ARG]++;
+ if (c == 'n' || ind == NOTABLE_ARG)
+ _switches[NOTABLE_ARG]++;
+ if (ind == NOTIMESUFFIX_ARG)
+ _switches[NOTIMESUFFIX_ARG]++;
+ if (c == 'o' || ind == OPTIONS_ARG) {
+ _switches[OPTIONS_ARG]++;
+ _string_args[OPTIONS_ARG] = optarg;
+ }
+ if (ind == PROGRAM_ID_ARG) {
+ _switches[PROGRAM_ID_ARG]++;
+ _string_args[PROGRAM_ID_ARG] = optarg;
+ }
+ if (ind == PRECISE_ARG)
+ _switches[PRECISE_ARG]++;
+ if (ind == RAW_ARG)
+ _switches[RAW_ARG]++;
+ if (ind == REGION_ARG)
+ _switches[REGION_ARG]++;
+ if (ind == REGION_ID_ARG) {
+ _switches[REGION_ID_ARG]++;
+ _int_args[REGION_ID_ARG] = atoi(optarg);
+ }
+ if (ind == RELATIVE_ARG)
+ _switches[RELATIVE_ARG]++;
+ if (ind == SEPARATOR_ARG) {
+ _switches[SEPARATOR_ARG]++;
+ _string_args[SEPARATOR_ARG] = optarg;
+ }
+ if (ind == UNITS_ARG) {
+ _switches[UNITS_ARG]++;
+ _string_args[UNITS_ARG] = optarg;
+ }
+ if (c == 'O' || ind == SORT_ARG) {
+ _switches[SORT_ARG]++;
+ _string_args[SORT_ARG] = optarg;
+ }
+ if (c == 'S' || ind == SELECT_ARG) {
+ _switches[SELECT_ARG]++;
+ _string_args[SELECT_ARG] = optarg;
+ }
+ if (ind == START_ARG) {
+ _switches[START_ARG]++;
+ _string_args[START_ARG] = optarg;
+ }
+ if (c == 'v' || ind == VERBOSE_ARG)
+ _switches[VERBOSE_ARG]++;
+ if (c == 'u' || ind == UUID_ARG) {
+ _switches[UUID_ARG]++;
+ _uuid = optarg;
+ }
+ if (c == 'y' || ind == YES_ARG)
+ _switches[YES_ARG]++;
+ if (ind == ADD_NODE_ON_RESUME_ARG)
+ _switches[ADD_NODE_ON_RESUME_ARG]++;
+ if (ind == ADD_NODE_ON_CREATE_ARG)
+ _switches[ADD_NODE_ON_CREATE_ARG]++;
+ if (ind == CHECKS_ARG)
+ _switches[CHECKS_ARG]++;
+ if (ind == COUNT_ARG) {
+ _switches[COUNT_ARG]++;
+ _int_args[COUNT_ARG] = atoi(optarg);
+ if (_int_args[COUNT_ARG] < 0) {
+ log_error("Count must be zero or greater.");
+ return 0;
+ }
+ }
+ if (ind == UDEVCOOKIE_ARG) {
+ _switches[UDEVCOOKIE_ARG]++;
+ _udev_cookie = _get_cookie_value(optarg);
+ }
+ if (ind == NOMONITOR_ARG)
+ _switches[NOMONITOR_ARG]++;
+ if (ind == NOUDEVRULES_ARG)
+ _switches[NOUDEVRULES_ARG]++;
+ if (ind == NOUDEVSYNC_ARG)
+ _switches[NOUDEVSYNC_ARG]++;
+ if (ind == VERIFYUDEV_ARG)
+ _switches[VERIFYUDEV_ARG]++;
+ if (c == 'G' || ind == GID_ARG) {
+ _switches[GID_ARG]++;
+ _int_args[GID_ARG] = atoi(optarg);
+ }
+ if (ind == GROUP_ARG)
+ _switches[GROUP_ARG]++;
+ if (ind == GROUP_ID_ARG) {
+ _switches[GROUP_ID_ARG]++;
+ _int_args[GROUP_ID_ARG] = atoi(optarg);
+ }
+ if (c == 'U' || ind == UID_ARG) {
+ _switches[UID_ARG]++;
+ _int_args[UID_ARG] = atoi(optarg);
+ }
+ if (c == 'M' || ind == MODE_ARG) {
+ _switches[MODE_ARG]++;
+ /* FIXME Accept modes as per chmod */
+ errno = 0;
+ _int_args[MODE_ARG] = (int) strtol(optarg, &s, 8);
+ if (errno || !s || *s || !_int_args[MODE_ARG]) {
+ log_error("Invalid argument for --mode: %s. %s",
+ optarg, errno ? strerror(errno) : "");
+ return 0;
+ }
+ }
+ if (ind == DEFERRED_ARG)
+ _switches[DEFERRED_ARG]++;
+ if (ind == EXEC_ARG) {
+ _switches[EXEC_ARG]++;
+ _command_to_exec = optarg;
+ }
+ if (ind == TARGET_ARG) {
+ _switches[TARGET_ARG]++;
+ _target = optarg;
+ }
+ if (ind == SEGMENTS_ARG)
+ _switches[SEGMENTS_ARG]++;
+ if (ind == INACTIVE_ARG)
+ _switches[INACTIVE_ARG]++;
+ if (ind == INTERVAL_ARG) {
+ _switches[INTERVAL_ARG]++;
+ _int_args[INTERVAL_ARG] = atoi(optarg);
+ if (_int_args[INTERVAL_ARG] <= 0) {
+ log_error("Interval must be a positive integer.");
+ return 0;
+ }
+ }
+ if (ind == MANGLENAME_ARG) {
+ _switches[MANGLENAME_ARG]++;
+ if (!strcasecmp(optarg, "none"))
+ _int_args[MANGLENAME_ARG] = DM_STRING_MANGLING_NONE;
+ else if (!strcasecmp(optarg, "auto"))
+ _int_args[MANGLENAME_ARG] = DM_STRING_MANGLING_AUTO;
+ else if (!strcasecmp(optarg, "hex"))
+ _int_args[MANGLENAME_ARG] = DM_STRING_MANGLING_HEX;
+ else {
+ log_error("Unknown name mangling mode.");
+ return 0;
+ }
+ dm_set_name_mangling_mode((dm_string_mangling_t) _int_args[MANGLENAME_ARG]);
+ }
+ if (ind == NAMEPREFIXES_ARG)
+ _switches[NAMEPREFIXES_ARG]++;
+ if (ind == NOFLUSH_ARG)
+ _switches[NOFLUSH_ARG]++;
+ if (ind == NOGROUP_ARG)
+ _switches[NOGROUP_ARG]++;
+ if (ind == NOHEADINGS_ARG)
+ _switches[NOHEADINGS_ARG]++;
+ if (ind == NOLOCKFS_ARG)
+ _switches[NOLOCKFS_ARG]++;
+ if (ind == NOOPENCOUNT_ARG)
+ _switches[NOOPENCOUNT_ARG]++;
+ if (ind == READAHEAD_ARG) {
+ _switches[READAHEAD_ARG]++;
+ if (!strcasecmp(optarg, "auto"))
+ _int_args[READAHEAD_ARG] = DM_READ_AHEAD_AUTO;
+ else if (!strcasecmp(optarg, "none"))
+ _int_args[READAHEAD_ARG] = DM_READ_AHEAD_NONE;
+ else {
+ for (s = optarg; isspace(*s); s++)
+ ;
+ if (*s == '+')
+ _read_ahead_flags = DM_READ_AHEAD_MINIMUM_FLAG;
+ _int_args[READAHEAD_ARG] = atoi(optarg);
+ if (_int_args[READAHEAD_ARG] < -1) {
+ log_error("Negative read ahead value "
+ "(%d) is not understood.",
+ _int_args[READAHEAD_ARG]);
+ return 0;
+ }
+ }
+ }
+ if (ind == RETRY_ARG)
+ _switches[RETRY_ARG]++;
+ if (ind == ROWS_ARG)
+ _switches[ROWS_ARG]++;
+ if (ind == SETUUID_ARG)
+ _switches[SETUUID_ARG]++;
+ if (ind == SHOWKEYS_ARG)
+ _switches[SHOWKEYS_ARG]++;
+ if (ind == TABLE_ARG) {
+ _switches[TABLE_ARG]++;
+ if (!(_table = strdup(optarg))) {
+ log_error("Could not allocate memory for table string.");
+ return 0;
+ }
+ }
+ if (ind == TREE_ARG)
+ _switches[TREE_ARG]++;
+ if (ind == UNQUOTED_ARG)
+ _switches[UNQUOTED_ARG]++;
+ if (ind == VERSION_ARG)
+ _switches[VERSION_ARG]++;
+ }
+
+ if (_switches[VERBOSE_ARG] > 1) {
+ dm_log_init_verbose(_switches[VERBOSE_ARG] - 1);
+ if (_switches[VERBOSE_ARG] > 2) {
+ if (!(_initial_timestamp = dm_timestamp_alloc()))
+ stack;
+ else if (!dm_timestamp_get(_initial_timestamp))
+ stack;
+ else
+ log_debug("Timestamp: 0.000000000 seconds");
+ }
+ }
+
+ if ((_switches[MAJOR_ARG] && !_switches[MINOR_ARG]) ||
+ (!_switches[MAJOR_ARG] && _switches[MINOR_ARG])) {
+ log_error("Please specify both major number and minor number.");
+ return 0;
+ }
+
+ if (_switches[TABLE_ARG] && _switches[NOTABLE_ARG]) {
+ log_error("--table and --notable are incompatible.");
+ return 0;
+ }
+
+ if (_switches[ADD_NODE_ON_RESUME_ARG] && _switches[ADD_NODE_ON_CREATE_ARG]) {
+ log_error("--addnodeonresume and --addnodeoncreate are incompatible.");
+ return 0;
+ }
+
+ *argvp += optind;
+ *argcp -= optind;
+
+ if (!*argcp)
+ _command = NULL;
+ else if (!strcmp((*argvp)[0], "stats")) {
+ _base_command = DMSETUP_STATS_CMD;
+ _base_command_type = STATS_TYPE;
+ _command = "stats";
+ (*argvp)++;
+ (*argcp)--;
+ } else if (_base_command == DMSTATS_CMD) {
+ _command = "stats";
+ } else if (*argcp) {
+ _command = (*argvp)[0];
+ (*argvp)++;
+ (*argcp)--;
+ }
+
+ return 1;
+}
+
+static int _perform_command_for_all_repeatable_args(CMD_ARGS)
+{
+ do {
+ if (!cmd->fn(cmd, subcommand, argc, argv++, NULL, multiple_devices)) {
+ log_error("Command failed.");
+ return 0;
+ }
+ } while (cmd->repeatable_cmd && argc-- > 1);
+
+ return 1;
+}
+
+static int _do_report_wait(void)
+{
+ return _do_timer_wait();
+}
+
+int main(int argc, char **argv)
+{
+ int ret = 1, r;
+ const char *dev_dir;
+ const struct command *cmd;
+ const char *subcommand = "";
+ int multiple_devices;
+
+ (void) setlocale(LC_ALL, "");
+
+ dev_dir = getenv(DM_DEV_DIR_ENV_VAR_NAME);
+ if (dev_dir && *dev_dir) {
+ if (!dm_set_dev_dir(dev_dir)) {
+ log_error("Invalid DM_DEV_DIR environment variable value.");
+ goto out;
+ }
+ } else
+ dev_dir = DEFAULT_DM_DEV_DIR;
+
+ if (!_process_switches(&argc, &argv, dev_dir)) {
+ log_error("Couldn't process command line.");
+ goto out;
+ }
+
+ if (_switches[HELP_ARG]) {
+ switch (_base_command_type) {
+ case STATS_TYPE:
+ if ((cmd = _find_stats_subcommand("help")))
+ goto doit;
+ goto unknown;
+ default:
+ if ((cmd = _find_dmsetup_command("help")))
+ goto doit;
+ goto unknown;
+ }
+ }
+
+ if (_switches[VERSION_ARG]) {
+ switch (_base_command_type) {
+ case STATS_TYPE:
+ if ((cmd = _find_stats_subcommand("version")))
+ goto doit;
+ goto unknown;
+ default:
+ if ((cmd = _find_dmsetup_command("version")))
+ goto doit;
+ goto unknown;
+ }
+ }
+
+ if (!_command) {
+ _usage(stderr);
+ goto out;
+ }
+
+ if (!(cmd = _find_dmsetup_command(_command))) {
+unknown:
+ log_error("Unknown command.");
+ _usage(stderr);
+ goto out;
+ }
+
+ if (argc < cmd->min_args ||
+ (cmd->max_args >= 0 && argc > cmd->max_args)) {
+ log_error("Incorrect number of arguments.");
+ _usage(stderr);
+ goto out;
+ }
+
+ if (!_switches[COLS_ARG] && !strcmp(cmd->name, "splitname"))
+ _switches[COLS_ARG]++;
+
+ if (!strcmp(cmd->name, "stats")) {
+ _switches[COLS_ARG]++;
+ if (!_switches[UNITS_ARG]) {
+ _switches[UNITS_ARG]++;
+ _string_args[UNITS_ARG] = (char *) "h";
+ }
+ }
+
+ if (!strcmp(cmd->name, "mangle"))
+ dm_set_name_mangling_mode(DM_STRING_MANGLING_NONE);
+
+ if (!_process_options(_string_args[OPTIONS_ARG])) {
+ log_error("Couldn't process command line.");
+ goto out;
+ }
+
+#ifdef UDEV_SYNC_SUPPORT
+ if (!_set_up_udev_support(dev_dir))
+ goto_out;
+#endif
+
+ /*
+ * Extract subcommand?
+ * dmsetup <command> <subcommand> [args...]
+ */
+ if (cmd->has_subcommands) {
+ subcommand = argv[0];
+ argc--, argv++;
+ }
+
+ /* Default to success */
+ ret = 0;
+
+ /* When -S is given, store the real command for later and run "info -c" first */
+ if (_switches[SELECT_ARG] && (cmd->repeatable_cmd == 2)) {
+ _selection_cmd = cmd;
+ _switches[COLS_ARG] = 1;
+ if (!(cmd = _find_dmsetup_command("info"))) {
+ log_error(INTERNAL_ERROR "finding dmsetup info command struct.");
+ goto out;
+ }
+ }
+
+ if (_switches[COLS_ARG]) {
+ if (!_report_init(cmd, subcommand))
+ ret = 1;
+ if (ret || !_report)
+ goto_out;
+ }
+
+ if (_switches[COUNT_ARG] && _int_args[COUNT_ARG])
+ _count = (uint64_t)_int_args[COUNT_ARG];
+ else if (_switches[COUNT_ARG] || _switches[INTERVAL_ARG])
+ _count = UINT64_MAX;
+
+ if (_switches[UNITS_ARG]) {
+ _disp_factor = _factor_from_units(_string_args[UNITS_ARG],
+ &_disp_units);
+ if (!_disp_factor) {
+ log_error("Invalid --units argument.");
+ ret = 1;
+ goto out;
+ }
+ }
+
+ /* Start interval timer. */
+ if (_count > 1)
+ if (!_start_timer()) {
+ ret = 1;
+ goto_out;
+ }
+
+doit:
+ multiple_devices = (cmd->repeatable_cmd && argc != 1 &&
+ (argc || (!_switches[UUID_ARG] && !_switches[MAJOR_ARG])));
+
+ do {
+ r = _perform_command_for_all_repeatable_args(cmd, subcommand, argc, argv, NULL, multiple_devices);
+ if (_concise_output_produced) {
+ putchar('\n');
+ fflush(stdout);
+ }
+ if (_report) {
+ /* only output headings for repeating reports */
+ if (_int_args[COUNT_ARG] != 1 && !dm_report_is_empty(_report))
+ dm_report_column_headings(_report);
+ dm_report_output(_report);
+
+ if (_count > 1 && r) {
+ putchar('\n');
+ fflush(stdout);
+ /* wait for --interval and update timestamps */
+ if (!_do_report_wait()) {
+ ret = 1;
+ goto_out;
+ }
+ }
+ }
+
+ if (!r) {
+ ret = 1;
+ goto_out;
+ }
+ } while (--_count);
+
+out:
+ if (_report)
+ dm_report_free(_report);
+
+ if (_dtree)
+ dm_tree_free(_dtree);
+
+ free(_table);
+
+ if (_initial_timestamp)
+ dm_timestamp_destroy(_initial_timestamp);
+
+ return (_switches[HELP_ARG] || _switches[VERSION_ARG]) ? 0 : ret;
+}
diff --git a/libdm/dm-tools/util.h b/libdm/dm-tools/util.h
new file mode 100644
index 0000000..730b903
--- /dev/null
+++ b/libdm/dm-tools/util.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _LVM_UTIL_H
+#define _LVM_UTIL_H
+
+#include <inttypes.h>
+
+#define min(a, b) ({ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ (void) (&_a == &_b); \
+ _a < _b ? _a : _b; })
+
+#define max(a, b) ({ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ (void) (&_a == &_b); \
+ _a > _b ? _a : _b; })
+
+#define is_power_of_2(n) ((n) && !((n) & ((n) - 1)))
+
+#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6)
+#define uninitialized_var(x) x
+#else
+#define uninitialized_var(x) x = x
+#endif
+
+#define KERNEL_VERSION(major, minor, release) (((major) << 16) + ((minor) << 8) + (release))
+
+/* Define some portable printing types */
+#define PRIsize_t "zu"
+#define PRIssize_t "zd"
+#define PRIptrdiff_t "td"
+#define PRIpid_t PRId32
+
+/* For convenience */
+#define FMTsize_t "%" PRIsize_t
+#define FMTssize_t "%" PRIssize_t
+#define FMTptrdiff_t "%" PRIptrdiff_t
+#define FMTpid_t "%" PRIpid_t
+
+#define FMTd8 "%" PRId8
+#define FMTd16 "%" PRId16
+#define FMTd32 "%" PRId32
+#define FMTd64 "%" PRId64
+
+#define FMTi8 "%" PRIi8
+#define FMTi16 "%" PRIi16
+#define FMTi32 "%" PRIi32
+#define FMTi64 "%" PRIi64
+
+#define FMTo8 "%" PRIo8
+#define FMTo16 "%" PRIo16
+#define FMTo32 "%" PRIo32
+#define FMTo64 "%" PRIo64
+
+#define FMTu8 "%" PRIu8
+#define FMTu16 "%" PRIu16
+#define FMTu32 "%" PRIu32
+#define FMTu64 "%" PRIu64
+
+#define FMTx8 "%" PRIx8
+#define FMTx16 "%" PRIx16
+#define FMTx32 "%" PRIx32
+#define FMTx64 "%" PRIx64
+
+#define FMTVGID "%." DM_TO_STRING(ID_LEN) "s"
+
+#endif
diff --git a/tools/Makefile.in b/tools/Makefile.in
index 3785b50..7d9dd93 100644
--- a/tools/Makefile.in
+++ b/tools/Makefile.in
@@ -67,7 +67,6 @@ SOURCES =\
vgsplit.c
SOURCES2 =\
- dmsetup.c \
lvm.c \
lvm2cmd-static.c \
lvm2cmd.c \
@@ -80,17 +79,13 @@ TARGETS =\
lvm \
man-generator
-TARGETS_DM = dmsetup
-
INSTALL_LVM_TARGETS = install_tools_dynamic
-INSTALL_DMSETUP_TARGETS = install_dmsetup_dynamic
+INSTALL_DMSETUP_TARGETS =
INSTALL_CMDLIB_TARGETS = install_cmdlib_dynamic install_cmdlib_include
ifeq ("@STATIC_LINK@", "yes")
TARGETS += lvm.static
- TARGETS_DM += dmsetup.static
INSTALL_LVM_TARGETS += install_tools_static
- INSTALL_DMSETUP_TARGETS += install_dmsetup_static
INSTALL_CMDLIB_TARGETS += install_cmdlib_static
endif
@@ -99,7 +94,7 @@ LIB_VERSION = $(LIB_VERSION_LVM)
CLEAN_TARGETS = liblvm2cmd.$(LIB_SUFFIX) $(TARGETS_DM) \
liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION) lvm-static.o \
- liblvm2cmd-static.a dmsetup.static lvm.static \
+ liblvm2cmd-static.a lvm.static \
$(LDDEPS) .exported_symbols_generated \
cmds.h command-lines-input.h command-count.h man-generator.c
@@ -117,19 +112,7 @@ CFLOW_TARGET = lvm
include $(top_builddir)/make.tmpl
-device-mapper: $(TARGETS_DM)
-
-CFLAGS_dmsetup.o += $(UDEV_CFLAGS) $(EXTRA_EXEC_CFLAGS)
-
-dmsetup: dmsetup.o $(INTERNAL_LIBS)
- @echo " [CC] $@"
- $(Q) $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
- -o $@ $+ $(LIBS) -lm
-
-dmsetup.static: dmsetup.o $(INTERNAL_LIBS)
- @echo " [CC] $@"
- $(Q) $(CC) $(CFLAGS) $(LDFLAGS) -static -L$(interfacebuilddir) \
- -o $@ $+ $(M_LIBS) $(PTHREAD_LIBS) $(STATIC_LIBS) $(LIBS)
+device-mapper:
all: device-mapper
@@ -225,8 +208,7 @@ CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
endif
.PHONY: install_cmdlib_dynamic install_cmdlib_static install_cmdlib_include \
- install_tools_dynamic install_tools_static \
- install_dmsetup_dynamic install_dmsetup_static
+ install_tools_dynamic install_tools_static
install_cmdlib_include: $(srcdir)/lvm2cmd.h
@echo " [INSTALL] $<"
@@ -255,18 +237,6 @@ install_tools_static: lvm.static
@echo " [INSTALL] $<"
$(Q) $(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
-install_dmsetup_dynamic: dmsetup
- @echo " [INSTALL] $<"
- $(Q) $(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
- $(Q) $(LN_S) -f $(<F) $(sbindir)/dmstats
-
-install_dmsetup_static: dmsetup.static
- @echo " [INSTALL] $<"
- $(Q) $(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
- $(Q) $(LN_S) -f $(<F) $(staticdir)/dmstats
-
-install_device-mapper: $(INSTALL_DMSETUP_TARGETS)
-
install_lvm2: $(INSTALL_LVM_TARGETS)
-install: install_lvm2 install_device-mapper
+install: install_lvm2
diff --git a/tools/dmsetup.c b/tools/dmsetup.c
deleted file mode 100644
index 7c5a2b3..0000000
--- a/tools/dmsetup.c
+++ /dev/null
@@ -1,7460 +0,0 @@
-/*
- * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2005-2007 NEC Corporation
- *
- * 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 "tools/tool.h"
-
-#include "base/memory/zalloc.h"
-#include "device_mapper/misc/dm-logging.h"
-
-#include <ctype.h>
-#include <dirent.h>
-#include <sys/wait.h>
-#include <sys/param.h>
-#include <locale.h>
-#include <langinfo.h>
-#include <time.h>
-
-#include <fcntl.h>
-#include <sys/stat.h>
-
-#ifdef UDEV_SYNC_SUPPORT
-# include <sys/types.h>
-# include <sys/ipc.h>
-# include <sys/sem.h>
-# include <libudev.h>
-#endif
-
-/* FIXME Unused so far */
-#undef HAVE_SYS_STATVFS_H
-
-#ifdef HAVE_SYS_STATVFS_H
-# include <sys/statvfs.h>
-#endif
-
-#ifdef HAVE_SYS_IOCTL_H
-# include <sys/ioctl.h>
-#endif
-
-#ifdef HAVE_SYS_TIMERFD_H
-# include <sys/timerfd.h>
-#endif
-
-#ifdef HAVE_TERMIOS_H
-# include <termios.h>
-#endif
-
-#ifdef HAVE_GETOPTLONG
-# include <getopt.h>
-# define GETOPTLONG_FN(a, b, c, d, e) getopt_long((a), (b), (c), (d), (e))
-# define OPTIND_INIT 0
-#else
-struct option {
-};
-extern int optind;
-extern char *optarg;
-# define GETOPTLONG_FN(a, b, c, d, e) getopt((a), (b), (c))
-# define OPTIND_INIT 1
-#endif
-
-#ifndef TEMP_FAILURE_RETRY
-# define TEMP_FAILURE_RETRY(expression) \
- (__extension__ \
- ({ long int __result; \
- do __result = (long int) (expression); \
- while (__result == -1L && errno == EINTR); \
- __result; }))
-#endif
-
-#ifdef __linux__
-# include "libdm/misc/kdev_t.h"
-#else
-# define MAJOR(x) major((x))
-# define MINOR(x) minor((x))
-# define MKDEV(x,y) makedev((x),(y))
-#endif
-
-#define LINE_SIZE 4096
-#define ARGS_MAX 256
-#define LOOP_TABLE_SIZE (PATH_MAX + 255)
-
-#define DEFAULT_DM_DEV_DIR "/dev/"
-
-#define DM_DEV_DIR_ENV_VAR_NAME "DM_DEV_DIR"
-#define DM_UDEV_COOKIE_ENV_VAR_NAME "DM_UDEV_COOKIE"
-
-/* FIXME Should be imported */
-#ifndef DM_MAX_TYPE_NAME
-# define DM_MAX_TYPE_NAME 16
-#endif
-
-/* FIXME Should be elsewhere */
-#define SECTOR_SHIFT 9L
-
-/* program_id used for dmstats-managed statistics regions */
-#define DM_STATS_PROGRAM_ID "dmstats"
-
-/*
- * Basic commands this code implments.
- */
-typedef enum {
- DMSETUP_CMD = 0,
- LOSETUP_CMD = 1,
- DMLOSETUP_CMD = 2,
- DMSTATS_CMD = 3,
- DMSETUP_STATS_CMD = 4,
- DEVMAP_NAME_CMD = 5
-} cmd_name_t;
-
-typedef enum {
- DMSETUP_TYPE = 0,
- LOSETUP_TYPE = 1,
- STATS_TYPE = 2,
- DEVMAP_NAME_TYPE = 3
-} cmd_type_t;
-
-#define DMSETUP_CMD_NAME "dmsetup"
-#define LOSETUP_CMD_NAME "losetup"
-#define DMLOSETUP_CMD_NAME "dmlosetup"
-#define DMSTATS_CMD_NAME "dmstats"
-#define DMSETUP_STATS_CMD_NAME "dmsetup stats"
-#define DEVMAP_NAME_CMD_NAME "devmap_name"
-
-static const struct {
- cmd_name_t command;
- const char name[14];
- cmd_type_t type;
-} _base_commands[] = {
- { DMSETUP_CMD, DMSETUP_CMD_NAME, DMSETUP_TYPE },
- { LOSETUP_CMD, LOSETUP_CMD_NAME, LOSETUP_TYPE },
- { DMLOSETUP_CMD, DMLOSETUP_CMD_NAME, LOSETUP_TYPE },
- { DMSTATS_CMD, DMSTATS_CMD_NAME, STATS_TYPE },
- { DMSETUP_STATS_CMD, DMSETUP_STATS_CMD_NAME, STATS_TYPE },
- { DEVMAP_NAME_CMD, DEVMAP_NAME_CMD_NAME, DEVMAP_NAME_TYPE },
-};
-
-static const int _num_base_commands = DM_ARRAY_SIZE(_base_commands);
-
-/*
- * We have only very simple switches ATM.
- */
-enum {
- READ_ONLY = 0,
- ADD_NODE_ON_CREATE_ARG,
- ADD_NODE_ON_RESUME_ARG,
- ALIAS_ARG,
- ALL_DEVICES_ARG,
- ALL_PROGRAMS_ARG,
- ALL_REGIONS_ARG,
- AREA_ARG,
- AREAS_ARG,
- AREA_SIZE_ARG,
- CONCISE_ARG,
- BOUNDS_ARG,
- CHECKS_ARG,
- CLEAR_ARG,
- COLS_ARG,
- COUNT_ARG,
- DEFERRED_ARG,
- SELECT_ARG,
- EXEC_ARG,
- FILEMAP_ARG,
- FOLLOW_ARG,
- FORCE_ARG,
- FOREGROUND_ARG,
- GID_ARG,
- GROUP_ARG,
- GROUP_ID_ARG,
- HELP_ARG,
- HISTOGRAM_ARG,
- INACTIVE_ARG,
- INTERVAL_ARG,
- LENGTH_ARG,
- MANGLENAME_ARG,
- MAJOR_ARG,
- REGIONS_ARG,
- MINOR_ARG,
- MODE_ARG,
- NAMEPREFIXES_ARG,
- NOFLUSH_ARG,
- NOGROUP_ARG,
- NOHEADINGS_ARG,
- NOLOCKFS_ARG,
- NOOPENCOUNT_ARG,
- NOSUFFIX_ARG,
- NOTABLE_ARG,
- NOTIMESUFFIX_ARG,
- UDEVCOOKIE_ARG,
- NOMONITOR_ARG,
- NOUDEVRULES_ARG,
- NOUDEVSYNC_ARG,
- OPTIONS_ARG,
- PRECISE_ARG,
- PROGRAM_ID_ARG,
- RAW_ARG,
- READAHEAD_ARG,
- REGION_ARG,
- REGION_ID_ARG,
- RELATIVE_ARG,
- RETRY_ARG,
- ROWS_ARG,
- SEPARATOR_ARG,
- SETUUID_ARG,
- SHOWKEYS_ARG,
- SORT_ARG,
- START_ARG,
- TABLE_ARG,
- TARGET_ARG,
- SEGMENTS_ARG,
- TREE_ARG,
- UID_ARG,
- UNBUFFERED_ARG,
- UNITS_ARG,
- UNQUOTED_ARG,
- USER_DATA_ARG,
- UUID_ARG,
- VERBOSE_ARG,
- VERIFYUDEV_ARG,
- VERSION_ARG,
- YES_ARG,
- NUM_SWITCHES
-};
-
-typedef enum {
- DR_TASK = 1,
- DR_INFO = 2,
- DR_DEPS = 4,
- DR_TREE = 8, /* Complete dependency tree required */
- DR_NAME = 16,
- DR_STATS = 32, /* Requires populated stats handle. */
- DR_STATS_META = 64, /* Requires listed stats handle. */
-} report_type_t;
-
-typedef enum {
- DN_DEVNO, /* Major and minor number pair */
- DN_BLK, /* Block device name (e.g. dm-0) */
- DN_MAP /* Map name (for dm devices only, equal to DN_BLK otherwise) */
-} dev_name_t;
-
-static cmd_name_t _base_command = DMSETUP_CMD; /* Default command is 'dmsetup' */
-static cmd_type_t _base_command_type = DMSETUP_TYPE;
-static int _switches[NUM_SWITCHES];
-static int _int_args[NUM_SWITCHES];
-static char *_string_args[NUM_SWITCHES];
-static int _num_devices;
-static char *_uuid;
-static char *_table;
-static char *_target;
-static char *_command_to_exec; /* --exec <command> */
-static const char *_command; /* dmsetup <command> */
-static uint32_t _read_ahead_flags;
-static uint32_t _udev_cookie;
-static int _udev_only;
-static struct dm_tree *_dtree;
-static struct dm_report *_report;
-static report_type_t _report_type;
-static dev_name_t _dev_name_type;
-static uint64_t _count = 1; /* count of repeating reports */
-static struct dm_timestamp *_initial_timestamp = NULL;
-static uint64_t _disp_factor = 512; /* display sizes in sectors */
-static char _disp_units = 's';
-const char *_program_id = DM_STATS_PROGRAM_ID; /* program_id used for reports. */
-static uint64_t _statstype = 0; /* stats objects to report */
-static int _concise_output_produced = 0; /* Was any concise output already printed? */
-struct command;
-static const struct command *_selection_cmd = NULL; /* Command to run against each device select with -S */
-
-/* string names for stats object types */
-const char *_stats_types[] = {
- "all",
- "area",
- "region",
- "group",
- NULL
-};
-
-/* report timekeeping */
-static struct dm_timestamp *_cycle_timestamp = NULL;
-#ifndef HAVE_SYS_TIMERFD_H
-static struct dm_timestamp *_start_timestamp = NULL;
-#else /* HAVE_SYS_TIMERFD_H */
-static int _timer_fd = -1; /* timerfd file descriptor. */
-#endif /* !HAVE_SYS_TIMERFD_H */
-
-static uint64_t _interval = 0; /* configured interval in nsecs */
-static uint64_t _new_interval = 0; /* flag top-of-interval */
-static uint64_t _last_interval = 0; /* approx. measured interval in nsecs */
-
-/* Invalid fd value used to signal end-of-reporting. */
-#define TIMER_STOPPED (-2)
-
-#define NSEC_PER_USEC UINT64_C(1000)
-#define NSEC_PER_MSEC UINT64_C(1000000)
-#define NSEC_PER_SEC UINT64_C(1000000000)
-
-/*
- * Commands
- */
-
-#define CMD_ARGS const struct command *cmd, const char *subcommand, int argc, char **argv, struct dm_names *names, int multiple_devices
-typedef int (*command_fn) (CMD_ARGS);
-
-struct command {
- const char *name;
- const char *help;
- int min_args;
- int max_args;
- int repeatable_cmd; /* Repeat to process device list? */
- /* 2 means --select is also supported */
- int has_subcommands; /* Command implements sub-commands. */
- command_fn fn;
-};
-
-static int _parse_line(struct dm_task *dmt, char *buffer, const char *file,
- int line)
-{
- char ttype[LINE_SIZE], *ptr, *comment;
- unsigned long long start, size;
- int n;
-
- /* trim trailing space */
- for (ptr = buffer + strlen(buffer) - 1; ptr >= buffer; ptr--)
- if (!isspace((int) *ptr))
- break;
- ptr++;
- *ptr = '\0';
-
- /* trim leading space */
- for (ptr = buffer; *ptr && isspace((int) *ptr); ptr++)
- ;
-
- if (!*ptr || *ptr == '#')
- return 1;
-
- if (sscanf(ptr, "%llu %llu %s %n",
- &start, &size, ttype, &n) < 3) {
- log_error("Invalid format on line %d of table %s.", line, file);
- return 0;
- }
-
- ptr += n;
- if ((comment = strchr(ptr, (int) '#')))
- *comment = '\0';
-
- if (!dm_task_add_target(dmt, start, size, ttype, ptr))
- return_0;
-
- return 1;
-}
-
-/* Parse multiple lines of table */
-static int _parse_table_lines(struct dm_task *dmt)
-{
- char *pos = _table, *next_pos;
- int line = 0;
-
- do {
- /* Identify and terminate each line */
- if ((next_pos = strchr(_table, '\n')))
- *next_pos++ = '\0';
- if (!_parse_line(dmt, pos, "", ++line))
- return_0;
- } while ((pos = next_pos));
-
- return 1;
-}
-
-static int _parse_file(struct dm_task *dmt, const char *file)
-{
- char *buffer = NULL;
- size_t buffer_size = 0;
- FILE *fp;
- int r = 0, line = 0;
-
- /* Table on cmdline or from stdin with --concise */
- if (_table)
- return _parse_table_lines(dmt);
-
- /* OK for empty stdin */
- if (file) {
- if (!(fp = fopen(file, "r"))) {
- log_error("Couldn't open '%s' for reading.", file);
- return 0;
- }
- } else
- fp = stdin;
-
-#ifndef HAVE_GETLINE
- buffer_size = LINE_SIZE;
- if (!(buffer = malloc(buffer_size))) {
- log_error("Failed to malloc line buffer.");
- return 0;
- }
-
- while (fgets(buffer, (int) buffer_size, fp))
-#else
- while (getline(&buffer, &buffer_size, fp) > 0)
-#endif
- if (!_parse_line(dmt, buffer, file ? : "on stdin", ++line))
- goto_out;
-
- r = 1;
-
-out:
- memset(buffer, 0, buffer_size);
-#ifndef HAVE_GETLINE
- free(buffer);
-#else
- free(buffer);
-#endif
- if (file && fclose(fp))
- log_sys_debug("fclose", file);
-
- return r;
-}
-
-struct dm_split_name {
- char *subsystem;
- char *vg_name;
- char *lv_name;
- char *lv_layer;
-};
-
-struct dmsetup_report_obj {
- struct dm_task *task;
- struct dm_info *info;
- struct dm_task *deps_task;
- struct dm_tree_node *tree_node;
- struct dm_split_name *split_name;
- struct dm_stats *stats;
-};
-
-static int _task_run(struct dm_task *dmt)
-{
- int r;
- uint64_t delta;
- struct dm_timestamp *ts;
-
- if (_initial_timestamp)
- dm_task_set_record_timestamp(dmt);
-
- r = dm_task_run(dmt);
-
- if (_initial_timestamp &&
- (ts = dm_task_get_ioctl_timestamp(dmt))) {
- delta = dm_timestamp_delta(ts, _initial_timestamp);
- log_debug("Timestamp: %7" PRIu64 ".%09" PRIu64 " seconds",
- delta / NSEC_PER_SEC, delta % NSEC_PER_SEC);
- }
-
- return r;
-}
-
-static struct dm_task *_get_deps_task(int major, int minor)
-{
- struct dm_task *dmt;
- struct dm_info info;
-
- if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
- return_NULL;
-
- if (!dm_task_set_major(dmt, major) ||
- !dm_task_set_minor(dmt, minor))
- goto_bad;
-
- if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
- goto_bad;
-
- if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
- goto_bad;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_bad;
-
- if (!_task_run(dmt))
- goto_bad;
-
- if (!dm_task_get_info(dmt, &info))
- goto_bad;
-
- if (!info.exists)
- goto_bad;
-
- return dmt;
-
-bad:
- dm_task_destroy(dmt);
- return NULL;
-}
-
-static char *_extract_uuid_prefix(const char *uuid, const int separator)
-{
- char *ptr = NULL;
- char *uuid_prefix = NULL;
- size_t len;
-
- if (uuid)
- ptr = strchr(uuid, separator);
-
- len = ptr ? ptr - uuid : 0;
- if (!(uuid_prefix = malloc(len + 1))) {
- log_error("Failed to allocate memory to extract uuid prefix.");
- return NULL;
- }
-
- if (uuid)
- memcpy(uuid_prefix, uuid, len);
-
- uuid_prefix[len] = '\0';
-
- return uuid_prefix;
-}
-
-static struct dm_split_name *_get_split_name(const char *uuid, const char *name,
- int separator)
-{
- struct dm_split_name *split_name;
-
- if (!(split_name = malloc(sizeof(*split_name)))) {
- log_error("Failed to allocate memory to split device name "
- "into components.");
- return NULL;
- }
-
- if (!(split_name->subsystem = _extract_uuid_prefix(uuid, separator))) {
- free(split_name);
- return_NULL;
- }
-
- split_name->vg_name = split_name->lv_name =
- split_name->lv_layer = (char *) "";
-
- if (!strcmp(split_name->subsystem, "LVM") &&
- (!(split_name->vg_name = strdup(name)) ||
- !dm_split_lvm_name(NULL, NULL, &split_name->vg_name,
- &split_name->lv_name, &split_name->lv_layer)))
- log_error("Failed to allocate memory to split LVM name "
- "into components.");
-
- return split_name;
-}
-
-static void _destroy_split_name(struct dm_split_name *split_name)
-{
- /*
- * lv_name and lv_layer are allocated within the same block
- * of memory as vg_name so don't need to be freed separately.
- */
- if (!strcmp(split_name->subsystem, "LVM"))
- free(split_name->vg_name);
-
- free(split_name->subsystem);
- free(split_name);
-}
-
-/*
- * Stats clock:
- *
- * Use either Linux timerfds or usleep to implement the reporting
- * interval wait.
- *
- * _start_timer() - Start the timer running.
- * _do_timer_wait() - Wait until the beginning of the next interval.
- *
- * _update_interval_times() - Update timestamps and interval estimate.
- */
-
-/*
- * Return the current interval number counting upwards from one.
- */
-static uint64_t _interval_num(void)
-{
- uint64_t count_arg = _int_args[COUNT_ARG];
- return ((uint64_t) _int_args[COUNT_ARG] - _count) + !!count_arg;
-}
-
-#ifdef HAVE_SYS_TIMERFD_H
-static int _start_timerfd_timer(void)
-{
- struct itimerspec interval_timer;
- time_t secs;
- long nsecs;
-
- log_debug("Using timerfd for interval timekeeping.");
-
- /* timer running? */
- if (_timer_fd != -1)
- return 1;
-
- memset(&interval_timer, 0, sizeof(interval_timer));
-
- /* Use CLOCK_MONOTONIC to avoid warp on RTC adjustments. */
- if ((_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)) < 0) {
- log_error("Could not create timer: %s", strerror(errno));
- return 0;
- }
-
- secs = (time_t) _interval / NSEC_PER_SEC;
- nsecs = (long) _interval % NSEC_PER_SEC;
-
- /* Must set interval and value to create an armed periodic timer. */
- interval_timer.it_interval.tv_sec = secs;
- interval_timer.it_interval.tv_nsec = nsecs;
- interval_timer.it_value.tv_sec = secs;
- interval_timer.it_value.tv_nsec = nsecs;
-
- log_debug("Setting interval timer to: " FMTu64 "s %ldns", (uint64_t)secs, nsecs);
- if (timerfd_settime(_timer_fd, 0, &interval_timer, NULL)) {
- log_error("Could not set interval timer: %s", strerror(errno));
- return 0;
- }
- return 1;
-}
-
-static int _do_timerfd_wait(void)
-{
- uint64_t expired;
- ssize_t bytes;
-
- if (_timer_fd < 0)
- return_0;
-
- /* read on timerfd returns a uint64_t in host byte order. */
- bytes = read(_timer_fd, &expired, sizeof(expired));
-
- if (bytes < 0) {
- /* EBADF from invalid timerfd or EINVAL from too small buffer. */
- log_error("Interval timer wait failed: %s.", strerror(errno));
- return 0;
- }
-
- /* read(2) on a timerfd descriptor is guaranteed to return 8 bytes. */
- if (bytes != 8)
- log_error("Unexpected byte count on timerfd read: " FMTssize_t ".", bytes);
-
- /* FIXME: attempt to rebase clock? */
- if (expired > 1)
- log_warn("WARNING: Try increasing --interval ("FMTu64
- " missed timer events).", expired - 1);
-
- /* Signal that a new interval has begun. */
- _new_interval = 1;
-
- /* Final interval? */
- if (_count == 2) {
- if (close(_timer_fd))
- stack;
- /* Tell _update_interval_times() to shut down. */
- _timer_fd = TIMER_STOPPED;
- }
-
- return 1;
-}
-
-static int _start_timer(void)
-{
- return _start_timerfd_timer();
-}
-
-static int _do_timer_wait(void)
-{
- return _do_timerfd_wait();
-}
-
-static int _timer_running(void)
-{
- return ((_timer_fd != TIMER_STOPPED) || _cycle_timestamp);
-}
-
-#else /* !HAVE_SYS_TIMERFD_H */
-static int _start_usleep_timer(void)
-{
- log_debug("Using usleep for interval timekeeping.");
- _start_timestamp = dm_timestamp_alloc();
- dm_timestamp_get(_start_timestamp);
- return 1;
-}
-
-static int _do_usleep_wait(void)
-{
- static struct dm_timestamp *_now = NULL;
- uint64_t this_interval;
- int64_t delta_t;
-
- /*
- * Report clock: compensate for time spent in userspace and stats
- * message ioctls by keeping track of the last wake time and
- * adjusting the sleep interval accordingly.
- */
- if (!_now) {
- if (!(_now = dm_timestamp_alloc()))
- return_0;
- dm_timestamp_get(_now);
- this_interval = _interval;
- log_error("Using "FMTu64" as first interval.", this_interval);
- } else {
- dm_timestamp_get(_now);
- delta_t = dm_timestamp_delta(_now, _start_timestamp);
- log_debug("Interval timer drift: "FMTd64".",
- (delta_t % _interval));
-
- /* FIXME: usleep timer drift over large counts. */
-
- /* adjust for time spent populating and reporting */
- this_interval = _interval - (delta_t % _interval);
- log_debug("Using "FMTu64" as interval.", this_interval);
- }
-
- /* Signal that a new interval has begun. */
- _new_interval = 1;
-
- if (usleep(this_interval / NSEC_PER_USEC)) {
- if (errno == EINTR)
- log_error("Report interval interrupted by signal.");
- else if (errno == EINVAL)
- log_error("Report interval too short.");
- else
- stack; /* other reason */
- return 0;
- }
-
- if (_count == 2) {
- dm_timestamp_destroy(_start_timestamp);
- dm_timestamp_destroy(_now);
- _start_timestamp = _now = NULL;
- }
-
- return 1;
-}
-
-static int _start_timer(void)
-{
- return _start_usleep_timer();
-}
-
-static int _do_timer_wait(void)
-{
- return _do_usleep_wait();
-}
-
-static int _timer_running(void)
-{
- return (_start_timestamp != NULL);
-}
-
-#endif /* HAVE_SYS_TIMERFD_H */
-
-static int _update_interval_times(void)
-{
- static struct dm_timestamp *this_timestamp = NULL;
- uint64_t delta_t, interval_num = _interval_num();
- int r = 1;
-
- /*
- * Clock shutdown for exit - nothing to do.
- */
- if (!_timer_running())
- goto out;
-
- /* clock is running */
- r = 0;
-
- /*
- * Current timestamp. If _new_interval is set this is used as
- * the new cycle start timestamp.
- */
- if (!this_timestamp) {
- if (!(this_timestamp = dm_timestamp_alloc()))
- return_0;
- }
-
- /*
- * Take cycle timstamp as close as possible to ioctl return.
- *
- * FIXME: use per-region timestamp deltas for interval estimate.
- */
- if (!dm_timestamp_get(this_timestamp))
- goto_out;
-
- /*
- * Stats clock: maintain a single timestamp taken just after the
- * call to dm_stats_populate() and take a delta between the current
- * and last value to determine the sampling interval.
- *
- * A new interval is started when the _new_interval flag is set
- * on return from _do_report_wait().
- *
- * The first interval is treated as a special case: since the
- * time since the last clear of the counters is unknown (no
- * previous timestamp exists) the duration is assumed to be the
- * configured value.
- */
- if (_cycle_timestamp)
- /* Current delta_t: time from start of cycle to now. */
- delta_t = dm_timestamp_delta(this_timestamp, _cycle_timestamp);
- else {
- _cycle_timestamp = dm_timestamp_alloc();
- if (!_cycle_timestamp) {
- log_error("Could not allocate timestamp object.");
- goto out;
- }
-
- /* Pretend we have the configured interval. */
- delta_t = _interval;
-
- /* start the first cycle */
- log_debug("Beginning first interval.");
- _new_interval = 1;
- }
-
- log_debug("Interval #%-4"PRIu64" time delta: %12"PRIu64"ns.",
- interval_num, delta_t);
-
- if (_new_interval) {
- /* Update timestamp and interval and clear _new_interval */
- dm_timestamp_copy(_cycle_timestamp, this_timestamp);
- _last_interval = delta_t;
- _new_interval = 0;
-
- /*
- * Log interval duration and current error.
- */
- log_debug("Interval #%-5"PRIu64" current err: %12"PRIi64"ns.",
- interval_num, ((int64_t)_last_interval - (int64_t)_interval));
- log_debug("End interval #%-9"PRIu64" duration: %12"PRIu64"ns.",
- interval_num, _last_interval);
- }
-
- r = 1;
-
-out:
- /* timer stopped or never started */
- if (!r || !_timer_running()) {
- /* The _cycle_timestamp has not yet been allocated if we
- * fail to obtain this_timestamp on the first interval.
- */
- if (_cycle_timestamp)
- dm_timestamp_destroy(_cycle_timestamp);
- dm_timestamp_destroy(this_timestamp);
-
- /* Clear timestamp pointers to signal shutdown. */
- _cycle_timestamp = this_timestamp = NULL;
- }
- return r;
-}
-
-static int _display_info_cols(struct dm_task *dmt, struct dm_info *info)
-{
- struct dmsetup_report_obj obj;
- uint64_t walk_flags = _statstype;
- int r = 0;
- int selected;
- char *device_name;
-
- obj.task = dmt;
- obj.info = info;
- obj.deps_task = NULL;
- obj.split_name = NULL;
- obj.stats = NULL;
-
- if (_report_type & DR_TREE)
- if (!(obj.tree_node = dm_tree_find_node(_dtree, info->major, info->minor))) {
- log_error("Cannot find node %d:%d.", info->major, info->minor);
- goto out;
- }
-
- if (_report_type & DR_DEPS)
- if (!(obj.deps_task = _get_deps_task(info->major, info->minor))) {
- log_error("Cannot get deps for %d:%d.", info->major, info->minor);
- goto out;
- }
-
- if (_report_type & DR_NAME)
- if (!(obj.split_name = _get_split_name(dm_task_get_uuid(dmt),
- dm_task_get_name(dmt), '-')))
- goto_out;
-
- if (!(_report_type & (DR_STATS | DR_STATS_META))) {
- /*
- * If _selection_cmd is set we are applying -S to some other command, so suppress
- * output and run that other command if the device matches the criteria.
- */
- if (!dm_report_object_is_selected(_report, &obj, _selection_cmd ? 0 : 1, &selected))
- goto_out;
- if (_selection_cmd && selected) {
- device_name = (char*) dm_task_get_name(dmt);
- if (!_selection_cmd->fn(_selection_cmd, NULL, 1, &device_name, NULL, 1))
- goto_out;
- }
- r = 1;
- goto out;
- }
-
- /*
- * Obtain statistics for the current reporting object and set
- * the interval estimate used for stats rate conversion.
- */
- if (_report_type & DR_STATS) {
- if (!(obj.stats = dm_stats_create(DM_STATS_PROGRAM_ID)))
- goto_out;
-
- if (!dm_stats_get_nr_regions(obj.stats)) {
- log_debug("Skipping %s with no regions.", dm_task_get_name(dmt));
- r = 1;
- goto out;
- }
-
- dm_stats_bind_devno(obj.stats, info->major, info->minor);
-
- if (!dm_stats_populate(obj.stats, _program_id, DM_STATS_REGIONS_ALL))
- goto_out;
-
- /* Update timestamps and handle end-of-interval accounting. */
- _update_interval_times();
-
- log_debug("Adjusted sample interval duration: %12"PRIu64"ns.", _last_interval);
- /* use measured approximation for calculations */
- dm_stats_set_sampling_interval_ns(obj.stats, _last_interval);
- } else if (!obj.stats && (_report_type & DR_STATS_META)
- /* Only a dm_stats_list is needed for DR_STATS_META reports. */
- && !(_report_type & DR_STATS)) {
- if (!(obj.stats = dm_stats_create(DM_STATS_PROGRAM_ID)))
- goto_out;
-
- dm_stats_bind_devno(obj.stats, info->major, info->minor);
-
- if (!dm_stats_list(obj.stats, _program_id))
- goto_out;
-
- /* No regions to report is not an error */
- if (!dm_stats_get_nr_regions(obj.stats))
- goto out;
- }
-
- /* group report with no groups? */
- if ((walk_flags == DM_STATS_WALK_GROUP)
- && !dm_stats_get_nr_groups(obj.stats))
- goto out;
-
- dm_stats_walk_init(obj.stats, walk_flags);
- dm_stats_walk_do(obj.stats) {
- if (!dm_report_object(_report, &obj))
- goto_out;
- dm_stats_walk_next(obj.stats);
- } dm_stats_walk_while(obj.stats);
-
- r = 1;
-
-out:
- if (obj.deps_task)
- dm_task_destroy(obj.deps_task);
- if (obj.split_name)
- _destroy_split_name(obj.split_name);
- if (obj.stats)
- dm_stats_destroy(obj.stats);
- return r;
-}
-
-static void _display_info_long(struct dm_task *dmt, struct dm_info *info)
-{
- const char *uuid;
- uint32_t read_ahead;
-
- printf("Name: %s\n", dm_task_get_name(dmt));
-
- printf("State: %s%s%s\n",
- info->suspended ? "SUSPENDED" : "ACTIVE",
- info->read_only ? " (READ-ONLY)" : "",
- info->deferred_remove ? " (DEFERRED REMOVE)" : "");
-
- /* FIXME Old value is being printed when it's being changed. */
- if (dm_task_get_read_ahead(dmt, &read_ahead))
- printf("Read Ahead: %" PRIu32 "\n", read_ahead);
-
- if (!info->live_table && !info->inactive_table)
- printf("Tables present: None\n");
- else
- printf("Tables present: %s%s%s\n",
- info->live_table ? "LIVE" : "",
- info->live_table && info->inactive_table ? " & " : "",
- info->inactive_table ? "INACTIVE" : "");
-
- if (info->open_count != -1)
- printf("Open count: %d\n", info->open_count);
-
- printf("Event number: %" PRIu32 "\n", info->event_nr);
- printf("Major, minor: %d, %d\n", info->major, info->minor);
-
- if (info->target_count != -1)
- printf("Number of targets: %d\n", info->target_count);
-
- if ((uuid = dm_task_get_uuid(dmt)) && *uuid)
- printf("UUID: %s\n", uuid);
-
- putchar('\n');
-}
-
-static int _display_info(struct dm_task *dmt)
-{
- struct dm_info info;
- int r = 1;
-
- if (!dm_task_get_info(dmt, &info))
- return_0;
-
- if (!info.exists) {
- log_error("Device does not exist.");
- return 0;
- }
-
- if (!_switches[COLS_ARG])
- _display_info_long(dmt, &info);
- else
- r = _display_info_cols(dmt, &info);
-
- return r;
-}
-
-static int _set_task_device(struct dm_task *dmt, const char *name, int optional)
-{
- if (name) {
- if (!dm_task_set_name(dmt, name))
- return_0;
- } else if (_switches[UUID_ARG]) {
- if (!dm_task_set_uuid(dmt, _uuid))
- return_0;
- } else if (_switches[MAJOR_ARG] && _switches[MINOR_ARG]) {
- if (!dm_task_set_major(dmt, _int_args[MAJOR_ARG]) ||
- !dm_task_set_minor(dmt, _int_args[MINOR_ARG]))
- return_0;
- } else if (!optional) {
- log_error("No device specified.");
- return 0;
- }
-
- return 1;
-}
-
-static int _set_task_add_node(struct dm_task *dmt)
-{
- if (!dm_task_set_add_node(dmt, DEFAULT_DM_ADD_NODE))
- return_0;
-
- if (_switches[ADD_NODE_ON_RESUME_ARG] &&
- !dm_task_set_add_node(dmt, DM_ADD_NODE_ON_RESUME))
- return_0;
-
- if (_switches[ADD_NODE_ON_CREATE_ARG] &&
- !dm_task_set_add_node(dmt, DM_ADD_NODE_ON_CREATE))
- return_0;
-
- return 1;
-}
-
-static int _load(CMD_ARGS)
-{
- int r = 0;
- struct dm_task *dmt;
- const char *file = NULL;
- const char *name = NULL;
-
- if (_switches[NOTABLE_ARG]) {
- log_error("--notable only available when creating new device.");
- return 0;
- }
-
- if (!_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
- if (!argc) {
- log_error("Please specify device.");
- return 0;
- }
- name = argv[0];
- argc--;
- argv++;
- } else if (argc > 1) {
- log_error("Too many command line arguments.");
- return 0;
- }
-
- if (argc == 1)
- file = argv[0];
-
- if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
- return_0;
-
- if (!_set_task_device(dmt, name, 0))
- goto_out;
-
- if (!_switches[NOTABLE_ARG] && !_parse_file(dmt, file))
- goto_out;
-
- if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
- goto_out;
-
- if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
- goto_out;
-
- if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
- goto_out;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- if (!_task_run(dmt))
- goto_out;
-
- r = 1;
-
- if (_switches[VERBOSE_ARG])
- r = _display_info(dmt);
-
-out:
- dm_task_destroy(dmt);
-
- return r;
-}
-
-static int _create_one_device(const char *name, const char *file)
-{
- int r = 0;
- struct dm_task *dmt;
- uint32_t cookie = 0;
- uint16_t udev_flags = 0;
-
- if (!(dmt = dm_task_create(DM_DEVICE_CREATE)))
- return_0;
-
- if (!dm_task_set_name(dmt, name))
- goto_out;
-
- if (_switches[UUID_ARG] && !dm_task_set_uuid(dmt, _uuid))
- goto_out;
-
- if (!_switches[NOTABLE_ARG] && !_parse_file(dmt, file))
- goto_out;
-
- if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
- goto_out;
-
- if (_switches[MAJOR_ARG] && !dm_task_set_major(dmt, _int_args[MAJOR_ARG]))
- goto_out;
-
- if (_switches[MINOR_ARG] && !dm_task_set_minor(dmt, _int_args[MINOR_ARG]))
- goto_out;
-
- if (_switches[UID_ARG] && !dm_task_set_uid(dmt, _int_args[UID_ARG]))
- goto_out;
-
- if (_switches[GID_ARG] && !dm_task_set_gid(dmt, _int_args[GID_ARG]))
- goto_out;
-
- if (_switches[MODE_ARG] && !dm_task_set_mode(dmt, _int_args[MODE_ARG]))
- goto_out;
-
- if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
- goto_out;
-
- if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
- goto_out;
-
- if (_switches[READAHEAD_ARG] &&
- !dm_task_set_read_ahead(dmt, _int_args[READAHEAD_ARG],
- _read_ahead_flags))
- goto_out;
-
- if (_switches[NOTABLE_ARG])
- dm_udev_set_sync_support(0);
-
- if (_switches[NOUDEVRULES_ARG])
- udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
- DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- if (!_set_task_add_node(dmt))
- goto_out;
-
- if (_udev_cookie)
- cookie = _udev_cookie;
-
- if (_udev_only)
- udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
-
- if (!dm_task_set_cookie(dmt, &cookie, udev_flags) ||
- !_task_run(dmt))
- goto_out;
-
- r = 1;
-
-out:
- if (!_udev_cookie)
- (void) dm_udev_wait(cookie);
-
- if (r && _switches[VERBOSE_ARG])
- r = _display_info(dmt);
-
- dm_task_destroy(dmt);
-
- return r;
-}
-
-#define DEFAULT_BUF_SIZE 4096
-
-static char *_slurp_stdin(void)
-{
- char *buf, *pos;
- size_t bufsize = DEFAULT_BUF_SIZE;
- size_t total = 0;
- ssize_t n = 0;
-
- if (!(buf = malloc(bufsize))) {
- log_error("Buffer memory allocation failed.");
- return NULL;
- }
-
- pos = buf;
- do {
- do
- n = read(STDIN_FILENO, pos, (size_t) bufsize - total - 1);
- while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
-
- if (n < 0) {
- log_error("Read from stdin aborted: %s", strerror(errno));
- free(buf);
- return NULL;
- }
-
- if (!n)
- break;
-
- total += n;
- pos += n;
- if (total == bufsize - 1) {
- bufsize *= 2;
- if (!(buf = realloc(buf, bufsize))) {
- log_error("Buffer memory extension to %" PRIsize_t " bytes failed.", bufsize);
- return NULL;
- }
- }
- } while (1);
-
- buf[total] = '\0';
-
- return buf;
-}
-
-static int _create_concise(const struct command *cmd, int argc, char **argv)
-{
- char *concise_format;
- char *c, *n;
- char *fields[5] = { NULL }; /* name,uuid,minor,flags,table */
- int f = 0;
-
- if (_switches[TABLE_ARG] || _switches[MINOR_ARG] || _switches[UUID_ARG] ||
- _switches[NOTABLE_ARG] || _switches[INACTIVE_ARG]){
- log_error("--concise is incompatible with --[no]table, --minor, --uuid and --inactive.");
- return 0;
- }
-
- if (argc)
- concise_format = argv[0];
- else if (!(concise_format = _slurp_stdin()))
- return_0;
-
- /* Work through input string c, parsing into sets of 5 fields. */
- /* Strip out any characters quoted by backslashes in-place. */
- /* Read characters from c and prepare them in situ for final processing at n */
- c = n = fields[f] = concise_format;
-
- while (*c) {
- /* Quoted character? Skip past quote. */
- if (*c == '\\') {
- if (!*(++c)) {
- log_error("Backslash must be followed by another character at end of string.");
- *n = '\0';
- log_error("Parsed %d fields: name: %s uuid: %s minor: %s flags: %s table: %s",
- f + 1, fields[0], fields[1], fields[2], fields[3], fields[4]);
- goto out;
- }
-
- /* Don't interpret next character */
- *n++ = *c++;
-
- continue;
- }
-
- /* Comma marking end of field? */
- if (*c == ',' && f < 4) {
- /* Terminate string */
- *n++ = '\0', c++;
-
- /* Store start of next field */
- fields[++f] = n;
-
- /* Skip any whitespace after field-separating commas */
- while(isspace(*c))
- c++;
-
- continue;
- }
-
- /* Comma marking end of a table line? */
- if (*c == ',' && f >= 4) {
- /* Replace comma with newline to match standard table input format */
- *n++ = '\n', c++;
-
- continue;
- }
-
- /* Semi-colon marking end of device? */
- if (*c == ';' || *(c + 1) == '\0') {
- /* End of input? */
- if (*c != ';')
- /* Copy final character */
- *n++ = *c;
-
- /* Terminate string */
- *n++ = '\0', c++;
-
- if (f != 4) {
- log_error("Five comma-separated fields are required for each device");
- log_error("Parsed %d fields: name: %s uuid: %s minor: %s flags: %s table: %s",
- f + 1, fields[0], fields[1], fields[2], fields[3], fields[4]);
- goto out;
- }
-
- /* Set up parameters the same way as when specified directly on command line */
- if (*fields[1]) {
- _switches[UUID_ARG] = 1;
- _uuid = fields[1];
- }
-
- if (*fields[2]) {
- _switches[MINOR_ARG] = 1;
- _int_args[MINOR_ARG] = atoi(fields[2]);
- }
-
- if (!strcmp(fields[3], "ro"))
- _switches[READ_ONLY] = 1;
- else if (*fields[3] && strcmp(fields[3], "rw")) {
- log_error("Invalid flags parameter '%s' must be 'ro' or 'rw' or empty.", fields[3]);
- _uuid = NULL;
- goto out;
- }
-
- _table = fields[4];
-
- /* Create the device */
- if (!_create_one_device(fields[0], NULL)) {
- _uuid = _table = NULL;
- goto out;
- }
-
- /* Clear parameters ready for any further devices */
- _switches[UUID_ARG] = 0;
- _switches[MINOR_ARG] = 0;
- _switches[READ_ONLY] = 0;
- _uuid = _table = NULL;
-
- f = 0;
- fields[0] = n;
- fields[1] = fields[2] = fields[3] = fields[4] = NULL;
-
- /* Skip any whitespace after semi-colons */
- while(isspace(*c))
- c++;
-
- continue;
- }
-
- /* Normal character */
- *n++ = *c++;
- }
-
- if (fields[0] != n) {
- *n = '\0';
- log_error("Incomplete entry: five comma-separated fields are required for each device");
- log_error("Parsed %d fields: name: %s uuid: %s minor: %s flags: %s table: %s",
- f + 1, fields[0], fields[1], fields[2], fields[3], fields[4]);
- goto out;
- }
-
- return 1;
-
-out:
- if (!argc)
- free(concise_format);
-
- return 0;
-}
-
-static int _create(CMD_ARGS)
-{
- const char *name;
- const char *file = NULL;
-
- if (_switches[CONCISE_ARG]) {
- if (argc > 1) {
- log_error("dmsetup create --concise takes at most one argument");
- return 0;
- }
- return _create_concise(cmd, argc, argv);
- }
-
- if (!argc) {
- log_error("Please provide a name for the new device.");
- return 0;
- }
-
- name = argv[0];
- if (argc == 2)
- file = argv[1];
-
- return _create_one_device(name, file);
-}
-
-static int _do_rename(const char *name, const char *new_name, const char *new_uuid) {
- int r = 0;
- struct dm_task *dmt;
- uint32_t cookie = 0;
- uint16_t udev_flags = 0;
-
- if (!(dmt = dm_task_create(DM_DEVICE_RENAME)))
- return_0;
-
- /* FIXME Kernel doesn't support uuid or device number here yet */
- if (!_set_task_device(dmt, name, 0))
- goto_out;
-
- if (new_uuid) {
- if (!dm_task_set_newuuid(dmt, new_uuid))
- goto_out;
- } else if (!new_name || !dm_task_set_newname(dmt, new_name))
- goto_out;
-
- if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
- goto_out;
-
- if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
- goto_out;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- if (_switches[NOUDEVRULES_ARG])
- udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
- DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
-
- if (_udev_cookie)
- cookie = _udev_cookie;
-
- if (_udev_only)
- udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
-
- if (!dm_task_set_cookie(dmt, &cookie, udev_flags) ||
- !_task_run(dmt))
- goto_out;
-
- r = 1;
-
-out:
- if (!_udev_cookie)
- (void) dm_udev_wait(cookie);
-
- dm_task_destroy(dmt);
-
- return r;
-}
-
-static int _rename(CMD_ARGS)
-{
- const char *name = (argc == 2) ? argv[0] : NULL;
-
- return _switches[SETUUID_ARG] ? _do_rename(name, NULL, argv[argc - 1]) :
- _do_rename(name, argv[argc - 1], NULL);
-
-}
-
-static int _message(CMD_ARGS)
-{
- int r = 0, i;
- size_t sz = 1;
- struct dm_task *dmt;
- char *str;
- const char *response;
- uint64_t sector;
- char *endptr;
-
- if (!(dmt = dm_task_create(DM_DEVICE_TARGET_MSG)))
- return_0;
-
- if (_switches[UUID_ARG] || _switches[MAJOR_ARG]) {
- if (!_set_task_device(dmt, NULL, 0))
- goto_out;
- } else {
- if (!_set_task_device(dmt, argv[0], 0))
- goto_out;
- argc--;
- argv++;
- }
-
- errno = 0;
- sector = strtoull(argv[0], &endptr, 10);
- if (errno || *endptr || endptr == argv[0]) {
- log_error("Invalid sector.");
- goto out;
- }
- if (!dm_task_set_sector(dmt, sector))
- goto_out;
-
- argc--;
- argv++;
-
- if (argc <= 0)
- log_error("No message supplied.");
-
- for (i = 0; i < argc; i++)
- sz += strlen(argv[i]) + 1;
-
- if (!(str = zalloc(sz))) {
- log_error("Message string allocation failed.");
- goto out;
- }
-
- for (i = 0; i < argc; i++) {
- if (i)
- strcat(str, " ");
- strcat(str, argv[i]);
- }
-
- i = dm_task_set_message(dmt, str);
-
- free(str);
-
- if (!i)
- goto_out;
-
- if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
- goto_out;
-
- if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
- goto_out;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- if (!_task_run(dmt))
- goto_out;
-
- if ((response = dm_task_get_message_response(dmt))) {
- if (!*response || response[strlen(response) - 1] == '\n')
- fputs(response, stdout);
- else
- puts(response);
- }
-
- r = 1;
-
-out:
- dm_task_destroy(dmt);
-
- return r;
-}
-
-static int _setgeometry(CMD_ARGS)
-{
- int r = 0;
- struct dm_task *dmt;
-
- if (!(dmt = dm_task_create(DM_DEVICE_SET_GEOMETRY)))
- return_0;
-
- if (_switches[UUID_ARG] || _switches[MAJOR_ARG]) {
- if (!_set_task_device(dmt, NULL, 0))
- goto_out;
- } else {
- if (!_set_task_device(dmt, argv[0], 0))
- goto_out;
- argc--;
- argv++;
- }
-
- if (!dm_task_set_geometry(dmt, argv[0], argv[1], argv[2], argv[3]))
- goto_out;
-
- if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
- goto_out;
-
- if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
- goto_out;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- /* run the task */
- if (!_task_run(dmt))
- goto_out;
-
- r = 1;
-
-out:
- dm_task_destroy(dmt);
-
- return r;
-}
-
-static int _splitname(CMD_ARGS)
-{
- struct dmsetup_report_obj obj = { NULL };
- int r;
-
- if (!(obj.split_name = _get_split_name((argc == 2) ? argv[1] : "LVM",
- argv[0], '\0')))
- return_0;
-
- r = dm_report_object(_report, &obj);
- _destroy_split_name(obj.split_name);
-
- return r;
-}
-
-static uint32_t _get_cookie_value(const char *str_value)
-{
- unsigned long int value;
- char *p;
-
- errno = 0;
- value = strtoul(str_value, &p, 0);
-
- if (errno || !value || (*p) || (value > UINT32_MAX)) {
- log_error("Incorrect cookie value.");
- return 0;
- }
-
- return (uint32_t) value;
-}
-
-static int _udevflags(CMD_ARGS)
-{
- uint32_t cookie;
- uint16_t flags;
- int i;
- static const char *dm_flag_names[] = {"DISABLE_DM_RULES",
- "DISABLE_SUBSYSTEM_RULES",
- "DISABLE_DISK_RULES",
- "DISABLE_OTHER_RULES",
- "LOW_PRIORITY",
- "DISABLE_LIBRARY_FALLBACK",
- "PRIMARY_SOURCE",
- 0};
-
- if (!(cookie = _get_cookie_value(argv[0])))
- return_0;
-
- flags = cookie >> DM_UDEV_FLAGS_SHIFT;
-
- for (i = 0; i < DM_UDEV_FLAGS_SHIFT; i++)
- if (1 << i & flags) {
- if (i < DM_UDEV_FLAGS_SHIFT / 2 && dm_flag_names[i])
- printf("DM_UDEV_%s_FLAG='1'\n", dm_flag_names[i]);
- else if (i < DM_UDEV_FLAGS_SHIFT / 2)
- /*
- * This is just a fallback. Each new DM flag
- * should have its symbolic name assigned.
- */
- printf("DM_UDEV_FLAG%d='1'\n", i);
- else
- /*
- * We can't assign symbolic names to subsystem
- * flags. Their semantics vary based on the
- * subsystem that is currently used.
- */
- printf("DM_SUBSYSTEM_UDEV_FLAG%d='1'\n",
- i - DM_UDEV_FLAGS_SHIFT / 2);
- }
-
- return 1;
-}
-
-static int _udevcomplete(CMD_ARGS)
-{
- uint32_t cookie;
-
- if (!(cookie = _get_cookie_value(argv[0])))
- return_0;
-
- /*
- * Strip flags from the cookie and use cookie magic instead.
- * If the cookie has non-zero prefix and the base is zero then
- * this one carries flags to control udev rules only and it is
- * not meant to be for notification. Return with success in this
- * situation.
- */
- if (!(cookie &= ~DM_UDEV_FLAGS_MASK))
- return 1;
-
- cookie |= DM_COOKIE_MAGIC << DM_UDEV_FLAGS_SHIFT;
-
- return dm_udev_complete(cookie);
-}
-
-#ifndef UDEV_SYNC_SUPPORT
-static const char _cmd_not_supported[] = "Command not supported. Recompile with \"--enable-udev_sync\" to enable.";
-
-static int _udevcreatecookie(CMD_ARGS)
-{
- log_error(_cmd_not_supported);
-
- return 0;
-}
-
-static int _udevreleasecookie(CMD_ARGS)
-{
- log_error(_cmd_not_supported);
-
- return 0;
-}
-
-static int _udevcomplete_all(CMD_ARGS)
-{
- log_error(_cmd_not_supported);
-
- return 0;
-}
-
-static int _udevcookies(CMD_ARGS)
-{
- log_error(_cmd_not_supported);
-
- return 0;
-}
-
-#else /* UDEV_SYNC_SUPPORT */
-static int _set_up_udev_support(const char *dev_dir)
-{
- int dirs_diff;
- const char *env;
- size_t len = strlen(dev_dir), udev_dir_len = strlen(DM_UDEV_DEV_DIR);
-
- if (_switches[NOUDEVSYNC_ARG])
- dm_udev_set_sync_support(0);
-
- if (!_udev_cookie) {
- env = getenv(DM_UDEV_COOKIE_ENV_VAR_NAME);
- if (env && *env && (_udev_cookie = _get_cookie_value(env)))
- log_debug("Using udev transaction 0x%08" PRIX32
- " defined by %s environment variable.",
- _udev_cookie,
- DM_UDEV_COOKIE_ENV_VAR_NAME);
- }
- else if (_switches[UDEVCOOKIE_ARG])
- log_debug("Using udev transaction 0x%08" PRIX32
- " defined by --udevcookie option.",
- _udev_cookie);
-
- /*
- * Normally, there's always a fallback action by libdevmapper if udev
- * has not done its job correctly, e.g. the nodes were not created.
- * If using udev transactions by specifying existing cookie value,
- * we need to disable node creation by libdevmapper completely,
- * disabling any fallback actions, since any synchronisation happens
- * at the end of the transaction only. We need to do this to prevent
- * races between udev and libdevmapper but only in case udev "dev path"
- * is the same as "dev path" used by libdevmapper.
- */
-
-
- /*
- * DM_UDEV_DEV_DIR always has '/' at its end.
- * If the dev_dir does not have it, be sure
- * to make the right comparison without the '/' char!
- */
- if (dev_dir[len - 1] != '/')
- udev_dir_len--;
-
- dirs_diff = udev_dir_len != len ||
- strncmp(DM_UDEV_DEV_DIR, dev_dir, len);
- _udev_only = !dirs_diff && (_udev_cookie || !_switches[VERIFYUDEV_ARG]);
-
- if (dirs_diff) {
- log_debug("The path %s used for creating device nodes that is "
- "set via DM_DEV_DIR environment variable differs from "
- "the path %s that is used by udev. All warnings "
- "about udev not working correctly while processing "
- "particular nodes will be suppressed. These nodes "
- "and symlinks will be managed in each directory "
- "separately.", dev_dir, DM_UDEV_DEV_DIR);
- dm_udev_set_checking(0);
- }
-
- return 1;
-}
-
-static int _udevcreatecookie(CMD_ARGS)
-{
- uint32_t cookie;
-
- if (!dm_udev_create_cookie(&cookie))
- return_0;
-
- if (cookie)
- printf("0x%08" PRIX32 "\n", cookie);
-
- return 1;
-}
-
-static int _udevreleasecookie(CMD_ARGS)
-{
- if (argv[0] && !(_udev_cookie = _get_cookie_value(argv[0])))
- return_0;
-
- if (!_udev_cookie) {
- log_error("No udev transaction cookie given.");
- return 0;
- }
-
- return dm_udev_wait(_udev_cookie);
-}
-
-__attribute__((format(printf, 1, 2)))
-static char _yes_no_prompt(const char *prompt, ...)
-{
- int c = 0, ret = 0;
- va_list ap;
-
- do {
- if (c == '\n' || !c) {
- va_start(ap, prompt);
- vprintf(prompt, ap);
- va_end(ap);
- }
-
- if ((c = getchar()) == EOF) {
- ret = 'n';
- break;
- }
-
- c = tolower(c);
- if ((c == 'y') || (c == 'n'))
- ret = c;
- } while (!ret || c != '\n');
-
- if (c != '\n')
- putchar('\n');
-
- return ret;
-}
-
-static int _udevcomplete_all(CMD_ARGS)
-{
- int max_id, id, sid;
- struct seminfo sinfo;
- struct semid_ds sdata;
- int counter = 0;
- int skipped = 0;
- unsigned age = 0;
- time_t t;
-
- if (argc == 1 && (sscanf(argv[0], "%u", &age) != 1)) {
- log_error("Failed to read age_in_minutes parameter.");
- return 0;
- }
-
- if (!_switches[YES_ARG]) {
- log_warn("WARNING: This operation will destroy all semaphores %s%.0d%swith keys "
- "that have a prefix %" PRIu16 " (0x%" PRIx16 ").",
- age ? "older than " : "", age, age ? " minutes " : "",
- DM_COOKIE_MAGIC, DM_COOKIE_MAGIC);
-
- if (_yes_no_prompt("Do you really want to continue? [y/n]: ") == 'n') {
- log_print("Semaphores with keys prefixed by %" PRIu16
- " (0x%" PRIx16 ") NOT destroyed.",
- DM_COOKIE_MAGIC, DM_COOKIE_MAGIC);
- return 1;
- }
- }
-
- if ((max_id = semctl(0, 0, SEM_INFO, &sinfo)) < 0) {
- log_sys_error("semctl", "SEM_INFO");
- return 0;
- }
-
- for (id = 0; id <= max_id; id++) {
- if ((sid = semctl(id, 0, SEM_STAT, &sdata)) < 0)
- continue;
-
- if (sdata.sem_perm.__key >> 16 == DM_COOKIE_MAGIC) {
- t = time(NULL);
-
- if (sdata.sem_ctime + age * 60 > t ||
- sdata.sem_otime + age * 60 > t) {
- skipped++;
- continue;
- }
- if (semctl(sid, 0, IPC_RMID, 0) < 0) {
- log_error("Could not cleanup notification semaphore "
- "with semid %d and cookie value "
- FMTu32 " (0x" FMTx32 ").", sid,
- sdata.sem_perm.__key, sdata.sem_perm.__key);
- continue;
- }
-
- counter++;
- }
- }
-
- log_print("%d semaphores with keys prefixed by "
- FMTu16 " (0x" FMTx16 ") destroyed. %d skipped.",
- counter, DM_COOKIE_MAGIC, DM_COOKIE_MAGIC, skipped);
-
- return 1;
-}
-
-static int _udevcookies(CMD_ARGS)
-{
- int max_id, id, sid;
- struct seminfo sinfo;
- struct semid_ds sdata;
- int val;
- char otime_str[26], ctime_str[26];
- char *otimes, *ctimes;
-
- if ((max_id = semctl(0, 0, SEM_INFO, &sinfo)) < 0) {
- log_sys_error("sem_ctl", "SEM_INFO");
- return 0;
- }
-
- printf("Cookie Semid Value Last semop time Last change time\n");
-
- for (id = 0; id <= max_id; id++) {
- if ((sid = semctl(id, 0, SEM_STAT, &sdata)) < 0)
- continue;
-
- if (sdata.sem_perm.__key >> 16 == DM_COOKIE_MAGIC) {
- if ((val = semctl(sid, 0, GETVAL)) < 0) {
- log_error("semid %d: sem_ctl failed for "
- "cookie 0x%" PRIx32 ": %s",
- sid, sdata.sem_perm.__key,
- strerror(errno));
- continue;
- }
-
- if ((otimes = ctime_r((const time_t *) &sdata.sem_otime, (char *)&otime_str)))
- otime_str[strlen(otimes)-1] = '\0';
- if ((ctimes = ctime_r((const time_t *) &sdata.sem_ctime, (char *)&ctime_str)))
- ctime_str[strlen(ctimes)-1] = '\0';
-
- printf("0x%-10x %-10d %-10d %s %s\n", sdata.sem_perm.__key,
- sid, val, otimes ? : "unknown",
- ctimes? : "unknown");
- }
- }
-
- return 1;
-}
-#endif /* UDEV_SYNC_SUPPORT */
-
-static int _version(CMD_ARGS)
-{
- char version[80];
-
- if (dm_get_library_version(version, sizeof(version)))
- printf("Library version: %s\n", version);
-
- if (!dm_driver_version(version, sizeof(version)))
- return_0;
-
- printf("Driver version: %s\n", version);
-
- /* don't output column headings for 'dmstats version'. */
- if (_report) {
- dm_report_free(_report);
- _report = NULL;
- }
-
- return 1;
-}
-
-static int _simple(int task, const char *name, uint32_t event_nr, int display)
-{
- uint32_t cookie = 0;
- uint16_t udev_flags = 0;
- int udev_wait_flag = task == DM_DEVICE_RESUME ||
- task == DM_DEVICE_REMOVE;
- int r = 0;
-
- struct dm_task *dmt;
-
- if (!(dmt = dm_task_create(task)))
- return_0;
-
- if (!_set_task_device(dmt, name, 0))
- goto_out;
-
- if (event_nr && !dm_task_set_event_nr(dmt, event_nr))
- goto_out;
-
- if (_switches[NOFLUSH_ARG] && !dm_task_no_flush(dmt))
- goto_out;
-
- if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
- goto_out;
-
- if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
- goto_out;
-
- if (_switches[NOLOCKFS_ARG] && !dm_task_skip_lockfs(dmt))
- goto_out;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- /* FIXME: needs to coperate with udev */
- if (!_set_task_add_node(dmt))
- goto_out;
-
- if (_switches[READAHEAD_ARG] &&
- !dm_task_set_read_ahead(dmt, _int_args[READAHEAD_ARG],
- _read_ahead_flags))
- goto_out;
-
- if (_switches[NOUDEVRULES_ARG])
- udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
- DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
-
- if (_udev_cookie)
- cookie = _udev_cookie;
-
- if (_udev_only)
- udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
-
- if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, udev_flags))
- goto_out;
-
- if (_switches[RETRY_ARG] && task == DM_DEVICE_REMOVE)
- dm_task_retry_remove(dmt);
-
- if (_switches[DEFERRED_ARG] && (task == DM_DEVICE_REMOVE || task == DM_DEVICE_REMOVE_ALL))
- dm_task_deferred_remove(dmt);
-
- r = _task_run(dmt);
-
-out:
- if (!_udev_cookie && udev_wait_flag)
- (void) dm_udev_wait(cookie);
-
- if (r && display && _switches[VERBOSE_ARG])
- r = _display_info(dmt);
-
- dm_task_destroy(dmt);
-
- return r;
-}
-
-static int _suspend(CMD_ARGS)
-{
- return _simple(DM_DEVICE_SUSPEND, argc ? argv[0] : NULL, 0, 1);
-}
-
-static int _resume(CMD_ARGS)
-{
- return _simple(DM_DEVICE_RESUME, argc ? argv[0] : NULL, 0, 1);
-}
-
-static int _clear(CMD_ARGS)
-{
- return _simple(DM_DEVICE_CLEAR, argc ? argv[0] : NULL, 0, 1);
-}
-
-static int _wait(CMD_ARGS)
-{
- const char *name = NULL;
-
- if (!_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
- if (!argc) {
- log_error("No device specified.");
- return 0;
- }
- name = argv[0];
- argc--, argv++;
- }
-
- return _simple(DM_DEVICE_WAITEVENT, name,
- (argc) ? (uint32_t) atoi(argv[argc - 1]) : 0, 1);
-}
-
-static int _process_all(const struct command *cmd, const char *subcommand, int argc, char **argv, int silent,
- int (*fn) (CMD_ARGS))
-{
- int r = 1;
- struct dm_names *names;
- unsigned next = 0;
-
- struct dm_task *dmt;
-
- if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
- return_0;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- if (!_task_run(dmt)) {
- r = 0;
- goto_out;
- }
-
- if (!(names = dm_task_get_names(dmt))) {
- r = 0;
- goto_out;
- }
-
- if (!names->dev) {
- if (!silent)
- printf("No devices found\n");
- goto out;
- }
-
- do {
- names = (struct dm_names *)((char *) names + next);
- if (!fn(cmd, subcommand, argc, argv, names, 1))
- r = 0;
- next = names->next;
- } while (next);
-
-out:
- dm_task_destroy(dmt);
- return r;
-}
-
-static uint64_t _get_device_size(const char *name)
-{
- uint64_t start, length, size = UINT64_C(0);
- struct dm_info info;
- char *target_type, *params;
- struct dm_task *dmt;
- void *next = NULL;
-
- if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
- return_0;
-
- if (!_set_task_device(dmt, name, 0))
- goto_out;
-
- if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
- goto_out;
-
- if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
- goto_out;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- if (!_task_run(dmt))
- goto_out;
-
- if (!dm_task_get_info(dmt, &info) || !info.exists)
- goto_out;
-
- do {
- next = dm_get_next_target(dmt, next, &start, &length,
- &target_type, ¶ms);
- size += length;
- } while (next);
-
-out:
- dm_task_destroy(dmt);
- return size;
-}
-
-static int _error_device(CMD_ARGS)
-{
- struct dm_task *dmt;
- const char *name;
- uint64_t size;
- int r = 0;
-
- name = names ? names->name : argv[0];
-
- if (!name || !*name) {
- log_error("No device specified.");
- return 0;
- }
-
- size = _get_device_size(name);
-
- if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
- return_0;
-
- if (!_set_task_device(dmt, name, 0))
- goto_bad;
-
- if (!dm_task_add_target(dmt, UINT64_C(0), size, "error", ""))
- goto_bad;
-
- if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
- goto_bad;
-
- if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
- goto_bad;
-
- if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
- goto_bad;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_bad;
-
- if (!_task_run(dmt))
- goto_bad;
-
- if (_switches[FORCE_ARG])
- /* Avoid hang on flushing with --force */
- _switches[NOLOCKFS_ARG] = _switches[NOFLUSH_ARG] = 1;
-
- if (!_simple(DM_DEVICE_RESUME, name, 0, 0)) {
- _simple(DM_DEVICE_CLEAR, name, 0, 0);
- goto_bad;
- }
-
- r = 1;
-
-bad:
- dm_task_destroy(dmt);
- return r;
-}
-
-static int _remove(CMD_ARGS)
-{
- if (_switches[FORCE_ARG] && argc) {
- /*
- * 'remove --force' option is doing 2 operations on the same device
- * this is not compatible with the use of --udevcookie/DM_UDEV_COOKIE.
- * Udevd collision could be partially avoided with --retry.
- */
- if (_udev_cookie)
- log_warn("WARNING: Use of cookie and --force is not compatible.");
- (void) _error_device(cmd, NULL, argc, argv, NULL, 0);
- }
-
- return _simple(DM_DEVICE_REMOVE, argc ? argv[0] : NULL, 0, 0);
-}
-
-static int _count_devices(CMD_ARGS)
-{
- _num_devices++;
-
- return 1;
-}
-
-static int _remove_all(CMD_ARGS)
-{
- int r;
-
- /* Remove all closed devices */
- r = _simple(DM_DEVICE_REMOVE_ALL, "", 0, 0) | dm_mknodes(NULL);
-
- if (!_switches[FORCE_ARG])
- return r;
-
- _num_devices = 0;
- r |= _process_all(cmd, NULL, argc, argv, 1, _count_devices);
-
- /* No devices left? */
- if (!_num_devices)
- return r;
-
- r |= _process_all(cmd, NULL, argc, argv, 1, _error_device);
- r |= _simple(DM_DEVICE_REMOVE_ALL, "", 0, 0) | dm_mknodes(NULL);
-
- _num_devices = 0;
- r |= _process_all(cmd, NULL, argc, argv, 1, _count_devices);
- if (!_num_devices)
- return r;
-
- log_error("Unable to remove %d device(s).", _num_devices);
-
- return r;
-}
-
-static void _display_dev(struct dm_task *dmt, const char *name)
-{
- struct dm_info info;
-
- if (dm_task_get_info(dmt, &info))
- printf("%s\t(%u, %u)\n", name, info.major, info.minor);
-}
-
-static int _mknodes(CMD_ARGS)
-{
- return dm_mknodes(argc ? argv[0] : NULL);
-}
-
-static int _exec_command(const char *name)
-{
- static char path[PATH_MAX];
- static char *args[ARGS_MAX + 1];
- static int argc = 0;
- char *c;
- pid_t pid;
-
- if (argc < 0)
- return_0;
-
- if (!dm_mknodes(name))
- return_0;
-
- if (dm_snprintf(path, sizeof(path), "%s/%s", dm_dir(), name) < 0)
- return_0;
-
- if (!argc) {
- c = _command_to_exec;
- while (argc < ARGS_MAX) {
- while (*c && isspace(*c))
- c++;
- if (!*c)
- break;
- args[argc++] = c;
- while (*c && !isspace(*c))
- c++;
- if (*c)
- *c++ = '\0';
- }
-
- if (!argc) {
- argc = -1;
- return_0;
- }
-
- if (argc == ARGS_MAX) {
- log_error("Too many args to --exec.");
- argc = -1;
- return 0;
- }
-
- args[argc++] = path;
- args[argc] = NULL;
- }
-
- if (!(pid = fork())) {
- execvp(args[0], args);
- _exit(127);
- } else if (pid < (pid_t) 0)
- return 0;
-
- TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
-
- return 1;
-}
-
-/*
- * Print string s using a backslash to quote each character that has a special
- * meaning in the concise format - comma, semi-colon and backslash.
- */
-static void _print_string_quoted(const char *s)
-{
- while (*s) {
- if (strchr(",;\\", *s))
- putchar('\\');
- putchar(*s++);
- }
-}
-
-static void hide_key(char *params, const char *name)
-{
- char *c = strstr(params, name);
-
- if (!c)
- return;
-
- c += strlen(name);
-
- /* key is optional */
- c = strpbrk(c, " :");
- if (!c || *c++ != ':')
- return;
-
- while (*c && *c != ' ')
- *c++ = '0';
-}
-
-static int _status(CMD_ARGS)
-{
- int r = 0;
- struct dm_task *dmt;
- void *next = NULL;
- uint64_t start, length;
- char *target_type = NULL;
- char *params, *c;
- int cmdno;
- const char *name = NULL;
- int matched = 0;
- int ls_only = 0;
- int use_concise = 0;
- struct dm_info info;
-
- if (names)
- name = names->name;
- else {
- if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
- /* FIXME Respect deps in concise mode, so they are correctly ordered for recreation */
- return _process_all(cmd, NULL, argc, argv, 0, _status);
- name = argv[0];
- }
-
- if (!strcmp(cmd->name, "table")) {
- cmdno = DM_DEVICE_TABLE;
- /* --concise only applies to 'table' */
- if (_switches[CONCISE_ARG])
- use_concise = 1;
- } else
- cmdno = DM_DEVICE_STATUS;
-
- if (!strcmp(cmd->name, "ls"))
- ls_only = 1;
-
- if (!(dmt = dm_task_create(cmdno)))
- return_0;
-
- if (!_set_task_device(dmt, name, 0))
- goto_out;
-
- if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
- goto_out;
-
- if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
- goto_out;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- if (_switches[NOFLUSH_ARG] && !dm_task_no_flush(dmt))
- goto_out;
-
- if (!_task_run(dmt))
- goto_out;
-
- if (!dm_task_get_info(dmt, &info))
- goto_out;
-
- if (!info.exists) {
- log_error("Device does not exist.");
- goto out;
- }
-
- if (!name)
- name = dm_task_get_name(dmt);
-
- /* Fetch targets and print 'em */
- do {
- next = dm_get_next_target(dmt, next, &start, &length,
- &target_type, ¶ms);
- /* Skip if target type doesn't match */
- if (_switches[TARGET_ARG] &&
- (!target_type || strcmp(target_type, _target)))
- continue;
-
- if (ls_only) {
- if (!_switches[EXEC_ARG] || !_command_to_exec ||
- _switches[VERBOSE_ARG])
- _display_dev(dmt, name);
- next = NULL;
- } else if (!_switches[EXEC_ARG] || !_command_to_exec ||
- _switches[VERBOSE_ARG]) {
- if (!use_concise) {
- if (!matched && _switches[VERBOSE_ARG])
- _display_info(dmt);
- if (multiple_devices && !_switches[VERBOSE_ARG])
- printf("%s: ", name);
- } else if (!matched) {
- /*
- * Before first target of device in concise output,
- * print basic device information in the appropriate format.
- * Separate devices by a semi-colon.
- */
- if (_concise_output_produced)
- putchar(';');
- _concise_output_produced = 1;
-
- _print_string_quoted(name);
- putchar(',');
- _print_string_quoted(dm_task_get_uuid(dmt));
- printf(",%u,%s", info.minor, info.read_only ? "ro" : "rw");
- }
- /* Next print any target-specific information */
- if (target_type) {
- /* Suppress encryption keys */
- if (!_switches[SHOWKEYS_ARG] &&
- cmdno == DM_DEVICE_TABLE) {
- if (!strcmp(target_type, "crypt")) {
- c = params;
- while (*c && *c != ' ')
- c++;
- if (*c)
- c++;
- /*
- * Do not suppress kernel key references prefixed
- * with colon ':'. Displaying those references is
- * harmless. crypt target supports kernel keys
- * starting with v1.15.0 (merged in kernel 4.10)
- */
- if (*c != ':')
- while (*c && *c != ' ')
- *c++ = '0';
- } else if (!strcmp(target_type, "integrity")) {
- /*
- * "internal_hash", "journal_crypt" and "journal_mac"
- * params allow keys optionally in hexbyte
- * representation.
- */
- hide_key(params, "internal_hash:");
- hide_key(params, "journal_crypt:");
- hide_key(params, "journal_mac:");
- }
- }
- if (use_concise)
- putchar(',');
- printf(FMTu64 " " FMTu64 " %s ", start, length, target_type);
- if (use_concise)
- _print_string_quoted(params);
- else
- printf("%s", params);
- }
- /* --concise places all devices on a single output line */
- if (!use_concise)
- putchar('\n');
- }
- matched = 1;
- } while (next);
-
- if (multiple_devices && _switches[VERBOSE_ARG] && matched && !ls_only && (!use_concise || _switches[VERBOSE_ARG] > 1))
- putchar('\n');
-
- if (matched && _switches[EXEC_ARG] && _command_to_exec && !_exec_command(name))
- goto_out;
-
- r = 1;
-
-out:
- dm_task_destroy(dmt);
- return r;
-}
-
-/* Show target names and their version numbers */
-static int _targets(CMD_ARGS)
-{
- int r = 0;
- struct dm_task *dmt;
- struct dm_versions *target;
- struct dm_versions *last_target;
-
- if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
- return_0;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- if (!_task_run(dmt))
- goto_out;
-
- target = dm_task_get_versions(dmt);
-
- /* Fetch targets and print 'em */
- do {
- last_target = target;
-
- printf("%-16s v%d.%d.%d\n", target->name, target->version[0],
- target->version[1], target->version[2]);
-
- target = (struct dm_versions *)((char *) target + target->next);
- } while (last_target != target);
-
- r = 1;
-
-out:
- dm_task_destroy(dmt);
- return r;
-}
-
-static int _info(CMD_ARGS)
-{
- int r = 0;
-
- struct dm_task *dmt;
- char *name = NULL;
-
- if (names)
- name = names->name;
- else {
- if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
- return _process_all(cmd, NULL, argc, argv, 0, _info);
- name = argv[0];
- }
-
- if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
- return_0;
-
- if (!_set_task_device(dmt, name, 0))
- goto_out;
-
- if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
- goto_out;
-
- if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
- goto_out;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- if (!_task_run(dmt))
- goto_out;
-
- r = _display_info(dmt);
-
-out:
- dm_task_destroy(dmt);
- return r;
-}
-
-static int _deps(CMD_ARGS)
-{
- int r = 0;
- uint32_t i;
- struct dm_deps *deps;
- struct dm_task *dmt;
- struct dm_info info;
- char *name = NULL;
- char dev_name[PATH_MAX];
- int major, minor;
-
- if (names)
- name = names->name;
- else {
- if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
- return _process_all(cmd, NULL, argc, argv, 0, _deps);
- name = argv[0];
- }
-
- if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
- return_0;
-
- if (!_set_task_device(dmt, name, 0))
- goto_out;
-
- if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
- goto_out;
-
- if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
- goto_out;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- if (!_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) {
- printf("Device does not exist.\n");
- r = 1;
- goto out;
- }
-
- if (_switches[VERBOSE_ARG])
- _display_info(dmt);
-
- if (multiple_devices && !_switches[VERBOSE_ARG])
- printf("%s: ", name);
- printf("%d dependencies\t:", deps->count);
-
- for (i = 0; i < deps->count; i++) {
- major = (int) MAJOR(deps->device[i]);
- minor = (int) MINOR(deps->device[i]);
-
- if ((_dev_name_type == DN_BLK || _dev_name_type == DN_MAP) &&
- dm_device_get_name(major, minor, _dev_name_type == DN_BLK,
- dev_name, PATH_MAX))
- printf(" (%s)", dev_name);
- else
- printf(" (%d, %d)", major, minor);
- }
- putchar('\n');
-
- if (multiple_devices && _switches[VERBOSE_ARG])
- putchar('\n');
-
- r = 1;
-
-out:
- dm_task_destroy(dmt);
- return r;
-}
-
-static int _display_name(CMD_ARGS)
-{
- char dev_name[PATH_MAX];
-
- if (!names)
- return 1;
-
- if ((_dev_name_type == DN_BLK || _dev_name_type == DN_MAP) &&
- dm_device_get_name((int) MAJOR(names->dev), (int) MINOR(names->dev),
- _dev_name_type == DN_BLK, dev_name, PATH_MAX))
- printf("%s\t(%s)\n", names->name, dev_name);
- else
- printf("%s\t(%d:%d)\n", names->name,
- (int) MAJOR(names->dev),
- (int) MINOR(names->dev));
-
- return 1;
-}
-
-/*
- * Tree drawing code
- */
-
-enum {
- TR_DEVICE=0, /* display device major:minor number */
- TR_BLKDEVNAME, /* display device kernel name */
- TR_TABLE,
- TR_STATUS,
- TR_ACTIVE,
- TR_RW,
- TR_OPENCOUNT,
- TR_UUID,
- TR_COMPACT,
- TR_TRUNCATE,
- TR_BOTTOMUP,
- NUM_TREEMODE,
-};
-
-static int _tree_switches[NUM_TREEMODE];
-
-#define TR_PRINT_ATTRIBUTE ( _tree_switches[TR_ACTIVE] || \
- _tree_switches[TR_RW] || \
- _tree_switches[TR_OPENCOUNT] || \
- _tree_switches[TR_UUID] )
-
-#define TR_PRINT_TARGETS ( _tree_switches[TR_TABLE] || \
- _tree_switches[TR_STATUS] )
-
-/* Compact - fewer newlines */
-#define TR_PRINT_COMPACT (_tree_switches[TR_COMPACT] && \
- !TR_PRINT_ATTRIBUTE && \
- !TR_PRINT_TARGETS)
-
-/* FIXME Get rid of this */
-#define MAX_DEPTH 100
-
-/* Drawing character definition from pstree */
-/* [pstree comment] UTF-8 defines by Johan Myreen, updated by Ben Winslow */
-#define UTF_V "\342\224\202" /* U+2502, Vertical line drawing char */
-#define UTF_VR "\342\224\234" /* U+251C, Vertical and right */
-#define UTF_H "\342\224\200" /* U+2500, Horizontal */
-#define UTF_UR "\342\224\224" /* U+2514, Up and right */
-#define UTF_HD "\342\224\254" /* U+252C, Horizontal and down */
-
-#define VT_BEG "\033(0\017" /* use graphic chars */
-#define VT_END "\033(B" /* back to normal char set */
-#define VT_V "x" /* see UTF definitions above */
-#define VT_VR "t"
-#define VT_H "q"
-#define VT_UR "m"
-#define VT_HD "w"
-
-static struct {
- const char *empty_2; /* */
- const char *branch_2; /* |- */
- const char *vert_2; /* | */
- const char *last_2; /* `- */
- const char *single_3; /* --- */
- const char *first_3; /* -+- */
-}
-_tsym_ascii = {
- " ",
- "|-",
- "| ",
- "`-",
- "---",
- "-+-"
-},
-_tsym_utf = {
- " ",
- UTF_VR UTF_H,
- UTF_V " ",
- UTF_UR UTF_H,
- UTF_H UTF_H UTF_H,
- UTF_H UTF_HD UTF_H
-},
-_tsym_vt100 = {
- " ",
- VT_BEG VT_VR VT_H VT_END,
- VT_BEG VT_V VT_END " ",
- VT_BEG VT_UR VT_H VT_END,
- VT_BEG VT_H VT_H VT_H VT_END,
- VT_BEG VT_H VT_HD VT_H VT_END
-},
-*_tsym = &_tsym_ascii;
-
-/*
- * Tree drawing functions.
- */
-/* FIXME Get rid of these statics - use dynamic struct */
-/* FIXME Explain what these vars are for */
-static int _tree_width[MAX_DEPTH], _tree_more[MAX_DEPTH];
-static int _termwidth = 80; /* Maximum output width */
-static int _cur_x = 1; /* Current horizontal output position */
-static char _last_char = 0;
-
-static void _out_char(const unsigned c)
-{
- /* Only first UTF-8 char counts */
- _cur_x += ((c & 0xc0) != 0x80);
-
- if (!_tree_switches[TR_TRUNCATE]) {
- putchar((int) c);
- return;
- }
-
- /* Truncation? */
- if (_cur_x <= _termwidth)
- putchar((int) c);
-
- if (_cur_x == _termwidth + 1 && ((c & 0xc0) != 0x80)) {
- if (_last_char || (c & 0x80)) {
- putchar('.');
- putchar('.');
- putchar('.');
- } else {
- _last_char = c;
- _cur_x--;
- }
- }
-}
-
-static void _out_string(const char *str)
-{
- while (*str)
- _out_char((unsigned char) *str++);
-}
-
-/* non-negative integers only */
-static unsigned _out_int(unsigned num)
-{
- unsigned digits = 0;
- unsigned divi;
-
- if (!num) {
- _out_char('0');
- return 1;
- }
-
- /* non zero case */
- for (divi = 1; num / divi; divi *= 10)
- digits++;
-
- for (divi /= 10; divi; divi /= 10)
- _out_char('0' + (num / divi) % 10);
-
- return digits;
-}
-
-static void _out_newline(void)
-{
- if (_last_char && _cur_x == _termwidth)
- putchar(_last_char);
- _last_char = 0;
- putchar('\n');
- _cur_x = 1;
-}
-
-static void _out_prefix(unsigned depth)
-{
- unsigned x, d;
-
- for (d = 0; d < depth; d++) {
- for (x = _tree_width[d] + 1; x > 0; x--)
- _out_char(' ');
-
- _out_string(d == depth - 1 ?
- !_tree_more[depth] ? _tsym->last_2 : _tsym->branch_2
- : _tree_more[d + 1] ?
- _tsym->vert_2 : _tsym->empty_2);
- }
-}
-
-/*
- * Display tree
- */
-static void _display_tree_attributes(struct dm_tree_node *node)
-{
- int attr = 0;
- const char *uuid;
- const struct dm_info *info;
-
- uuid = dm_tree_node_get_uuid(node);
- info = dm_tree_node_get_info(node);
-
- if (!info->exists)
- return;
-
- if (_tree_switches[TR_ACTIVE]) {
- _out_string(attr++ ? ", " : " [");
- _out_string(info->suspended ? "SUSPENDED" : "ACTIVE");
- }
-
- if (_tree_switches[TR_RW]) {
- _out_string(attr++ ? ", " : " [");
- _out_string(info->read_only ? "RO" : "RW");
- }
-
- if (_tree_switches[TR_OPENCOUNT]) {
- _out_string(attr++ ? ", " : " [");
- (void) _out_int((unsigned) info->open_count);
- }
-
- if (_tree_switches[TR_UUID]) {
- _out_string(attr++ ? ", " : " [");
- _out_string(uuid && *uuid ? uuid : "");
- }
-
- if (attr)
- _out_char(']');
-}
-
-/* FIXME Display table or status line. (Disallow both?) */
-static void _display_tree_targets(struct dm_tree_node *node, unsigned depth)
-{
-}
-
-static void _display_tree_node(struct dm_tree_node *node, unsigned depth,
- unsigned first_child __attribute__((unused)),
- unsigned last_child, unsigned has_children)
-{
- int offset;
- const char *name;
- const struct dm_info *info;
- int first_on_line = 0;
- char dev_name[PATH_MAX];
-
- /* Sub-tree for targets has 2 more depth */
- if (depth + 2 > MAX_DEPTH)
- return;
-
- name = dm_tree_node_get_name(node);
-
- if ((!name || !*name) &&
- (!_tree_switches[TR_DEVICE] && !_tree_switches[TR_BLKDEVNAME]))
- return;
-
- /* Indicate whether there are more nodes at this depth */
- _tree_more[depth] = !last_child;
- _tree_width[depth] = 0;
-
- if (_cur_x == 1)
- first_on_line = 1;
-
- if (!TR_PRINT_COMPACT || first_on_line)
- _out_prefix(depth);
-
- /* Remember the starting point for compact */
- offset = _cur_x;
-
- if (TR_PRINT_COMPACT && !first_on_line)
- _out_string(_tree_more[depth] ? _tsym->first_3 : _tsym->single_3);
-
- /* display node */
- if (name)
- _out_string(name);
-
- info = dm_tree_node_get_info(node);
-
- if (_tree_switches[TR_BLKDEVNAME] &&
- dm_device_get_name(info->major, info->minor, 1, dev_name, PATH_MAX)) {
- _out_string(name ? " <" : "<");
- _out_string(dev_name);
- _out_char('>');
- }
-
- if (_tree_switches[TR_DEVICE]) {
- _out_string(name ? " (" : "(");
- (void) _out_int(info->major);
- _out_char(':');
- (void) _out_int(info->minor);
- _out_char(')');
- }
-
- /* display additional info */
- if (TR_PRINT_ATTRIBUTE)
- _display_tree_attributes(node);
-
- if (TR_PRINT_COMPACT)
- _tree_width[depth] = _cur_x - offset;
-
- if (!TR_PRINT_COMPACT || !has_children)
- _out_newline();
-
- if (TR_PRINT_TARGETS) {
- _tree_more[depth + 1] = has_children;
- _display_tree_targets(node, depth + 2);
- }
-}
-
-/*
- * Walk the dependency tree
- */
-static void _display_tree_walk_children(struct dm_tree_node *node,
- unsigned depth)
-{
- struct dm_tree_node *child, *next_child;
- void *handle = NULL;
- uint32_t inverted = _tree_switches[TR_BOTTOMUP];
- unsigned first_child = 1;
- unsigned has_children;
-
- next_child = dm_tree_next_child(&handle, node, inverted);
-
- while ((child = next_child)) {
- next_child = dm_tree_next_child(&handle, node, inverted);
- has_children =
- dm_tree_node_num_children(child, inverted) ? 1 : 0;
-
- _display_tree_node(child, depth, first_child,
- next_child ? 0U : 1U, has_children);
-
- if (has_children)
- _display_tree_walk_children(child, depth + 1);
-
- first_child = 0;
- }
-}
-
-static int _add_dep(CMD_ARGS)
-{
- if (names &&
- !dm_tree_add_dev(_dtree, (unsigned) MAJOR(names->dev), (unsigned) MINOR(names->dev)))
- return_0;
-
- return 1;
-}
-
-/*
- * Create and walk dependency tree
- *
- * An incomplete _dtree may still be used by the caller,
- * but the error must be reported.
- */
-static int _build_whole_deptree(const struct command *cmd)
-{
- if (_dtree)
- return 1;
-
- if (!(_dtree = dm_tree_create()))
- return_0;
-
- if (!_process_all(cmd, NULL, 0, NULL, 0, _add_dep))
- return_0;
-
- return 1;
-}
-
-static int _display_tree(CMD_ARGS)
-{
- int r;
-
- r = _build_whole_deptree(cmd);
-
- if (_dtree)
- _display_tree_walk_children(dm_tree_find_node(_dtree, 0, 0), 0);
-
- return r;
-}
-
-/*
- * Report device information
- */
-
-/* dm specific display functions */
-
-static int _int32_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const int32_t value = *(const int32_t *)data;
-
- return dm_report_field_int32(rh, field, &value);
-}
-
-static int _uint32_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const uint32_t value = *(const int32_t *)data;
-
- return dm_report_field_uint32(rh, field, &value);
-}
-
-static int _show_units(void)
-{
- /* --nosuffix overrides --units */
- if (_switches[NOSUFFIX_ARG])
- return_0;
-
- return (_int_args[UNITS_ARG]) ? 1 : 0;
-}
-
-static int _dm_name_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const char *name = dm_task_get_name((const struct dm_task *) data);
-
- return dm_report_field_string(rh, field, &name);
-}
-
-static int _dm_mangled_name_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- char *name;
- int r = 0;
-
- if ((name = dm_task_get_name_mangled((const struct dm_task *) data))) {
- r = dm_report_field_string(rh, field, (const char * const *) &name);
- free(name);
- }
-
- return r;
-}
-
-static int _dm_unmangled_name_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- char *name;
- int r = 0;
-
- if ((name = dm_task_get_name_unmangled((const struct dm_task *) data))) {
- r = dm_report_field_string(rh, field, (const char * const *) &name);
- free(name);
- }
-
- return r;
-}
-
-static int _dm_uuid_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field,
- const void *data, void *private __attribute__((unused)))
-{
- const char *uuid = dm_task_get_uuid((const struct dm_task *) data);
-
- if (!uuid || !*uuid)
- uuid = "";
-
- return dm_report_field_string(rh, field, &uuid);
-}
-
-static int _dm_mangled_uuid_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field,
- const void *data, void *private __attribute__((unused)))
-{
- char *uuid;
- int r = 0;
-
- if ((uuid = dm_task_get_uuid_mangled((const struct dm_task *) data))) {
- r = dm_report_field_string(rh, field, (const char * const *) &uuid);
- free(uuid);
- }
-
- return r;
-}
-
-static int _dm_unmangled_uuid_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field,
- const void *data, void *private __attribute__((unused)))
-{
- char *uuid;
- int r = 0;
-
- if ((uuid = dm_task_get_uuid_unmangled((const struct dm_task *) data))) {
- r = dm_report_field_string(rh, field, (const char * const *) &uuid);
- free(uuid);
- }
-
- return r;
-}
-
-static int _dm_read_ahead_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- uint32_t value;
-
- if (!dm_task_get_read_ahead((const struct dm_task *) data, &value))
- value = 0;
-
- return dm_report_field_uint32(rh, field, &value);
-}
-
-static int _dm_blk_name_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- char dev_name[PATH_MAX];
- const char *s = dev_name;
- const struct dm_info *info = data;
-
- if (!dm_device_get_name(info->major, info->minor, 1, dev_name, PATH_MAX)) {
- log_error("Could not resolve block device name for %d:%d.",
- info->major, info->minor);
- return 0;
- }
-
- return dm_report_field_string(rh, field, &s);
-}
-
-static int _dm_info_status_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- char buf[5];
- const char *s = buf;
- const struct dm_info *info = data;
-
- buf[0] = info->live_table ? 'L' : '-';
- buf[1] = info->inactive_table ? 'I' : '-';
- buf[2] = info->suspended ? 's' : '-';
- buf[3] = info->read_only ? 'r' : 'w';
- buf[4] = '\0';
-
- return dm_report_field_string(rh, field, &s);
-}
-
-static int _dm_info_table_loaded_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field,
- const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_info *info = data;
-
- if (info->live_table) {
- if (info->inactive_table)
- dm_report_field_set_value(field, "Both", NULL);
- else
- dm_report_field_set_value(field, "Live", NULL);
- return 1;
- }
-
- if (info->inactive_table)
- dm_report_field_set_value(field, "Inactive", NULL);
- else
- dm_report_field_set_value(field, "None", NULL);
-
- return 1;
-}
-
-static int _dm_info_suspended_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field,
- const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_info *info = data;
-
- if (info->suspended)
- dm_report_field_set_value(field, "Suspended", NULL);
- else
- dm_report_field_set_value(field, "Active", NULL);
-
- return 1;
-}
-
-static int _dm_info_read_only_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field,
- const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_info *info = data;
-
- if (info->read_only)
- dm_report_field_set_value(field, "Read-only", NULL);
- else
- dm_report_field_set_value(field, "Writeable", NULL);
-
- return 1;
-}
-
-
-static int _dm_info_devno_disp(struct dm_report *rh, struct dm_pool *mem,
- struct dm_report_field *field, const void *data,
- void *private)
-{
- char buf[PATH_MAX], *repstr;
- const struct dm_info *info = data;
-
- if (!dm_pool_begin_object(mem, 8)) {
- log_error("dm_pool_begin_object failed");
- return 0;
- }
-
- if (private) {
- if (!dm_device_get_name(info->major, info->minor,
- 1, buf, PATH_MAX)) {
- stack;
- goto out_abandon;
- }
- }
- else {
- if (dm_snprintf(buf, sizeof(buf), "%d:%d",
- info->major, info->minor) < 0) {
- log_error("dm_pool_alloc failed");
- goto out_abandon;
- }
- }
-
- if (!dm_pool_grow_object(mem, buf, strlen(buf) + 1)) {
- log_error("dm_pool_grow_object failed");
- goto out_abandon;
- }
-
- repstr = dm_pool_end_object(mem);
- dm_report_field_set_value(field, repstr, repstr);
- return 1;
-
- out_abandon:
- dm_pool_abandon_object(mem);
- return 0;
-}
-
-static int _dm_tree_names(struct dm_report *rh, struct dm_pool *mem,
- struct dm_report_field *field, const void *data,
- void *private, unsigned inverted)
-{
- const struct dm_tree_node *node = data;
- struct dm_tree_node *parent;
- void *t = NULL;
- const char *name;
- int first_node = 1;
- char *repstr;
-
- if (!dm_pool_begin_object(mem, 16)) {
- log_error("dm_pool_begin_object failed");
- return 0;
- }
-
- while ((parent = dm_tree_next_child(&t, node, inverted))) {
- name = dm_tree_node_get_name(parent);
- if (!name || !*name)
- continue;
- if (!first_node && !dm_pool_grow_object(mem, ",", 1)) {
- log_error("dm_pool_grow_object failed");
- goto out_abandon;
- }
- if (!dm_pool_grow_object(mem, name, 0)) {
- log_error("dm_pool_grow_object failed");
- goto out_abandon;
- }
- if (first_node)
- first_node = 0;
- }
-
- if (!dm_pool_grow_object(mem, "\0", 1)) {
- log_error("dm_pool_grow_object failed");
- goto out_abandon;
- }
-
- repstr = dm_pool_end_object(mem);
- dm_report_field_set_value(field, repstr, repstr);
- return 1;
-
- out_abandon:
- dm_pool_abandon_object(mem);
- return 0;
-}
-
-static int _dm_deps_names_disp(struct dm_report *rh,
- struct dm_pool *mem,
- struct dm_report_field *field,
- const void *data, void *private)
-{
- return _dm_tree_names(rh, mem, field, data, private, 0);
-}
-
-static int _dm_tree_parents_names_disp(struct dm_report *rh,
- struct dm_pool *mem,
- struct dm_report_field *field,
- const void *data, void *private)
-{
- return _dm_tree_names(rh, mem, field, data, private, 1);
-}
-
-static int _dm_tree_parents_devs_disp(struct dm_report *rh, struct dm_pool *mem,
- struct dm_report_field *field,
- const void *data, void *private)
-{
- const struct dm_tree_node *node = data;
- struct dm_tree_node *parent;
- void *t = NULL;
- const struct dm_info *info;
- int first_node = 1;
- char buf[DM_MAX_TYPE_NAME], *repstr;
-
- if (!dm_pool_begin_object(mem, 16)) {
- log_error("dm_pool_begin_object failed");
- return 0;
- }
-
- while ((parent = dm_tree_next_child(&t, node, 1))) {
- info = dm_tree_node_get_info(parent);
- if (!info->major && !info->minor)
- continue;
- if (!first_node && !dm_pool_grow_object(mem, ",", 1)) {
- log_error("dm_pool_grow_object failed");
- goto out_abandon;
- }
- if (dm_snprintf(buf, sizeof(buf), "%d:%d",
- info->major, info->minor) < 0) {
- log_error("dm_snprintf failed");
- goto out_abandon;
- }
- if (!dm_pool_grow_object(mem, buf, 0)) {
- log_error("dm_pool_grow_object failed");
- goto out_abandon;
- }
- if (first_node)
- first_node = 0;
- }
-
- if (!dm_pool_grow_object(mem, "\0", 1)) {
- log_error("dm_pool_grow_object failed");
- goto out_abandon;
- }
-
- repstr = dm_pool_end_object(mem);
- dm_report_field_set_value(field, repstr, repstr);
- return 1;
-
- out_abandon:
- dm_pool_abandon_object(mem);
- return 0;
-}
-
-static int _dm_tree_parents_count_disp(struct dm_report *rh,
- struct dm_pool *mem,
- struct dm_report_field *field,
- const void *data, void *private)
-{
- const struct dm_tree_node *node = data;
- int num_parent = dm_tree_node_num_children(node, 1);
-
- return dm_report_field_int(rh, field, &num_parent);
-}
-
-static int _dm_deps_disp_common(struct dm_report *rh, struct dm_pool*mem,
- struct dm_report_field *field, const void *data,
- void *private, int disp_blk_dev_names)
-{
- const struct dm_deps *deps = data;
- char buf[PATH_MAX], *repstr;
- int major, minor;
- unsigned i;
-
- if (!dm_pool_begin_object(mem, 16)) {
- log_error("dm_pool_begin_object failed");
- return 0;
- }
-
- for (i = 0; i < deps->count; i++) {
- major = (int) MAJOR(deps->device[i]);
- minor = (int) MINOR(deps->device[i]);
-
- if (disp_blk_dev_names) {
- if (!dm_device_get_name(major, minor, 1, buf, PATH_MAX)) {
- log_error("Could not resolve block device "
- "name for %d:%d.", major, minor);
- goto out_abandon;
- }
- }
- else if (dm_snprintf(buf, sizeof(buf), "%d:%d",
- major, minor) < 0) {
- log_error("dm_snprintf failed");
- goto out_abandon;
- }
-
- if (!dm_pool_grow_object(mem, buf, 0)) {
- log_error("dm_pool_grow_object failed");
- goto out_abandon;
- }
-
- if (i + 1 < deps->count && !dm_pool_grow_object(mem, ",", 1)) {
- log_error("dm_pool_grow_object failed");
- goto out_abandon;
- }
- }
-
- if (!dm_pool_grow_object(mem, "\0", 1)) {
- log_error("dm_pool_grow_object failed");
- goto out_abandon;
- }
-
- repstr = dm_pool_end_object(mem);
- dm_report_field_set_value(field, repstr, repstr);
- return 1;
-
- out_abandon:
- dm_pool_abandon_object(mem);
- return 0;
-}
-
-static int _dm_deps_disp(struct dm_report *rh, struct dm_pool *mem,
- struct dm_report_field *field, const void *data,
- void *private)
-{
- return _dm_deps_disp_common(rh, mem, field, data, private, 0);
-}
-
-static int _dm_deps_blk_names_disp(struct dm_report *rh, struct dm_pool *mem,
- struct dm_report_field *field,
- const void *data, void *private)
-{
- return _dm_deps_disp_common(rh, mem, field, data, private, 1);
-}
-
-static int _dm_subsystem_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- return dm_report_field_string(rh, field, (const char *const *) data);
-}
-
-static int _dm_vg_name_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
-
- return dm_report_field_string(rh, field, (const char *const *) data);
-}
-
-static int _dm_lv_name_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-
-{
- return dm_report_field_string(rh, field, (const char *const *) data);
-}
-
-static int _dm_lv_layer_name_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-
-{
- return dm_report_field_string(rh, field, (const char *const *) data);
-}
-
-/**
- * All _dm_stats_*_disp functions for basic counters are identical:
- * obtain the value for the current region and area and pass it to
- * dm_report_field_uint64().
- */
-#define MK_STATS_COUNTER_DISP_FN(counter) \
-static int _dm_stats_ ## counter ## _disp(struct dm_report *rh, \
- struct dm_pool *mem __attribute__((unused)), \
- struct dm_report_field *field, const void *data, \
- void *private __attribute__((unused))) \
-{ \
- const struct dm_stats *dms = (const struct dm_stats *) data; \
- uint64_t value = dm_stats_get_ ## counter(dms, DM_STATS_REGION_CURRENT, \
- DM_STATS_AREA_CURRENT); \
- return dm_report_field_uint64(rh, field, &value); \
-}
-
-MK_STATS_COUNTER_DISP_FN(reads)
-MK_STATS_COUNTER_DISP_FN(reads_merged)
-MK_STATS_COUNTER_DISP_FN(read_sectors)
-MK_STATS_COUNTER_DISP_FN(read_nsecs)
-MK_STATS_COUNTER_DISP_FN(writes)
-MK_STATS_COUNTER_DISP_FN(writes_merged)
-MK_STATS_COUNTER_DISP_FN(write_sectors)
-MK_STATS_COUNTER_DISP_FN(write_nsecs)
-MK_STATS_COUNTER_DISP_FN(io_in_progress)
-MK_STATS_COUNTER_DISP_FN(io_nsecs)
-MK_STATS_COUNTER_DISP_FN(weighted_io_nsecs)
-MK_STATS_COUNTER_DISP_FN(total_read_nsecs)
-MK_STATS_COUNTER_DISP_FN(total_write_nsecs)
-#undef MK_STATS_COUNTER_DISP_FN
-
-static int _dm_stats_region_id_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- uint64_t group_id, region_id = dm_stats_get_current_region(dms);
- char *group_buf = NULL, *repstr;
-
- if (dm_stats_current_object_type(dms) == DM_STATS_OBJECT_TYPE_GROUP) {
- group_id = dm_stats_get_group_id(dms, dm_stats_get_current_region(dms));
- if (!dm_stats_get_group_descriptor(dms, group_id, &group_buf))
- return 0;
- /* group_buf will disappear with the current handle */
- repstr = dm_pool_strdup(mem, group_buf);
- dm_report_field_set_value(field, repstr, &group_id);
- return 1;
- }
-
- return dm_report_field_uint64(rh, field, ®ion_id);
-}
-
-static int _dm_stats_region_start_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- uint64_t region_start;
- const char *repstr;
- double *sortval;
- char units = _disp_units;
- uint64_t factor = _disp_factor;
-
- if (!dm_stats_get_current_region_start(dms, ®ion_start))
- return_0;
-
- if (!(repstr = dm_size_to_string(mem, region_start, units, 1, factor,
- _show_units(), DM_SIZE_UNIT)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = (double) region_start;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static int _dm_stats_region_len_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- uint64_t region_length;
- const char *repstr;
- double *sortval;
- char units = _disp_units;
- uint64_t factor = _disp_factor;
-
- if (!dm_stats_get_current_region_len(dms, ®ion_length))
- return_0;
-
- if (!(repstr = dm_size_to_string(mem, region_length, units, 1, factor,
- _show_units(), DM_SIZE_UNIT)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = (double) region_length;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static int _dm_stats_area_id_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- uint64_t area_id = dm_stats_get_current_area(dms);
-
- if (dm_stats_current_object_type(dms) == DM_STATS_OBJECT_TYPE_GROUP)
- area_id = 0;
-
- return dm_report_field_uint64(rh, field, &area_id);
-}
-
-static int _dm_stats_area_start_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- uint64_t area_start;
- const char *repstr;
- double *sortval;
- char units = _disp_units;
- uint64_t factor = _disp_factor;
-
- if (!dm_stats_get_current_area_start(dms, &area_start))
- return_0;
-
- if (!(repstr = dm_size_to_string(mem, area_start, units, 1, factor,
- _show_units(), DM_SIZE_UNIT)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = (double) area_start;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static int _dm_stats_area_offset_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- uint64_t area_offset;
- const char *repstr;
- double *sortval;
- char units = _disp_units;
- uint64_t factor = _disp_factor;
-
- if (!dm_stats_get_current_area_offset(dms, &area_offset))
- return_0;
-
- if (!(repstr = dm_size_to_string(mem, area_offset, units, 1, factor,
- _show_units(), DM_SIZE_UNIT)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = (double) area_offset;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static int _dm_stats_area_len_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- uint64_t area_len;
- const char *repstr;
- double *sortval;
- char units = _disp_units;
- uint64_t factor = _disp_factor;
-
- if (!dm_stats_get_current_area_len(dms, &area_len))
- return_0;
-
- if (!(repstr = dm_size_to_string(mem, area_len, units, 1, factor,
- _show_units(), DM_SIZE_UNIT)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = (double) area_len;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static int _dm_stats_area_count_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- uint64_t area_count, region;
-
- region = dm_stats_get_current_region(dms);
- if (!(area_count = dm_stats_get_region_nr_areas(dms, region)))
- return_0;
-
- return dm_report_field_uint64(rh, field, &area_count);
-}
-
-static int _dm_stats_group_id_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- uint64_t group_id;
-
- group_id = dm_stats_get_group_id(dms,
- dm_stats_get_current_region(dms));
-
- if (!dm_stats_group_present(dms, group_id)) {
- dm_report_field_set_value(field, "-", &group_id);
- return 1;
- }
-
- return dm_report_field_uint64(rh, field, &group_id);
-}
-
-static int _dm_stats_program_id_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- const char *program_id;
- if (!(program_id = dm_stats_get_current_region_program_id(dms)))
- return_0;
- return dm_report_field_string(rh, field, (const char * const *) &program_id);
-}
-
-static int _dm_stats_user_data_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- const char *user_data;
- if (!(user_data = dm_stats_get_current_region_aux_data(dms)))
- return_0;
- return dm_report_field_string(rh, field, (const char * const *) &user_data);
-}
-
-static int _dm_stats_name_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- const char *stats_name;
- if (!(stats_name = dm_stats_get_alias(dms, DM_STATS_REGION_CURRENT)))
- return_0;
-
- return dm_report_field_string(rh, field, (const char * const *) &stats_name);
-}
-
-static int _dm_stats_object_type_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- int type = dm_stats_current_object_type(dms);
-
- return dm_report_field_string(rh, field, (const char * const *) &_stats_types[type]);
-}
-
-static int _dm_stats_precise_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- int precise;
- precise = dm_stats_get_current_region_precise_timestamps(dms);
- return dm_report_field_int(rh, field, (const int *) &precise);
-}
-
-static const char *_get_histogram_string(const struct dm_stats *dms, int rel,
- int vals, int bounds)
-{
- const struct dm_histogram *dmh;
- int flags = 0, width = (_switches[NOHEADINGS_ARG]) ? -1 : 0;
-
- if (!(dmh = dm_stats_get_histogram(dms, DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT)))
- return ""; /* No histogram. */
-
- flags |= (vals) ? DM_HISTOGRAM_VALUES
- : 0;
-
- flags |= bounds;
-
- flags |= (rel) ? DM_HISTOGRAM_PERCENT
- : 0;
-
- flags |= (_switches[NOTIMESUFFIX_ARG]) ? 0 : DM_HISTOGRAM_SUFFIX;
-
- /* FIXME: make unit conversion optional. */
- return dm_histogram_to_string(dmh, -1, width, flags);
-}
-
-static int _stats_hist_count_disp(struct dm_report *rh,
- struct dm_report_field *field, const void *data,
- int bounds)
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- const char *histogram;
-
- histogram = _get_histogram_string(dms, 0, 1, bounds); /* counts */
-
- if (!histogram)
- return_0;
-
- return dm_report_field_string(rh, field, (const char * const *) &histogram);
-}
-
-static int _dm_stats_hist_count_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- return _stats_hist_count_disp(rh, field, data, 0);
-}
-
-static int _dm_stats_hist_count_bounds_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- return _stats_hist_count_disp(rh, field, data, DM_HISTOGRAM_BOUNDS_LOWER);
-}
-
-static int _dm_stats_hist_count_ranges_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- return _stats_hist_count_disp(rh, field, data, DM_HISTOGRAM_BOUNDS_RANGE);
-}
-
-static int _stats_hist_percent_disp(struct dm_report *rh,
- struct dm_report_field *field, const void *data,
- int bounds)
-{
-
- /* FIXME: configurable to-string options. */
- const struct dm_stats *dms = (const struct dm_stats *) data;
- const char *histogram;
-
- histogram = _get_histogram_string(dms, 1, 1, bounds); /* relative values */
-
- if (!histogram)
- return_0;
-
- return dm_report_field_string(rh, field, (const char * const *) &histogram);
-}
-
-static int _dm_stats_hist_percent_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- return _stats_hist_percent_disp(rh, field, data, 0);
-}
-
-static int _dm_stats_hist_percent_bounds_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- return _stats_hist_percent_disp(rh, field, data, DM_HISTOGRAM_BOUNDS_LOWER);
-}
-
-static int _dm_stats_hist_percent_ranges_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- return _stats_hist_percent_disp(rh, field, data, DM_HISTOGRAM_BOUNDS_RANGE);
-}
-
-static int _stats_hist_bounds_disp(struct dm_report *rh,
- struct dm_report_field *field, const void *data,
- int bounds)
-{
- /* FIXME: configurable to-string options. */
- const struct dm_stats *dms = (const struct dm_stats *) data;
- const char *histogram;
-
- histogram = _get_histogram_string(dms, 0, 0, bounds);
-
- if (!histogram)
- return_0;
-
- return dm_report_field_string(rh, field, (const char * const *) &histogram);
-}
-
-static int _dm_stats_hist_bounds_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- return _stats_hist_bounds_disp(rh, field, data, DM_HISTOGRAM_BOUNDS_LOWER);
-}
-
-static int _dm_stats_hist_ranges_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- return _stats_hist_bounds_disp(rh, field, data, DM_HISTOGRAM_BOUNDS_RANGE);
-}
-
-static int _dm_stats_hist_bins_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- int bins;
- bins = dm_stats_get_region_nr_histogram_bins(dms, DM_STATS_REGION_CURRENT);
- return dm_report_field_int(rh, field, (const int *) &bins);
-}
-
-static int _dm_stats_rrqm_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- char buf[64];
- char *repstr;
- double *sortval, rrqm;
-
- if (!dm_stats_get_rd_merges_per_sec(dms, &rrqm,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
- if (dm_snprintf(buf, sizeof(buf), "%.2f", rrqm) < 0)
- return_0;
-
- if (!(repstr = dm_pool_strdup(mem, buf)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = rrqm;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-
-}
-
-static int _dm_stats_wrqm_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- char buf[64];
- char *repstr;
- double *sortval, wrqm;
-
- if (!dm_stats_get_wr_merges_per_sec(dms, &wrqm,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
- if (dm_snprintf(buf, sizeof(buf), "%.2f", wrqm) < 0)
- return_0;
-
- if (!(repstr = dm_pool_strdup(mem, buf)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = wrqm;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-
-}
-
-static int _dm_stats_rs_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- char buf[64];
- char *repstr;
- double *sortval, rs;
-
- if (!dm_stats_get_reads_per_sec(dms, &rs,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
- if (dm_snprintf(buf, sizeof(buf), "%.2f", rs) < 0)
- return_0;
-
- if (!(repstr = dm_pool_strdup(mem, buf)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = rs;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-
-}
-
-static int _dm_stats_ws_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- char buf[64];
- char *repstr;
- double *sortval, ws;
-
- if (!dm_stats_get_writes_per_sec(dms, &ws,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
- if (dm_snprintf(buf, sizeof(buf), "%.2f", ws) < 0)
- return_0;
-
- if (!(repstr = dm_pool_strdup(mem, buf)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = ws;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-
-}
-
-static int _dm_stats_read_secs_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- const char *repstr;
- double *sortval, rsec;
- char units = _disp_units;
- uint64_t factor = _disp_factor;
-
- if (!dm_stats_get_read_sectors_per_sec(dms, &rsec,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
- if (!(repstr = dm_size_to_string(mem, (uint64_t) rsec, units, 1,
- factor, _show_units(), DM_SIZE_UNIT)))
-
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = rsec;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static int _dm_stats_write_secs_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- const char *repstr;
- double *sortval, wsec;
- char units = _disp_units;
- uint64_t factor = _disp_factor;
-
- if (!dm_stats_get_write_sectors_per_sec(dms, &wsec,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
- if (!(repstr = dm_size_to_string(mem, (uint64_t) wsec, units, 1,
- factor, _show_units(), DM_SIZE_UNIT)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = wsec;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static int _dm_stats_arqsz_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- const char *repstr;
- double *sortval, arqsz;
- char units = _disp_units;
- uint64_t factor = _disp_factor;
-
- if (!dm_stats_get_average_request_size(dms, &arqsz,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
-
- if (!(repstr = dm_size_to_string(mem, (uint64_t) arqsz, units, 1,
- factor, _show_units(), DM_SIZE_UNIT)))
-
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = arqsz;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static int _dm_stats_qusz_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- char buf[64];
- char *repstr;
- double *sortval, qusz;
-
- if (!dm_stats_get_average_queue_size(dms, &qusz,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
- if (dm_snprintf(buf, sizeof(buf), "%.2f", qusz) < 0)
- return_0;
-
- if (!(repstr = dm_pool_strdup(mem, buf)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = qusz;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static int _dm_stats_await_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- char buf[64];
- char *repstr;
- double *sortval, await;
-
- if (!dm_stats_get_average_wait_time(dms, &await,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
- /* FIXME: make scale configurable */
- /* display in msecs */
- await /= NSEC_PER_MSEC;
-
- if (dm_snprintf(buf, sizeof(buf), "%.2f", await) < 0)
- return_0;
-
- if (!(repstr = dm_pool_strdup(mem, buf)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = await;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static int _dm_stats_r_await_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- char buf[64];
- char *repstr;
- double *sortval, r_await;
-
- if (!dm_stats_get_average_rd_wait_time(dms, &r_await,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
- /* FIXME: make scale configurable */
- /* display in msecs */
- r_await /= NSEC_PER_MSEC;
-
- if (dm_snprintf(buf, sizeof(buf), "%.2f", r_await) < 0)
- return_0;
-
- if (!(repstr = dm_pool_strdup(mem, buf)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = r_await;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static int _dm_stats_w_await_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- char buf[64];
- char *repstr;
- double *sortval, w_await;
-
- if (!dm_stats_get_average_wr_wait_time(dms, &w_await,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
- /* FIXME: make scale configurable */
- /* display in msecs */
- w_await /= NSEC_PER_MSEC;
-
- if (dm_snprintf(buf, sizeof(buf), "%.2f", w_await) < 0)
- return_0;
-
- if (!(repstr = dm_pool_strdup(mem, buf)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = w_await;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static int _dm_stats_tput_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- char buf[64];
- char *repstr;
- double *sortval, tput;
-
- if (!dm_stats_get_throughput(dms, &tput,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
- if (dm_snprintf(buf, sizeof(buf), "%.2f", tput) < 0)
- return_0;
-
- if (!(repstr = dm_pool_strdup(mem, buf)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = tput;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static int _dm_stats_svctm_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- char buf[64];
- char *repstr;
- double *sortval, svctm;
-
- if (!dm_stats_get_service_time(dms, &svctm,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
- /* FIXME: make scale configurable */
- /* display in msecs */
- svctm /= NSEC_PER_MSEC;
-
- if (dm_snprintf(buf, sizeof(buf), "%.2f", svctm) < 0)
- return_0;
-
- if (!(repstr = dm_pool_strdup(mem, buf)))
- return_0;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t))))
- return_0;
-
- *sortval = svctm;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-
-}
-
-static int _dm_stats_util_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- const struct dm_stats *dms = (const struct dm_stats *) data;
- dm_percent_t util;
-
- if (!dm_stats_get_utilization(dms, &util,
- DM_STATS_REGION_CURRENT,
- DM_STATS_AREA_CURRENT))
- return_0;
-
- dm_report_field_percent(rh, field, &util);
- return 1;
-}
-
-static int _dm_stats_sample_interval_ns_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- /* FIXME: use internal interval estimate when supported by libdm */
- return dm_report_field_uint64(rh, field, &_last_interval);
-}
-
-static int _dm_stats_sample_interval_disp(struct dm_report *rh,
- struct dm_pool *mem __attribute__((unused)),
- struct dm_report_field *field, const void *data,
- void *private __attribute__((unused)))
-{
- char buf[64];
- char *repstr;
- double *sortval;
-
- if (!(sortval = dm_pool_alloc(mem, sizeof(*sortval))))
- return_0;
-
- *sortval = (double)_last_interval / (double) NSEC_PER_SEC;
-
- if (dm_snprintf(buf, sizeof(buf), "%2.6f", *sortval) < 0)
- return_0;
-
- if (!(repstr = dm_pool_strdup(mem, buf)))
- return_0;
-
- dm_report_field_set_value(field, repstr, sortval);
- return 1;
-}
-
-static void *_task_get_obj(void *obj)
-{
- return ((struct dmsetup_report_obj *)obj)->task;
-}
-
-static void *_info_get_obj(void *obj)
-{
- return ((struct dmsetup_report_obj *)obj)->info;
-}
-
-static void *_deps_get_obj(void *obj)
-{
- return dm_task_get_deps(((struct dmsetup_report_obj *)obj)->deps_task);
-}
-
-static void *_tree_get_obj(void *obj)
-{
- return ((struct dmsetup_report_obj *)obj)->tree_node;
-}
-
-static void *_split_name_get_obj(void *obj)
-{
- return ((struct dmsetup_report_obj *)obj)->split_name;
-}
-
-static void *_stats_get_obj(void *obj)
-{
- return ((struct dmsetup_report_obj *)obj)->stats;
-}
-
-static const struct dm_report_object_type _report_types[] = {
- { DR_TASK, "Mapped Device Name", "name_", _task_get_obj },
- { DR_INFO, "Mapped Device Information", "info_", _info_get_obj },
- { DR_DEPS, "Mapped Device Relationship Information", "deps_", _deps_get_obj },
- { DR_TREE, "Mapped Device Relationship Information", "tree_", _tree_get_obj },
- { DR_NAME, "Mapped Device Name Components", "splitname_", _split_name_get_obj },
- { DR_STATS, "Mapped Device Statistics","stats_", _stats_get_obj },
- { DR_STATS_META, "Mapped Device Statistics Region Information","region_", _stats_get_obj },
- { 0, "", "", NULL }
-};
-
-/* Column definitions */
-/* N.B. Field names must not contain the substring 'help' as this will disable --count. */
-#define OFFSET_OF(strct, field) (((char*)&((struct strct*)0)->field) - (char*)0)
-#define STR (DM_REPORT_FIELD_TYPE_STRING)
-#define NUM (DM_REPORT_FIELD_TYPE_NUMBER)
-#define SIZ (DM_REPORT_FIELD_TYPE_SIZE)
-#define TIM (DM_REPORT_FIELD_TYPE_TIME)
-#define FIELD_O(type, strct, sorttype, head, field, width, func, id, desc) {DR_ ## type, sorttype, OFFSET_OF(strct, field), width, id, head, &_ ## func ## _disp, desc},
-#define FIELD_F(type, sorttype, head, width, func, id, desc) {DR_ ## type, sorttype, 0, width, id, head, &_ ## func ## _disp, desc},
-
-static const struct dm_report_field_type _report_fields[] = {
-/* *INDENT-OFF* */
-FIELD_F(TASK, STR, "Name", 16, dm_name, "name", "Name of mapped device.")
-FIELD_F(TASK, STR, "MangledName", 16, dm_mangled_name, "mangled_name", "Mangled name of mapped device.")
-FIELD_F(TASK, STR, "UnmangledName", 16, dm_unmangled_name, "unmangled_name", "Unmangled name of mapped device.")
-FIELD_F(TASK, STR, "UUID", 32, dm_uuid, "uuid", "Unique (optional) identifier for mapped device.")
-FIELD_F(TASK, STR, "MangledUUID", 32, dm_mangled_uuid, "mangled_uuid", "Mangled unique (optional) identifier for mapped device.")
-FIELD_F(TASK, STR, "UnmangledUUID", 32, dm_unmangled_uuid, "unmangled_uuid", "Unmangled unique (optional) identifier for mapped device.")
-
-/* FIXME Next one should be INFO */
-FIELD_F(TASK, NUM, "RAhead", 6, dm_read_ahead, "read_ahead", "Read ahead value.")
-
-FIELD_F(INFO, STR, "BlkDevName", 16, dm_blk_name, "blkdevname", "Name of block device.")
-FIELD_F(INFO, STR, "Stat", 4, dm_info_status, "attr", "(L)ive, (I)nactive, (s)uspended, (r)ead-only, read-(w)rite.")
-FIELD_F(INFO, STR, "Tables", 6, dm_info_table_loaded, "tables_loaded", "Which of the live and inactive table slots are filled.")
-FIELD_F(INFO, STR, "Suspended", 9, dm_info_suspended, "suspended", "Whether the device is suspended.")
-FIELD_F(INFO, STR, "Read-only", 9, dm_info_read_only, "readonly", "Whether the device is read-only or writeable.")
-FIELD_F(INFO, STR, "DevNo", 5, dm_info_devno, "devno", "Device major and minor numbers")
-FIELD_O(INFO, dm_info, NUM, "Maj", major, 3, int32, "major", "Block device major number.")
-FIELD_O(INFO, dm_info, NUM, "Min", minor, 3, int32, "minor", "Block device minor number.")
-FIELD_O(INFO, dm_info, NUM, "Open", open_count, 4, int32, "open", "Number of references to open device, if requested.")
-FIELD_O(INFO, dm_info, NUM, "Targ", target_count, 4, int32, "segments", "Number of segments in live table, if present.")
-FIELD_O(INFO, dm_info, NUM, "Event", event_nr, 6, uint32, "events", "Number of most recent event.")
-
-FIELD_O(DEPS, dm_deps, NUM, "#Devs", count, 5, int32, "device_count", "Number of devices used by this one.")
-FIELD_F(TREE, STR, "DevNamesUsed", 16, dm_deps_names, "devs_used", "List of names of mapped devices used by this one.")
-FIELD_F(DEPS, STR, "DevNosUsed", 16, dm_deps, "devnos_used", "List of device numbers of devices used by this one.")
-FIELD_F(DEPS, STR, "BlkDevNamesUsed", 16, dm_deps_blk_names, "blkdevs_used", "List of names of block devices used by this one.")
-
-FIELD_F(TREE, NUM, "#Refs", 5, dm_tree_parents_count, "device_ref_count", "Number of mapped devices referencing this one.")
-FIELD_F(TREE, STR, "RefNames", 8, dm_tree_parents_names, "names_using_dev", "List of names of mapped devices using this one.")
-FIELD_F(TREE, STR, "RefDevNos", 9, dm_tree_parents_devs, "devnos_using_dev", "List of device numbers of mapped devices using this one.")
-
-FIELD_O(NAME, dm_split_name, STR, "Subsys", subsystem, 6, dm_subsystem, "subsystem", "Userspace subsystem responsible for this device.")
-FIELD_O(NAME, dm_split_name, STR, "VG", vg_name, 4, dm_vg_name, "vg_name", "LVM Volume Group name.")
-FIELD_O(NAME, dm_split_name, STR, "LV", lv_name, 4, dm_lv_name, "lv_name", "LVM Logical Volume name.")
-FIELD_O(NAME, dm_split_name, STR, "LVLayer", lv_layer, 7, dm_lv_layer_name, "lv_layer", "LVM device layer.")
-
-/* basic stats counters */
-FIELD_F(STATS, NUM, "#Reads", 6, dm_stats_reads, "read_count", "Count of reads completed.")
-FIELD_F(STATS, NUM, "#RdMrgs", 7, dm_stats_reads_merged, "reads_merged_count", "Count of read requests merged.")
-FIELD_F(STATS, NUM, "#RdSectors", 10, dm_stats_read_sectors, "read_sector_count", "Count of sectors read.")
-FIELD_F(STATS, NUM, "AccRdTime", 11, dm_stats_read_nsecs, "read_time", "Accumulated duration of all read requests (ns).")
-FIELD_F(STATS, NUM, "#Writes", 7, dm_stats_writes, "write_count", "Count of writes completed.")
-FIELD_F(STATS, NUM, "#WrMrgs", 7, dm_stats_writes_merged, "writes_merged_count", "Count of write requests merged.")
-FIELD_F(STATS, NUM, "#WrSectors", 10, dm_stats_write_sectors, "write_sector_count", "Count of sectors written.")
-FIELD_F(STATS, NUM, "AccWrTime", 11, dm_stats_write_nsecs, "write_time", "Accumulated duration of all writes (ns).")
-FIELD_F(STATS, NUM, "#InProg", 7, dm_stats_io_in_progress, "in_progress_count", "Count of requests currently in progress.")
-FIELD_F(STATS, NUM, "IoTicks", 7, dm_stats_io_nsecs, "io_ticks", "Nanoseconds spent servicing requests.")
-FIELD_F(STATS, NUM, "QueueTicks", 10, dm_stats_weighted_io_nsecs, "queue_ticks", "Total nanoseconds spent in queue.")
-FIELD_F(STATS, NUM, "RdTicks", 7, dm_stats_total_read_nsecs, "read_ticks", "Nanoseconds spent servicing reads.")
-FIELD_F(STATS, NUM, "WrTicks", 7, dm_stats_total_write_nsecs, "write_ticks", "Nanoseconds spent servicing writes.")
-
-/* Stats derived metrics */
-FIELD_F(STATS, NUM, "RMrg/s", 6, dm_stats_rrqm, "reads_merged_per_sec", "Read requests merged per second.")
-FIELD_F(STATS, NUM, "WMrg/s", 6, dm_stats_wrqm, "writes_merged_per_sec", "Write requests merged per second.")
-FIELD_F(STATS, NUM, "R/s", 3, dm_stats_rs, "reads_per_sec", "Reads per second.")
-FIELD_F(STATS, NUM, "W/s", 3, dm_stats_ws, "writes_per_sec", "Writes per second.")
-FIELD_F(STATS, NUM, "RSz/s", 5, dm_stats_read_secs, "read_size_per_sec", "Size of data read per second.")
-FIELD_F(STATS, NUM, "WSz/s", 5, dm_stats_write_secs, "write_size_per_sec", "Size of data written per second.")
-FIELD_F(STATS, NUM, "AvgRqSz", 7, dm_stats_arqsz, "avg_request_size", "Average request size.")
-FIELD_F(STATS, NUM, "QSize", 5, dm_stats_qusz, "queue_size", "Average queue size.")
-FIELD_F(STATS, NUM, "AWait", 5, dm_stats_await, "await", "Averate wait time.")
-FIELD_F(STATS, NUM, "RdAWait", 7, dm_stats_r_await, "read_await", "Averate read wait time.")
-FIELD_F(STATS, NUM, "WrAWait", 7, dm_stats_w_await, "write_await", "Averate write wait time.")
-FIELD_F(STATS, NUM, "Throughput", 10, dm_stats_tput, "throughput", "Throughput.")
-FIELD_F(STATS, NUM, "SvcTm", 5, dm_stats_svctm, "service_time", "Service time.")
-FIELD_F(STATS, NUM, "Util%", 5, dm_stats_util, "util", "Utilization.")
-
-/* Histogram fields */
-FIELD_F(STATS, STR, "Histogram Counts", 16, dm_stats_hist_count, "hist_count", "Latency histogram counts.")
-FIELD_F(STATS, STR, "Histogram Counts", 16, dm_stats_hist_count_bounds, "hist_count_bounds", "Latency histogram counts with bin boundaries.")
-FIELD_F(STATS, STR, "Histogram Counts", 16, dm_stats_hist_count_ranges, "hist_count_ranges", "Latency histogram counts with bin ranges.")
-FIELD_F(STATS, STR, "Histogram%", 10, dm_stats_hist_percent, "hist_percent", "Relative latency histogram.")
-FIELD_F(STATS, STR, "Histogram%", 10, dm_stats_hist_percent_bounds, "hist_percent_bounds", "Relative latency histogram with bin boundaries.")
-FIELD_F(STATS, STR, "Histogram%", 10, dm_stats_hist_percent_ranges, "hist_percent_ranges", "Relative latency histogram with bin ranges.")
-
-/* Stats interval duration estimates */
-FIELD_F(STATS, NUM, "IntervalNs", 10, dm_stats_sample_interval_ns, "interval_ns", "Sampling interval in nanoseconds.")
-FIELD_F(STATS, NUM, "Interval", 8, dm_stats_sample_interval, "interval", "Sampling interval.")
-
-/* Stats report meta-fields */
-FIELD_F(STATS_META, NUM, "RgID", 4, dm_stats_region_id, "region_id", "Region ID.")
-FIELD_F(STATS_META, SIZ, "RgStart", 7, dm_stats_region_start, "region_start", "Region start.")
-FIELD_F(STATS_META, SIZ, "RgSize", 6, dm_stats_region_len, "region_len", "Region length.")
-FIELD_F(STATS_META, NUM, "ArID", 4, dm_stats_area_id, "area_id", "Area ID.")
-FIELD_F(STATS_META, SIZ, "ArStart", 7, dm_stats_area_start, "area_start", "Area offset from start of device.")
-FIELD_F(STATS_META, SIZ, "ArSize", 6, dm_stats_area_len, "area_len", "Area length.")
-FIELD_F(STATS_META, SIZ, "ArOff", 5, dm_stats_area_offset, "area_offset", "Area offset from start of region.")
-FIELD_F(STATS_META, NUM, "#Areas", 6, dm_stats_area_count, "area_count", "Area count.")
-FIELD_F(STATS_META, NUM, "GrpID", 5, dm_stats_group_id, "group_id", "Group ID.")
-FIELD_F(STATS_META, STR, "ProgID", 6, dm_stats_program_id, "program_id", "Program ID.")
-FIELD_F(STATS_META, STR, "UserData", 8, dm_stats_user_data, "user_data", "Auxiliary data.")
-FIELD_F(STATS_META, STR, "Precise", 7, dm_stats_precise, "precise", "Set if nanosecond precision counters are enabled.")
-FIELD_F(STATS_META, STR, "#Bins", 9, dm_stats_hist_bins, "hist_bins", "The number of histogram bins configured.")
-FIELD_F(STATS_META, STR, "Histogram Bounds", 16, dm_stats_hist_bounds, "hist_bounds", "Latency histogram bin boundaries.")
-FIELD_F(STATS_META, STR, "Histogram Ranges", 16, dm_stats_hist_ranges, "hist_ranges", "Latency histogram bin ranges.")
-FIELD_F(STATS_META, STR, "Name", 16, dm_stats_name, "stats_name", "Stats name of current object.")
-FIELD_F(STATS_META, STR, "ObjType", 7, dm_stats_object_type, "obj_type", "Type of stats object being reported.")
-{0, 0, 0, 0, "", "", NULL, NULL},
-/* *INDENT-ON* */
-};
-
-#undef FIELD_O
-#undef FIELD_F
-
-#undef STR
-#undef NUM
-#undef SIZ
-
-static const char *_default_report_options = "name,major,minor,attr,open,segments,events,uuid";
-static const char *_splitname_report_options = "vg_name,lv_name,lv_layer";
-
-/* Stats counters & derived metrics. */
-#define RD_COUNTERS "read_count,reads_merged_count,read_sector_count,read_time,read_ticks"
-#define WR_COUNTERS "write_count,writes_merged_count,write_sector_count,write_time,write_ticks"
-#define IO_COUNTERS "in_progress_count,io_ticks,queue_ticks"
-#define COUNTERS RD_COUNTERS "," WR_COUNTERS "," IO_COUNTERS
-
-#define METRICS "reads_merged_per_sec,writes_merged_per_sec," \
- "reads_per_sec,writes_per_sec," \
- "read_size_per_sec,write_size_per_sec," \
- "avg_request_size,queue_size,util," \
- "await,read_await,write_await"
-
-/* Device, region and area metadata. */
-#define STATS_DEV_INFO "statsname,group_id,region_id,obj_type"
-#define STATS_AREA_INFO "area_id,area_start,area_len"
-#define STATS_AREA_INFO_FULL STATS_DEV_INFO ",region_start,region_len,area_count,area_id,area_start,area_len"
-#define STATS_REGION_INFO STATS_DEV_INFO ",region_start,region_len,area_count,area_len"
-
-/* Minimal set of fields for histogram report. */
-#define STATS_HIST STATS_REGION_INFO ",util,await"
-
-/* Default stats report options. */
-static const char *_stats_default_report_options = STATS_DEV_INFO "," STATS_AREA_INFO "," METRICS;
-static const char *_stats_raw_report_options = STATS_DEV_INFO "," STATS_AREA_INFO "," COUNTERS;
-static const char *_stats_list_options = STATS_REGION_INFO ",program_id";
-static const char *_stats_area_list_options = STATS_AREA_INFO_FULL ",program_id";
-static const char *_stats_hist_list_options = STATS_REGION_INFO ",hist_bins,hist_bounds";
-static const char *_stats_hist_area_list_options = STATS_AREA_INFO_FULL ",hist_bins,hist_bounds";
-static const char *_stats_hist_options = STATS_HIST ",hist_count_bounds";
-static const char *_stats_hist_relative_options = STATS_HIST ",hist_percent_bounds";
-
-static int _report_init(const struct command *cmd, const char *subcommand)
-{
- char *options = (char *) _default_report_options;
- char *opt_fields = NULL; /* optional fields from command line */
- const char *keys = "";
- const char *separator = " ";
- const char *selection = NULL;
- int aligned = 1, headings = 1, buffered = 1, field_prefixes = 0;
- int quoted = 1, columns_as_rows = 0;
- uint32_t flags = 0;
- size_t len = 0;
- int r = 0;
-
- if (cmd && !strcmp(cmd->name, "splitname")) {
- options = (char *) _splitname_report_options;
- _report_type |= DR_NAME;
- }
-
- if (cmd && !strcmp(cmd->name, "stats")) {
- _report_type |= DR_STATS_META;
- if (!strcmp(subcommand, "list")) {
- if (!_switches[HISTOGRAM_ARG])
- options = (char *) ((_switches[VERBOSE_ARG])
- ? _stats_area_list_options
- : _stats_list_options);
- else
- options = (char *) ((_switches[VERBOSE_ARG])
- ? _stats_hist_area_list_options
- : _stats_hist_list_options);
- } else {
- if (_switches[HISTOGRAM_ARG])
- options = (char *) ((_switches[RELATIVE_ARG])
- ? _stats_hist_relative_options
- : _stats_hist_options);
- else
- options = (char *) ((!_switches[RAW_ARG])
- ? _stats_default_report_options
- : _stats_raw_report_options);
- _report_type |= DR_STATS;
- }
- }
-
- if (cmd && !strcmp(cmd->name, "list")) {
- options = (char *) _stats_list_options;
- _report_type |= DR_STATS_META;
- }
-
- /* emulate old dmsetup behaviour */
- if (_switches[NOHEADINGS_ARG]) {
- separator = ":";
- aligned = 0;
- headings = 0;
- }
-
- if (_switches[UNBUFFERED_ARG])
- buffered = 0;
-
- if (_switches[ROWS_ARG])
- columns_as_rows = 1;
-
- if (_switches[UNQUOTED_ARG])
- quoted = 0;
-
- if (_switches[NAMEPREFIXES_ARG]) {
- aligned = 0;
- field_prefixes = 1;
- }
-
- if (_switches[OPTIONS_ARG] && _string_args[OPTIONS_ARG]) {
- /* Count & interval forbidden for help. */
- /* FIXME Detect "help" correctly and exit */
- if (strstr(_string_args[OPTIONS_ARG], "help")) {
- _switches[COUNT_ARG] = 0;
- _count = 1;
- _switches[INTERVAL_ARG] = 0;
- headings = 0;
- }
-
- if (*_string_args[OPTIONS_ARG] != '+')
- options = _string_args[OPTIONS_ARG];
- else {
- char *tmpopts;
- opt_fields = _string_args[OPTIONS_ARG] + 1;
- len = strlen(options) + strlen(opt_fields) + 2;
- if (!(tmpopts = malloc(len))) {
- log_error("Failed to allocate option string.");
- return 0;
- }
- if (dm_snprintf(tmpopts, len, "%s,%s",
- options, opt_fields) < 0) {
- free(tmpopts);
- return 0;
- }
- options = tmpopts;
- }
- }
-
- if (_switches[SORT_ARG] && _string_args[SORT_ARG]) {
- keys = _string_args[SORT_ARG];
- buffered = 1;
- if (cmd && (!strcmp(cmd->name, "status") || !strcmp(cmd->name, "table"))) {
- log_error("--sort is not yet supported with status and table.");
- goto out;
- }
- }
-
- if (_switches[SEPARATOR_ARG] && _string_args[SEPARATOR_ARG]) {
- separator = _string_args[SEPARATOR_ARG];
- aligned = 0;
- }
-
- if (_switches[SELECT_ARG] && _string_args[SELECT_ARG])
- selection = _string_args[SELECT_ARG];
-
- if (aligned)
- flags |= DM_REPORT_OUTPUT_ALIGNED;
-
- if (buffered)
- flags |= DM_REPORT_OUTPUT_BUFFERED;
-
- if (headings)
- flags |= DM_REPORT_OUTPUT_HEADINGS;
-
- if (field_prefixes)
- flags |= DM_REPORT_OUTPUT_FIELD_NAME_PREFIX;
-
- if (!quoted)
- flags |= DM_REPORT_OUTPUT_FIELD_UNQUOTED;
-
- if (columns_as_rows)
- flags |= DM_REPORT_OUTPUT_COLUMNS_AS_ROWS;
-
- if (!(_report = dm_report_init_with_selection(&_report_type, _report_types,
- _report_fields, options, separator, flags, keys,
- selection, NULL, NULL)))
- goto_out;
-
- r = 1;
-
- if ((_report_type & DR_TREE) && cmd) {
- r = _build_whole_deptree(cmd);
- if (!_dtree) {
- log_error("Internal device dependency tree creation failed.");
- goto out;
- }
- }
-
- if (!_switches[INTERVAL_ARG])
- _int_args[INTERVAL_ARG] = 1; /* 1s default. */
-
- _interval = NSEC_PER_SEC * (uint64_t) _int_args[INTERVAL_ARG];
-
- if (field_prefixes)
- dm_report_set_output_field_name_prefix(_report, "dm_");
-
-out:
- if (len)
- free(options);
-
- return r;
-}
-
-/*
- * List devices
- */
-static int _ls(CMD_ARGS)
-{
- if ((_switches[TARGET_ARG] && _target) ||
- (_switches[EXEC_ARG] && _command_to_exec))
- return _status(cmd, NULL, argc, argv, NULL, 0);
-
- if ((_switches[TREE_ARG]))
- return _display_tree(cmd, NULL, 0, NULL, NULL, 0);
-
- return _process_all(cmd, NULL, argc, argv, 0, _display_name);
-}
-
-static int _mangle(CMD_ARGS)
-{
- const char *name, *uuid;
- char *new_name = NULL, *new_uuid = NULL;
- struct dm_task *dmt;
- struct dm_info info;
- int r = 0;
- int target_format;
-
- if (names)
- name = names->name;
- else {
- if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
- return _process_all(cmd, NULL, argc, argv, 0, _mangle);
- name = argv[0];
- }
-
- if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
- return_0;
-
- if (!(_set_task_device(dmt, name, 0)))
- goto_out;
-
- if (!_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- if (!_task_run(dmt))
- goto_out;
-
- if (!dm_task_get_info(dmt, &info) || !info.exists)
- goto_out;
-
- uuid = dm_task_get_uuid(dmt);
-
- target_format = _switches[MANGLENAME_ARG] ? _int_args[MANGLENAME_ARG]
- : DEFAULT_DM_NAME_MANGLING;
-
- if (target_format == DM_STRING_MANGLING_AUTO) {
- if (strstr(name, "\\x5cx")) {
- log_error("The name \"%s\" seems to be mangled more than once. "
- "Manual intervention required to rename the device.", name);
- goto out;
- }
- if (strstr(uuid, "\\x5cx")) {
- log_error("The UUID \"%s\" seems to be mangled more than once. "
- "Manual intervention required to correct the device UUID.", uuid);
- goto out;
- }
- }
-
- if (target_format == DM_STRING_MANGLING_NONE) {
- if (!(new_name = dm_task_get_name_unmangled(dmt)))
- goto_out;
- if (!(new_uuid = dm_task_get_uuid_unmangled(dmt)))
- goto_out;
- }
- else {
- if (!(new_name = dm_task_get_name_mangled(dmt)))
- goto_out;
- if (!(new_uuid = dm_task_get_uuid_mangled(dmt)))
- goto_out;
- }
-
- /* We can't rename the UUID, the device must be reactivated manually. */
- if (strcmp(uuid, new_uuid)) {
- log_error("%s: %s: UUID in incorrect form. ", name, uuid);
- log_error("Unable to change device UUID. The device must be deactivated first.");
- r = 0;
- goto out;
- }
-
- /* Nothing to do if the name is in correct form already. */
- if (!strcmp(name, new_name)) {
- log_print("%s: %s: name %salready in correct form", name,
- *uuid ? uuid : "[no UUID]", *uuid ? "and UUID " : "");
- r = 1;
- goto out;
- }
- else
- log_print("%s: renaming to %s", name, new_name);
-
- /* Rename to correct form of the name. */
- r = _do_rename(name, new_name, NULL);
-
-out:
- free(new_name);
- free(new_uuid);
- dm_task_destroy(dmt);
- return r;
-}
-
-static int _stats(CMD_ARGS);
-static int _bind_stats_device(struct dm_stats *dms, const char *name)
-{
- if (name && !dm_stats_bind_name(dms, name))
- return_0;
- else if (_switches[UUID_ARG] && !dm_stats_bind_uuid(dms, _uuid))
- return_0;
- else if (_switches[MAJOR_ARG] && _switches[MINOR_ARG]
- && !dm_stats_bind_devno(dms, _int_args[MAJOR_ARG],
- _int_args[MINOR_ARG]))
- return_0;
-
- return 1;
-}
-
-static int _stats_clear_one_region(struct dm_stats *dms, uint64_t region_id)
-{
-
- if (!dm_stats_region_present(dms, region_id)) {
- log_error("No such region: %"PRIu64".", region_id);
- return 0;
- }
- if (!dm_stats_clear_region(dms, region_id)) {
- log_error("Clearing statistics region %"PRIu64" failed.",
- region_id);
- return 0;
- }
- log_info("Cleared statistics region %"PRIu64".", region_id);
- return 1;
-}
-
-static int _stats_clear_regions(struct dm_stats *dms, uint64_t region_id)
-{
- int allregions = (region_id == DM_STATS_REGIONS_ALL);
-
- if (!dm_stats_list(dms, NULL))
- return_0;
-
- if (!dm_stats_get_nr_regions(dms))
- return 1;
-
- if (!allregions)
- return _stats_clear_one_region(dms, region_id);
-
- dm_stats_foreach_region(dms) {
- region_id = dm_stats_get_current_region(dms);
- if (!_stats_clear_one_region(dms, region_id))
- return_0;
- }
-
- return 1;
-}
-
-static int _stats_clear(CMD_ARGS)
-{
- struct dm_stats *dms;
- uint64_t region_id;
- char *name = NULL;
- int allregions = _switches[ALL_REGIONS_ARG];
-
- /* clear does not use a report */
- if (_report) {
- dm_report_free(_report);
- _report = NULL;
- }
-
- if (!_switches[REGION_ID_ARG] && !_switches[ALL_REGIONS_ARG]) {
- log_error("Please specify a --regionid or use --allregions.");
- return 0;
- }
-
- if (names)
- name = names->name;
- else {
- if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
- return _process_all(cmd, subcommand, argc, argv, 0, _stats_clear);
- name = argv[0];
- }
-
- region_id = (allregions) ? DM_STATS_REGIONS_ALL
- : (uint64_t) _int_args[REGION_ID_ARG];
-
- if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
- return_0;
-
- if (!_bind_stats_device(dms, name))
- goto_out;
-
- if (!_stats_clear_regions(dms, region_id))
- goto_out;
-
- dm_stats_destroy(dms);
- return 1;
-
-out:
- dm_stats_destroy(dms);
- return 0;
-}
-
-static uint64_t _factor_from_units(char *argptr, char *unit_type)
-{
- return dm_units_to_factor(argptr, unit_type, 0, NULL);
-}
-
-/**
- * Parse a start, length, or area size argument in bytes from a string
- * using optional units as supported by _factor_from_units().
- */
-static int _size_from_string(char *argptr, uint64_t *size, const char *name)
-{
- uint64_t factor;
- char *endptr = NULL, unit_type;
- if (!argptr)
- return_0;
-
- *size = strtoull(argptr, &endptr, 10);
- if (endptr == argptr) {
- *size = 0;
- log_error("Invalid %s argument: \"%s\"",
- name, (*argptr) ? argptr : "");
- return 0;
- }
-
- if (*endptr == '\0') {
- *size *= 512;
- return 1;
- }
-
- factor = _factor_from_units(endptr, &unit_type);
- if (factor)
- *size *= factor;
-
- return 1;
-}
-
-/*
- * FIXME: expose this from libdm-stats
- */
-static uint64_t _nr_areas_from_step(uint64_t len, int64_t step)
-{
- /* Default is one area. */
- if (!step || !len)
- return 1;
-
- /* --areas */
- if (step < 0)
- return (uint64_t)(-step);
-
- /* --areasize - cast step to unsigned as it cannot be -ve here. */
- return (len / step) + !!(len % (uint64_t) step);
-}
-
-/* maximum length of a string representation of an integer */
-#define max_int_strlen(i) (strlen(#i))
-#define MAX_UINT64_STRLEN max_int_strlen(UINT64_MAX)
-static int _stats_group_segments(struct dm_stats *dms, uint64_t *region_ids,
- int count, const char *alias)
-{
- /* NULL, commas, and count * region_id */
- size_t bufsize = 1 + count + count * MAX_UINT64_STRLEN;
- char *this_region, *regions = NULL;
- uint64_t group_id;
- int r, i;
-
- this_region = regions = malloc(bufsize);
-
- if (!regions) {
- log_error("Could not allocate memory for region_id table.");
- return 0;
- }
-
- for (i = 0; i < count; i++) {
- /*
- * We don't expect large numbers of segments (compared to e.g.
- * --filemap): use a fixed-size buffer based on the number of
- * region identifiers and do not collapse continuous ranges
- * of identifiers in the group descriptor argument.
- */
- r = dm_snprintf(this_region, bufsize, FMTu64 "%s", region_ids[i],
- (i < (count - 1)) ? "," : "");
- if (r < 0)
- goto_bad;
- this_region += r;
- bufsize -= r;
- }
-
- /* refresh handle */
- if (!(r = dm_stats_list(dms, NULL)))
- goto bad;
-
- if ((r = dm_stats_create_group(dms, regions, alias, &group_id)))
- printf("Grouped regions %s as group ID " FMTu64 "%s%s\n",
- regions, group_id, (alias) ? " with alias " : "",
- (alias) ? : "");
- else
- log_error("Failed to create group for regions %s.", regions);
-
-bad:
- free(regions);
- return r;
-}
-
-/*
- * Create a single region starting at start and spanning len sectors,
- * or, if the segments argument is no-zero create one region for each
- * segment present in the mapped device. Passing zero for segments,
- * start, and length will create a single segment spanning the whole
- * device.
- */
-static int _do_stats_create_regions(struct dm_stats *dms,
- const char *name, uint64_t start,
- uint64_t len, int64_t step,
- int segments,
- const char *program_id,
- const char *user_data)
-{
- uint64_t this_start = 0, this_len = len, region_id = UINT64_C(0);
- const char *devname = NULL, *histogram = _string_args[BOUNDS_ARG];
- int r = 0, count = 0, precise = _switches[PRECISE_ARG];
- struct dm_histogram *bounds = NULL; /* histogram bounds */
- uint64_t *region_ids = NULL; /* segments */
- char *target_type, *params; /* unused */
- struct dm_task *dmt;
- struct dm_info info;
- void *next = NULL;
-
- if (_switches[ALIAS_ARG] && _switches[NOGROUP_ARG]) {
- log_error("Cannot set alias with --nogroup.");
- dm_stats_destroy(dms);
- return 0;
- }
-
- if (histogram && !(bounds = dm_histogram_bounds_from_string(histogram)))
- return_0;
-
- if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) {
- dm_histogram_bounds_destroy(bounds);
- dm_stats_destroy(dms);
- return_0;
- }
-
- if (!_set_task_device(dmt, name, 0))
- goto_out;
-
- if (!dm_task_no_open_count(dmt))
- goto_out;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- if (!_task_run(dmt))
- goto_out;
-
- if (!dm_task_get_info(dmt, &info) || !info.exists)
- goto_out;
-
- if (!(devname = dm_task_get_name(dmt)))
- goto_out;
-
- if (!segments || (info.target_count == 1))
- region_ids = ®ion_id;
- else
- region_ids = malloc(info.target_count * sizeof(*region_ids));
-
- do {
- uint64_t segment_start, segment_len;
- next = dm_get_next_target(dmt, next, &segment_start, &segment_len,
- &target_type, ¶ms);
-
- /* Accumulate whole-device size for nr_areas calculation. */
- if (!segments && !len)
- this_len += segment_len;
-
- /* Segments or whole-device. */
- if (segments || !next) {
- /*
- * this_start and this_len hold the start and length in
- * sectors of the to-be-created region: this is either the
- * segment start/len (for --segments), the value of the
- * --start/--length arguments, or 0/0 for a default
- * whole-device region).
- */
- this_start = (segments) ? segment_start : start;
- this_len = (segments) ? segment_len : this_len;
- if (!(r = dm_stats_create_region(dms, ®ion_ids[count],
- this_start, this_len, step,
- precise, bounds,
- program_id, user_data))) {
- log_error("%s: Could not create statistics region.",
- devname);
- goto out;
- }
-
- printf("%s: Created new region with "FMTu64" area(s) as "
- "region ID "FMTu64"\n", devname,
- _nr_areas_from_step(this_len, step),
- region_ids[count++]);
- }
- } while (next);
-
- if (!_switches[NOGROUP_ARG] && segments)
- r = _stats_group_segments(dms, region_ids, count,
- _string_args[ALIAS_ARG]);
-
-out:
- if (region_ids != ®ion_id)
- free(region_ids);
-
- dm_task_destroy(dmt);
- dm_stats_destroy(dms);
- dm_histogram_bounds_destroy(bounds);
- return r;
-}
-
-/*
- * Returns the full absolute path, or NULL if the path could
- * not be resolved.
- */
-static char *_get_abspath(const char *path)
-{
- char *_path;
-
-#ifdef HAVE_CANONICALIZE_FILE_NAME
- _path = canonicalize_file_name(path);
-#else
- /* FIXME Provide alternative */
- log_error(INTERNAL_ERROR "Unimplemented _get_abspath.");
- _path = NULL;
-#endif
- return _path;
-}
-
-static int _stats_check_filemap_switches(void)
-{
- if (_switches[AREAS_ARG] || _switches[AREA_SIZE_ARG]) {
- log_error("--filemap is incompatible with --areas and --area-size.");
- return 0;
- }
-
- if (_switches[START_ARG] || _switches[LENGTH_ARG]) {
- log_error("--filemap is incompatible with --start and --length.");
- return 0;
- }
-
- if (_switches[SEGMENTS_ARG]) {
- log_error("--filemap and --segments are incompatible.");
- return 0;
- }
-
- if (_switches[USER_DATA_ARG]) {
- log_error("--userdata is not yet supported with --filemap.");
- return 0;
- }
-
- if (_switches[UUID_ARG] || _switches[MAJOR_ARG]) {
- log_error("--uuid and --major are incompatible with --filemap.");
- return 0;
- }
-
- if (_switches[ALL_DEVICES_ARG]) {
- log_error("--alldevices is incompatible with --filemap.");
- return 0;
- }
-
- return 1;
-}
-
-static dm_filemapd_mode_t _stats_get_filemapd_mode(void)
-{
- if (_switches[NOMONITOR_ARG] || _switches[NOGROUP_ARG])
- return DM_FILEMAPD_FOLLOW_NONE;
- if (!_switches[FOLLOW_ARG])
- return DM_FILEMAPD_FOLLOW_INODE;
- return dm_filemapd_mode_from_string(_string_args[FOLLOW_ARG]);
-}
-
-static int _stats_create_file(CMD_ARGS)
-{
- const char *alias, *program_id = DM_STATS_PROGRAM_ID;
- const char *bounds_str = _string_args[BOUNDS_ARG];
- int foreground = _switches[FOREGROUND_ARG];
- int verbose = _switches[VERBOSE_ARG];
- uint64_t *regions, *region, count = 0;
- struct dm_histogram *bounds = NULL;
- char *path, *abspath = NULL;
- struct dm_stats *dms = NULL;
- int group, fd = -1, precise;
- dm_filemapd_mode_t mode;
-
- if (names) {
- log_error("Device names are not compatibile with --filemap.");
- return 0;
- }
-
- if (!_stats_check_filemap_switches())
- return 0;
-
- /* _stats_create_file does not use _process_all() */
- if (!argc) {
- log_error("--filemap requires a file path argument");
- return 0;
- }
-
- path = argv[0];
-
- if (_switches[PRECISE_ARG]) {
- if (!dm_stats_driver_supports_precise()) {
- log_error("Using --precise requires driver version "
- "4.32.0 or later.");
- return 0;
- }
- }
-
- if (_switches[BOUNDS_ARG]) {
- if (!dm_stats_driver_supports_histogram()) {
- log_error("Using --bounds requires driver version "
- "4.32.0 or later.");
- return 0;
- }
- }
-
- if (!(abspath = _get_abspath(path))) {
- log_error("Could not canonicalize file name: %s", path);
- return 0;
- }
-
- if (bounds_str
- && !(bounds = dm_histogram_bounds_from_string(bounds_str))) {
- free(abspath);
- return_0;
- }
-
- if (_switches[PROGRAM_ID_ARG])
- program_id = _string_args[PROGRAM_ID_ARG];
- if (!strlen(program_id) && !_switches[FORCE_ARG])
- program_id = DM_STATS_PROGRAM_ID;
-
- precise = _int_args[PRECISE_ARG];
- group = !_switches[NOGROUP_ARG];
-
- mode = _stats_get_filemapd_mode();
- if (!_switches[NOMONITOR_ARG] && (mode == DM_FILEMAPD_FOLLOW_NONE))
- goto bad;
-
- if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
- goto_bad;
-
- fd = open(abspath, O_RDONLY);
-
- if (fd < 0) {
- log_error("Could not open %s for reading", abspath);
- goto bad;
- }
-
- if (!dm_stats_bind_from_fd(dms, fd))
- goto_bad;
-
- if (!strlen(program_id))
- /* force creation of a region with no id */
- dm_stats_set_program_id(dms, 1, NULL);
-
- if (group && !_switches[ALIAS_ARG])
- alias = dm_basename(abspath);
- else if (group)
- alias = _string_args[ALIAS_ARG];
- else if (!_switches[ALIAS_ARG])
- alias = NULL;
- else {
- log_error("Cannot set alias with --nogroup.");
- goto bad;
- }
-
- regions = dm_stats_create_regions_from_fd(dms, fd, group, precise,
- bounds, alias);
-
- if (!regions) {
- log_error("Could not create regions from file %s.", abspath);
- goto bad;
- }
-
- if (!_switches[NOMONITOR_ARG] && group) {
- if (!dm_stats_start_filemapd(fd, regions[0], abspath, mode,
- foreground, verbose))
- log_warn("Failed to start filemap monitoring daemon.");
- }
-
- if (close(fd))
- log_sys_debug("close", abspath);
-
- fd = -1;
-
- for (region = regions; *region != DM_STATS_REGIONS_ALL; region++)
- count++;
-
- if (group) {
- printf("%s: Created new group with "FMTu64" region(s) as "
- "group ID "FMTu64".\n", path, count, regions[0]);
- } else {
- region = regions;
- do
- printf("%s: Created new region with 1 area as "
- "region ID "FMTu64".\n", path, *region);
- while (*(++region) != DM_STATS_REGIONS_ALL);
- }
-
- free(regions);
- free(abspath);
- free(bounds);
- dm_stats_destroy(dms);
- return 1;
-
-bad:
- free(abspath);
- free(bounds);
-
- if ((fd > -1) && close(fd))
- log_sys_debug("close", path);
-
- if (dms)
- dm_stats_destroy(dms);
-
- return 0;
-}
-
-static int _stats_create(CMD_ARGS)
-{
- struct dm_stats *dms;
- const char *name, *user_data = "", *program_id = DM_STATS_PROGRAM_ID;
- uint64_t start = 0, len = 0, areas = 0, area_size = 0;
- int64_t step = 0;
-
- /* create does not use a report */
- if (_report) {
- dm_report_free(_report);
- _report = NULL;
- }
-
- if (_switches[ALL_REGIONS_ARG]) {
- log_error("Cannot use --allregions with create.");
- return 0;
- }
-
- if (_switches[ALL_PROGRAMS_ARG]) {
- log_error("Cannot use --allprograms with create.");
- return 0;
- }
-
- if (_switches[AREAS_ARG] && _switches[AREA_SIZE_ARG]) {
- log_error("Please specify one of --areas and --areasize.");
- return 0;
- }
-
- if (_switches[PROGRAM_ID_ARG]
- && !strlen(_string_args[PROGRAM_ID_ARG]) && !_switches[FORCE_ARG]) {
- log_error("Creating a region with no program "
- "id requires --force.");
- return 0;
- }
-
- if (_switches[FILEMAP_ARG])
- return _stats_create_file(cmd, subcommand, argc, argv,
- names, multiple_devices);
-
- if (names)
- name = names->name;
- else {
- if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
- if (!_switches[ALL_DEVICES_ARG]) {
- log_error("Please specify device(s) or use "
- "--alldevices.");
- return 0;
- }
- return _process_all(cmd, subcommand, argc, argv, 0, _stats_create);
- }
- name = argv[0];
- }
-
- if (_switches[AREAS_ARG])
- areas = (uint64_t) _int_args[AREAS_ARG];
-
- if (_switches[AREA_SIZE_ARG])
- if (!_size_from_string(_string_args[AREA_SIZE_ARG],
- &area_size, "areasize"))
- return_0;
-
- areas = (areas) ? areas : 1;
- /* bytes to sectors or -(areas): promote to signed before conversion */
- step = (area_size) ? ((int64_t) area_size / 512) : -((int64_t) areas);
-
- if (_switches[START_ARG]) {
- if (!_size_from_string(_string_args[START_ARG],
- &start, "start"))
- return_0;
- }
-
- /* bytes to sectors */
- start /= 512;
-
- if (_switches[LENGTH_ARG]) {
- if (!_size_from_string(_string_args[LENGTH_ARG],
- &len, "length"))
- return_0;
- }
-
- /* bytes to sectors */
- len /= 512;
-
- if (_switches[PROGRAM_ID_ARG])
- program_id = _string_args[PROGRAM_ID_ARG];
- if (!strlen(program_id) && !_switches[FORCE_ARG])
- program_id = DM_STATS_PROGRAM_ID;
-
- if (_switches[USER_DATA_ARG])
- user_data = _string_args[USER_DATA_ARG];
-
- if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
- return_0;
-
- if (!_bind_stats_device(dms, name))
- goto_bad;
-
- if (_switches[PRECISE_ARG]) {
- if (!dm_stats_driver_supports_precise()) {
- log_error("Using --precise requires driver version "
- "4.32.0 or later.");
- goto bad;
- }
- }
-
- if (_switches[BOUNDS_ARG]) {
- if (!dm_stats_driver_supports_histogram()) {
- log_error("Using --bounds requires driver version "
- "4.32.0 or later.");
- goto bad;
- }
- }
-
- if (!strlen(program_id))
- /* force creation of a region with no id */
- dm_stats_set_program_id(dms, 1, NULL);
-
- return _do_stats_create_regions(dms, name, start, len, step,
- _switches[SEGMENTS_ARG],
- program_id, user_data);
-
-bad:
- dm_stats_destroy(dms);
- return 0;
-}
-
-static int _stats_delete(CMD_ARGS)
-{
- struct dm_stats *dms;
- uint64_t region_id, group_id;
- char *name = NULL;
- const char *program_id = DM_STATS_PROGRAM_ID;
- int allregions = _switches[ALL_REGIONS_ARG];
- int r = 0;
-
- /* delete does not use a report */
- if (_report) {
- dm_report_free(_report);
- _report = NULL;
- }
-
- if (_switches[REGION_ID_ARG] && _switches[GROUP_ID_ARG]) {
- log_error("Please use one of --regionid and --groupid.");
- return 0;
- }
-
- if (!_switches[REGION_ID_ARG] && !allregions && !_switches[GROUP_ID_ARG]) {
- log_error("Please specify a --regionid or --groupid, or use --allregions.");
- return 0;
- }
-
- if (names)
- name = names->name;
- else {
- if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
- if (!_switches[ALL_DEVICES_ARG]) {
- log_error("Please specify device(s) or use "
- "--alldevices.");
- return 0;
- }
- return _process_all(cmd, subcommand, argc, argv, 0, _stats_delete);
- }
- name = argv[0];
- }
-
- if (_switches[PROGRAM_ID_ARG])
- program_id = _string_args[PROGRAM_ID_ARG];
- if (_switches[ALL_PROGRAMS_ARG])
- program_id = DM_STATS_ALL_PROGRAMS;
-
- region_id = (uint64_t) _int_args[REGION_ID_ARG];
- group_id = (uint64_t) _int_args[GROUP_ID_ARG];
-
- if (!(dms = dm_stats_create(program_id)))
- return_0;
-
- if (!_bind_stats_device(dms, name))
- goto_out;
-
- /* allregions and group delete require a listed handle */
- if ((allregions || _switches[GROUP_ID_ARG])
- && !dm_stats_list(dms, program_id))
- goto_out;
-
- if (allregions && !dm_stats_get_nr_regions(dms)) {
- /* no regions present */
- r = 1;
- goto out;
- }
-
- if (_switches[GROUP_ID_ARG]) {
- if (!dm_stats_delete_group(dms, group_id, 1)) {
- log_error("Could not delete statistics group.");
- goto out;
- }
- printf("Deleted statistics group " FMTu64 ".\n", group_id);
- } else if (_switches[ALL_REGIONS_ARG]) {
- dm_stats_foreach_region(dms) {
- region_id = dm_stats_get_current_region(dms);
- if (!dm_stats_delete_region(dms, region_id)) {
- log_error("Could not delete statistics region.");
- goto out;
- }
- log_info("Deleted statistics region %" PRIu64, region_id);
- }
- } else {
- if (!dm_stats_delete_region(dms, region_id)) {
- log_error("Could not delete statistics region");
- goto out;
- }
- log_info("Deleted statistics region " FMTu64 ".", region_id);
- }
-
- r = 1;
-
-out:
- dm_stats_destroy(dms);
- return r;
-}
-
-static int _stats_print_one_region(struct dm_stats *dms, int clear,
- uint64_t region_id)
-{
- char *stbuff = NULL;
-
- /*FIXME: line control for large regions */
- if (!(stbuff = dm_stats_print_region(dms, region_id, 0, 0, clear))) {
- log_error("Could not print statistics region.");
- return 0;
- }
-
- printf("%s", stbuff);
- dm_stats_buffer_destroy(dms, stbuff);
-
- return 1;
-}
-
-static int _stats_print(CMD_ARGS)
-{
- struct dm_stats *dms;
- char *name, *stbuff = NULL;
- uint64_t region_id;
- unsigned clear = (unsigned) _switches[CLEAR_ARG];
- int allregions = _switches[ALL_REGIONS_ARG];
- int r = 0;
-
- /* print does not use a report */
- if (_report) {
- dm_report_free(_report);
- _report = NULL;
- }
-
- if (!_switches[REGION_ID_ARG] && !allregions) {
- log_error("Please specify a --regionid or use --allregions.");
- return 0;
- }
-
- if (names)
- name = names->name;
- else {
- if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
- return _process_all(cmd, subcommand, argc, argv, 0, _stats_print);
- name = argv[0];
- }
-
- if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
- return_0;
-
- if (!_bind_stats_device(dms, name))
- goto_out;
-
- if (!dm_stats_list(dms, NULL))
- goto_out;
-
- if (allregions && !dm_stats_get_nr_regions(dms)) {
- r = 1;
- goto out;
- }
-
- if (!allregions) {
- region_id = (uint64_t) _int_args[REGION_ID_ARG];
- if (!_stats_print_one_region(dms, clear, region_id))
- goto_out;
- r = 1;
- goto out;
- }
-
- dm_stats_foreach_region(dms) {
- region_id = dm_stats_get_current_region(dms);
- if (!_stats_print_one_region(dms, clear, region_id))
- goto_out;
-
- /*FIXME: line control for large regions */
- if (!(stbuff = dm_stats_print_region(dms, region_id, 0, 0, clear))) {
- log_error("Could not print statistics region.");
- goto out;
- }
-
- printf("%s", stbuff);
- dm_stats_buffer_destroy(dms, stbuff);
- }
-
- r = 1;
-
-out:
- dm_stats_destroy(dms);
- return r;
-}
-
-static int _stats_report(CMD_ARGS)
-{
- int r = 0, objtype_args;
-
- struct dm_task *dmt;
- char *name = NULL;
-
- objtype_args = (_switches[AREA_ARG]
- || _switches[REGION_ARG]
- || _switches[GROUP_ARG]);
-
- if (_switches[PROGRAM_ID_ARG])
- _program_id = _string_args[PROGRAM_ID_ARG];
-
- if (_switches[ALL_PROGRAMS_ARG])
- _program_id = "";
-
- if (_switches[VERBOSE_ARG] && !strcmp(subcommand, "list"))
- _statstype |= (DM_STATS_WALK_ALL
- | DM_STATS_WALK_SKIP_SINGLE_AREA);
-
- /* suppress duplicates unless the user has requested all regions */
- if (!strcmp(subcommand, "report") && !objtype_args)
- /* suppress duplicate rows of output */
- _statstype |= (DM_STATS_WALK_ALL
- | DM_STATS_WALK_SKIP_SINGLE_AREA);
-
- if (names)
- name = names->name;
- else {
- if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
- return _process_all(cmd, subcommand, argc, argv, 0, _info);
- name = argv[0];
- }
-
- if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
- return_0;
-
- if (!_set_task_device(dmt, name, 0))
- goto_out;
-
- if (_switches[CHECKS_ARG] && !dm_task_enable_checks(dmt))
- goto_out;
-
- if (!_task_run(dmt))
- goto_out;
-
- r = _display_info(dmt);
-
-out:
- dm_task_destroy(dmt);
-
- if (!r && _report) {
- dm_report_free(_report);
- _report = NULL;
- }
-
- return r;
-}
-
-static int _stats_group(CMD_ARGS)
-{
- char *name, *alias = NULL, *regions = NULL;
- struct dm_stats *dms;
- uint64_t group_id;
- int r = 0;
-
- /* group does not use a report */
- if (_report) {
- dm_report_free(_report);
- _report = NULL;
- }
-
- if (!_switches[REGIONS_ARG]) {
- log_error("Group requires --regions.");
- return 0;
- }
-
- regions = _string_args[REGIONS_ARG];
-
- if (names)
- name = names->name;
- else {
- if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
- if (!_switches[ALL_DEVICES_ARG]) {
- log_error("Please specify device(s) or use "
- "--alldevices.");
- return 0;
- }
- return _process_all(cmd, subcommand, argc, argv, 0, _stats_group);
- }
- name = argv[0];
- }
-
- if (_switches[ALIAS_ARG])
- alias = _string_args[ALIAS_ARG];
-
- if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
- return_0;
-
- if (!_bind_stats_device(dms, name))
- goto_out;
-
- if (!dm_stats_list(dms, NULL))
- goto_out;
-
- if(!dm_stats_create_group(dms, regions, alias, &group_id)) {
- log_error("Could not create group on %s: %s", name, regions);
- goto out;
- }
-
- printf("Grouped regions %s as group ID " FMTu64 " on %s\n",
- regions, group_id, name);
-
- r = 1;
-
-out:
- dm_stats_destroy(dms);
- return r;
-}
-
-static int _stats_ungroup(CMD_ARGS)
-{
- struct dm_stats *dms;
- uint64_t group_id;
- char *name;
- int r = 0;
-
- /* ungroup does not use a report */
- if (_report) {
- dm_report_free(_report);
- _report = NULL;
- }
-
- if (!_switches[GROUP_ID_ARG]) {
- log_error("Please specify group id.");
- return 0;
- }
-
- group_id = (uint64_t) _int_args[GROUP_ID_ARG];
-
- if (names)
- name = names->name;
- else {
- if (!argc && !_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
- if (!_switches[ALL_DEVICES_ARG]) {
- log_error("Please specify device(s) or use "
- "--alldevices.");
- return 0;
- }
- return _process_all(cmd, subcommand, argc, argv, 0, _stats_ungroup);
- }
- name = argv[0];
- }
-
- if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
- return_0;
-
- if (!_bind_stats_device(dms, name))
- goto_out;
-
- if (!dm_stats_list(dms, NULL))
- goto_out;
-
- if (!(r = dm_stats_delete_group(dms, group_id, 0)))
- log_error("Could not delete group " FMTu64 " on %s.",
- group_id, name);
-
- printf("Removed group ID "FMTu64" on %s\n", group_id, name);
-
-out:
- dm_stats_destroy(dms);
- return r;
-}
-
-static int _stats_update_file(CMD_ARGS)
-{
- uint64_t group_id, *region, *regions = NULL, count = 0;
- const char *program_id = DM_STATS_PROGRAM_ID;
- int foreground = _switches[FOREGROUND_ARG];
- int verbose = _switches[VERBOSE_ARG];
- char *path, *abspath = NULL;
- struct dm_stats *dms = NULL;
- dm_filemapd_mode_t mode;
- int fd = -1;
-
-
- if (names) {
- log_error("Device names are not compatibile with update_filemap.");
- return 0;
- }
-
- if (!_stats_check_filemap_switches())
- return 0;
-
- /* _stats_update_file does not use _process_all() */
- if (!argc) {
- log_error("update_filemap requires a file path argument");
- return 0;
- }
-
- if (!_switches[GROUP_ID_ARG]) {
- log_error("--groupid is required to update a filemap group.");
- return 0;
- }
-
- path = argv[0];
-
- if (!(abspath = _get_abspath(path))) {
- log_error("Could not canonicalize file name: %s", path);
- return 0;
- }
-
- group_id = (uint64_t) _int_args[GROUP_ID_ARG];
-
- mode = _stats_get_filemapd_mode();
- if (!_switches[NOMONITOR_ARG] && (mode == DM_FILEMAPD_FOLLOW_NONE))
- goto bad;
-
- if (_switches[PROGRAM_ID_ARG])
- program_id = _string_args[PROGRAM_ID_ARG];
- if (!strlen(program_id) && !_switches[FORCE_ARG])
- program_id = DM_STATS_PROGRAM_ID;
-
- if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
- goto_bad;
-
- fd = open(abspath, O_RDONLY);
-
- if (fd < 0) {
- log_error("Could not open %s for reading", abspath);
- goto bad;
- }
-
- if (!dm_stats_bind_from_fd(dms, fd))
- goto_bad;
-
- if (!strlen(program_id))
- /* force creation of a region with no id */
- dm_stats_set_program_id(dms, 1, NULL);
-
- /*
- * Start dmfilemapd - it will test the file descriptor to determine
- * whether it is necessary to call dm_stats_update_regions_from_fd().
- *
- * If starting the daemon fails, fall back to a direct update.
- */
- if (!_switches[NOMONITOR_ARG]) {
- if (dm_stats_start_filemapd(fd, group_id, abspath, mode,
- foreground, verbose))
- goto out;
-
- log_warn("Failed to start filemap monitoring daemon.");
-
- /* fall back to one-shot update */
- }
-
- /*
- * --nomonitor and fall back case - perform a one-shot update directly
- * from dmsetup.
- */
- regions = dm_stats_update_regions_from_fd(dms, fd, group_id);
-
- if (!regions) {
- log_error("Could not update regions from file %s", abspath);
- goto bad;
- }
-
- for (region = regions; *region != DM_STATS_REGIONS_ALL; region++)
- count++;
-
- if (group_id != regions[0]) {
- printf("Group ID changed from " FMTu64 " to " FMTu64,
- group_id, regions[0]);
- group_id = regions[0];
- }
-
- printf("%s: Updated group ID " FMTu64 " with "FMTu64" region(s).\n",
- path, group_id, count);
-
-out:
- if (close(fd))
- log_sys_debug("close", abspath);
-
- free(regions);
- free(abspath);
- dm_stats_destroy(dms);
- return 1;
-
-bad:
- free(abspath);
-
- if ((fd > -1) && close(fd))
- log_sys_debug("close", path);
-
- dm_stats_destroy(dms);
-
- return 0;
-}
-
-/*
- * Command dispatch tables and usage.
- */
-static int _stats_help(CMD_ARGS);
-
-/*
- * dmsetup stats <cmd> [options] [device_name]
- * dmstats <cmd> [options] [device_name]
- *
- * clear [--allregions|--regionid id] [--alldevices|<device>...]
- * create [--start <start> [--length <len>]
- * [--areas <nr_areas>] [--areasize <size>]
- * [--programid <id>] [--userdata <data> ]
- * [--bounds histogram_boundaries] [--precise]
- * [--alldevices|<device>...]
- * create --filemap [--nogroup] [--nomonitor] [--follow=mode]
- * [--programid <id>] [--userdata <data> ]
- * [--bounds histogram_boundaries] [--precise] [<file_path>]
- * delete [--allprograms|--programid id]
- * [--allregions|--regionid id]
- * [--alldevices|<device>...]
- * group [--alias NAME] --regions <regions>
- * [--allprograms|--programid id] [--alldevices|<device>...]
- * list [--allprograms|--programid id] [--allregions|--regionid id]
- * print [--clear] [--allprograms|--programid id]
- * [--allregions|--regionid id]
- * [--alldevices|<device>...]
- * report [--interval <seconds>] [--count <cnt>]
- * [--units <u>] [--programid <id>] [--regionid <id>]
- * [-o <fields>] [-O|--sort <sort_fields>]
- * [-S|--select <selection>] [--nameprefixes]
- * [--noheadings] [--separator <separator>]
- * [--allprograms|--programid id] [<device>...]
- * ungroup --groupid <id> [--allprograms|--programid id]
- * [--alldevices|<device>...]
- */
-
-#define INDENT "\n\t "
-/* groups of commonly used options */
-#define AREA_OPTS "[--areas <nr_areas>] [--areasize <size>] "
-#define REGION_OPTS "[--start <start> [--length <len>]" INDENT AREA_OPTS
-#define ID_OPTS "[--programid <id>] [--userdata <data> ] "
-#define SELECT_OPTS "[--programid <id>] [--regionid <id>] "
-#define HIST_OPTS "[--bounds histogram_boundaries] "
-#define PRECISE_OPTS "[--precise] "
-#define SEGMENTS_OPT "[--segments] "
-#define EXTRA_OPTS HIST_OPTS PRECISE_OPTS
-#define FILE_MONITOR_OPTS "[--nomonitor] [--follow mode]"
-#define GROUP_ID_OPT "--groupid <id> "
-#define ALL_PROGS_OPT "[--allprograms|--programid id] "
-#define ALL_REGIONS_OPT "[--allregions|--regionid id] "
-#define ALL_DEVICES_OPT "[--alldevices|<device>...] "
-#define ALL_PROGS_REGIONS_DEVICES ALL_PROGS_OPT INDENT ALL_REGIONS_OPT INDENT ALL_DEVICES_OPT
-#define FIELD_OPTS "[-o <fields>] [-O|--sort <sort_fields>]"
-#define DM_REPORT_OPTS FIELD_OPTS INDENT "[-S|--select <selection>] [--nameprefixes]" INDENT \
-"[--noheadings] [--separator <separator>]"
-
-/* command options */
-#define CREATE_OPTS REGION_OPTS INDENT ID_OPTS INDENT EXTRA_OPTS INDENT SEGMENTS_OPT
-#define FILEMAP_OPTS "--filemap [--nogroup] " FILE_MONITOR_OPTS INDENT ID_OPTS INDENT EXTRA_OPTS
-#define PRINT_OPTS "[--clear] " ALL_PROGS_REGIONS_DEVICES
-#define REPORT_OPTS "[--interval <seconds>] [--count <cnt>]" INDENT \
-"[--units <u>] " SELECT_OPTS INDENT DM_REPORT_OPTS INDENT ALL_PROGS_OPT
-#define GROUP_OPTS "[--alias NAME] --regions <regions>" INDENT ALL_PROGS_OPT ALL_DEVICES_OPT
-#define UNGROUP_OPTS GROUP_ID_OPT ALL_PROGS_OPT INDENT ALL_DEVICES_OPT
-#define UPDATE_OPTS GROUP_ID_OPT INDENT FILE_MONITOR_OPTS " <file_path>"
-
-/*
- * The 'create' command has two entries in the table, to allow for the
- * the fact that 'create' and 'create --filemap' have largely disjoint
- * sets of options.
- */
-static struct command _stats_subcommands[] = {
- {"help", "", 0, 0, 0, 0, _stats_help},
- {"clear", ALL_REGIONS_OPT ALL_DEVICES_OPT, 0, -1, 1, 0, _stats_clear},
- {"create", CREATE_OPTS ALL_DEVICES_OPT, 0, -1, 1, 0, _stats_create},
- {"create", FILEMAP_OPTS "<file_path>", 0, -1, 1, 0, _stats_create},
- {"delete", ALL_PROGS_REGIONS_DEVICES, 1, -1, 1, 0, _stats_delete},
- {"group", GROUP_OPTS, 1, -1, 1, 0, _stats_group},
- {"list", ALL_PROGS_OPT ALL_REGIONS_OPT, 0, -1, 1, 0, _stats_report},
- {"print", PRINT_OPTS, 0, -1, 1, 0, _stats_print},
- {"report", REPORT_OPTS "[<device>...]", 0, -1, 1, 0, _stats_report},
- {"ungroup", UNGROUP_OPTS, 1, -1, 1, 0, _stats_ungroup},
- {"update_filemap", UPDATE_OPTS, 1, 1, 0, 0, _stats_update_file},
- {"version", "", 0, -1, 1, 0, _version},
- {NULL, NULL, 0, 0, 0, 0, NULL}
-};
-
-#undef AREA_OPTS
-#undef REGION_OPTS
-#undef ID_OPTS
-#undef SELECT_OPTS
-#undef HIST_OPTS
-#undef PRECISE_OPTS
-#undef EXTRA_OPTS
-#undef ALL_PROGS_OPT
-#undef ALL_REGIONS_OPT
-#undef ALL_DEVICES_OPT
-#undef ALL_PROGS_REGIONS_DEVICES
-#undef FIELD_OPTS
-#undef DM_REPORT_OPTS
-#undef CREATE_OPTS
-#undef FILEMAP_OPTS
-#undef PRINT_OPTS
-#undef REPORT_OPTS
-#undef GROUP_OPTS
-#undef UNGROUP_OPTS
-
-static int _dmsetup_help(CMD_ARGS);
-
-static struct command _dmsetup_commands[] = {
- {"help", "[-c|-C|--columns]", 0, 0, 0, 0, _dmsetup_help},
- {"create", "<dev_name>\n"
- "\t [-j|--major <major> -m|--minor <minor>]\n"
- "\t [-U|--uid <uid>] [-G|--gid <gid>] [-M|--mode <octal_mode>]\n"
- "\t [-u|--uuid <uuid>] [--addnodeonresume|--addnodeoncreate]\n"
- "\t [--readahead {[+]<sectors>|auto|none}]\n"
- "\t [-n|--notable|--table {<table>|<table_file>}]\n"
- "\tcreate --concise [<concise_device_spec_list>]", 0, 2, 0, 0, _create},
- {"remove", "[--deferred] [-f|--force] [--retry] <device>...", 0, -1, 2, 0, _remove},
- {"remove_all", "[-f|--force]", 0, 0, 0, 0, _remove_all},
- {"suspend", "[--noflush] [--nolockfs] <device>...", 0, -1, 2, 0, _suspend},
- {"resume", "[--noflush] [--nolockfs] <device>...\n"
- "\t [--addnodeonresume|--addnodeoncreate]\n"
- "\t [--readahead {[+]<sectors>|auto|none}]", 0, -1, 2, 0, _resume},
- {"load", "<device> [<table>|<table_file>]", 0, 2, 0, 0, _load},
- {"clear", "<device>", 0, -1, 2, 0, _clear},
- {"reload", "<device> [<table>|<table_file>]", 0, 2, 0, 0, _load},
- {"wipe_table", "[-f|--force] [--noflush] [--nolockfs] <device>...", 0, -1, 2, 0, _error_device},
- {"rename", "<device> [--setuuid] <new_name_or_uuid>", 1, 2, 0, 0, _rename},
- {"message", "<device> <sector> <message>", 2, -1, 0, 0, _message},
- {"ls", "[--target <target_type>] [--exec <command>] [-o <options>] [--tree]", 0, 0, 0, 0, _ls},
- {"info", "[<device>...]", 0, -1, 1, 0, _info},
- {"deps", "[-o <options>] [<device>...]", 0, -1, 2, 0, _deps},
- {"stats", "<command> [<options>] [<device>...]", 1, -1, 1, 1, _stats},
- {"status", "[<device>...] [--noflush] [--target <target_type>]", 0, -1, 2, 0, _status},
- {"table", "[<device>...] [--concise] [--target <target_type>] [--showkeys]", 0, -1, 2, 0, _status},
- {"wait", "<device> [<event_nr>] [--noflush]", 0, 2, 0, 0, _wait},
- {"mknodes", "[<device>...]", 0, -1, 1, 0, _mknodes},
- {"mangle", "[<device>...]", 0, -1, 1, 0, _mangle},
- {"udevcreatecookie", "", 0, 0, 0, 0, _udevcreatecookie},
- {"udevreleasecookie", "[<cookie>]", 0, 1, 0, 0, _udevreleasecookie},
- {"udevflags", "<cookie>", 1, 1, 0, 0, _udevflags},
- {"udevcomplete", "<cookie>", 1, 1, 0, 0, _udevcomplete},
- {"udevcomplete_all", "[<age_in_minutes>]", 0, 1, 0, 0, _udevcomplete_all},
- {"udevcookies", "", 0, 0, 0, 0, _udevcookies},
- {"targets", "", 0, 0, 0, 0, _targets},
- {"version", "", 0, 0, 0, 0, _version},
- {"setgeometry", "<device> <cyl> <head> <sect> <start>", 5, 5, 0, 0, _setgeometry},
- {"splitname", "<device> [<subsystem>]", 1, 2, 0, 0, _splitname},
- {NULL, NULL, 0, 0, 0, 0, NULL}
-};
-
-/*
- * Usage and help text.
- */
-
-static void _devmap_name_usage(FILE *out)
-{
- fprintf(out, "Usage: " DEVMAP_NAME_CMD_NAME " <major> <minor>\n\n");
-}
-
-static void _stats_usage(FILE *out)
-{
- int i;
-
- fprintf(out, "Usage:\n\n"
- "%s\n"
- " [-h|--help]\n"
- " [-v|--verbose [-v|--verbose ...]]\n"
- " [--areas <nr_areas>] [--areasize <size>]\n"
- " [--userdata <data>] [--clear]\n"
- " [--count <count>] [--interval <seconds>]\n"
- " [-o <fields>] [-O|--sort <sort_fields>]\n"
- " [--programid <id>]\n"
- " [--start <start>] [--length <length>]\n"
- " [--segments] [--units <units>]\n\n",
- _base_commands[_base_command].name);
-
- for (i = 0; _stats_subcommands[i].name; i++)
- fprintf(out, "\t%s %s\n", _stats_subcommands[i].name, _stats_subcommands[i].help);
-
- fprintf(out, "\n<device> may be device name or (if only one) -u <uuid> or -j <major> -m <minor>\n"
- "<fields> are comma-separated. Use 'help -c' for list.\n\n");
-}
-
-static void _dmsetup_usage(FILE *out)
-{
- int i;
-
- fprintf(out, "Usage:\n\n"
- "%s\n"
- " [--version] [-h|--help [-c|-C|--columns]]\n"
- " [-v|--verbose [-v|--verbose ...]] [-f|--force]\n"
- " [--checks] [--manglename {none|hex|auto}]\n"
- " [-r|--readonly] [--noopencount] [--noflush] [--nolockfs] [--inactive]\n"
- " [--udevcookie <cookie>] [--noudevrules] [--noudevsync] [--verifyudev]\n"
- " [-y|--yes] [--readahead {[+]<sectors>|auto|none}] [--retry]\n"
- " [-c|-C|--columns] [-o <fields>] [-O|--sort <sort_fields>]\n"
- " [-S|--select <selection>] [--nameprefixes] [--noheadings]\n"
- " [--separator <separator>]\n\n",
- _base_commands[_base_command].name);
-
- for (i = 0; _dmsetup_commands[i].name; i++)
- fprintf(out, "\t%s %s\n", _dmsetup_commands[i].name, _dmsetup_commands[i].help);
-
- fprintf(out, "\n<device> may be device name or (if only one) -u <uuid> or "
- "-j <major> -m <minor>\n"
- "<mangling_mode> is one of 'none', 'auto' and 'hex'.\n"
- "<fields> are comma-separated. Use 'help -c' for list.\n"
- "<concise_device_specification> has single-device entries separated by semi-colons:\n"
- " <name>,<uuid>,<minor>,<flags>,<table>\n"
- " where <flags> is 'ro' or 'rw' (the default) and any of <uuid>, <minor>\n"
- " and <flags> may be empty. Separate extra table lines with commas.\n"
- " E.g.: dev1,,,,0 100 linear 253:1 0,100 100 error;dev2,,,ro,0 1 error\n"
- "Table_file contents may be supplied on stdin.\n"
- "Options are: devno, devname, blkdevname.\n"
- "Tree specific options are: ascii, utf, vt100; compact, inverted, notrunc;\n"
- " blkdevname, [no]device, active, open, rw and uuid.\n\n");
-}
-
-static void _losetup_usage(FILE *out)
-{
- fprintf(out,
- "Usage:\n\n"
- "%s [-d|-a] [-e encryption] "
- "[-o offset] [-f|loop_device] [file]\n\n",
- _base_commands[_base_command].name);
-}
-
-static void _usage(FILE *out)
-{
- switch (_base_commands[_base_command].type) {
- case DMSETUP_TYPE:
- return _dmsetup_usage(out);
- case LOSETUP_TYPE:
- return _losetup_usage(out);
- case STATS_TYPE:
- return _stats_usage(out);
- case DEVMAP_NAME_TYPE:
- return _devmap_name_usage(out);
- }
-}
-
-static int _stats_help(CMD_ARGS)
-{
- _usage(stderr);
-
- if (_switches[COLS_ARG] || (argc && !strcmp(argv[0], "report"))) {
- _switches[OPTIONS_ARG] = 1;
- _string_args[OPTIONS_ARG] = (char *) "help";
- _switches[SORT_ARG] = 0;
-
- if (_report) {
- dm_report_free(_report);
- _report = NULL;
- }
-
- (void) _report_init(cmd, "help");
- if (_report) {
- dm_report_free(_report);
- _report = NULL;
- }
- }
-
- return 1;
-}
-
-static int _dmsetup_help(CMD_ARGS)
-{
- _usage(stderr);
-
- if (_switches[COLS_ARG]) {
- _switches[OPTIONS_ARG] = 1;
- _string_args[OPTIONS_ARG] = (char *) "help";
- _switches[SORT_ARG] = 0;
-
- if (_report) {
- dm_report_free(_report);
- _report = NULL;
- }
- (void) _report_init(cmd, "");
- if (_report) {
- dm_report_free(_report);
- _report = NULL;
- }
- }
-
- return 1;
-}
-
-static const struct command *_find_command(const struct command *commands,
- const char *name)
-{
- int i;
-
- for (i = 0; commands[i].name; i++)
- if (!strcmp(commands[i].name, name))
- return commands + i;
-
- return NULL;
-}
-
-static const struct command *_find_dmsetup_command(const char *name)
-{
- return _find_command(_dmsetup_commands, name);
-}
-
-static const struct command *_find_stats_subcommand(const char *name)
-{
- return _find_command(_stats_subcommands, name);
-}
-
-static int _stats(CMD_ARGS)
-{
- const struct command *stats_cmd;
-
- if (_switches[AREA_ARG] || _switches[REGION_ARG] || _switches[GROUP_ARG])
- _statstype = 0; /* switches will OR flags in */
- else
- _statstype = DM_STATS_WALK_REGION | DM_STATS_WALK_GROUP;
-
- if (_switches[AREA_ARG])
- _statstype |= DM_STATS_WALK_AREA;
- if (_switches[REGION_ARG])
- _statstype |= DM_STATS_WALK_REGION;
- if (_switches[GROUP_ARG])
- _statstype |= DM_STATS_WALK_GROUP;
-
- if (!(stats_cmd = _find_stats_subcommand(subcommand))) {
- log_error("Unknown stats command.");
- _stats_help(stats_cmd, NULL, argc, argv, NULL, multiple_devices);
- return 0;
- }
-
- if (_switches[ALL_PROGRAMS_ARG] && _switches[PROGRAM_ID_ARG]) {
- log_error("Please supply one of --allprograms and --programid");
- return 0;
- }
-
- if (_switches[ALL_REGIONS_ARG] && _switches[REGION_ID_ARG]) {
- log_error("Please supply one of --allregions and --regionid");
- return 0;
- }
-
- if (_switches[FOLLOW_ARG] && _switches[NOMONITOR_ARG]) {
- log_error("Use of --follow is incompatible with --nomonitor.");
- return 0;
- }
-
- /*
- * Pass the sub-command through to allow a single function to be
- * used to implement several distinct sub-commands (e.g. 'report'
- * and 'list' share a single implementation.
- */
- if (!stats_cmd->fn(stats_cmd, subcommand, argc, argv, NULL,
- multiple_devices))
- return_0;
-
- return 1;
-}
-
-static int _process_tree_options(const char *options)
-{
- const char *s, *end;
- struct winsize winsz;
- size_t len;
-
- /* Symbol set default */
- if (!strcmp(nl_langinfo(CODESET), "UTF-8"))
- _tsym = &_tsym_utf;
- else
- _tsym = &_tsym_ascii;
-
- /* Default */
- _tree_switches[TR_DEVICE] = 1;
- _tree_switches[TR_TRUNCATE] = 1;
-
- /* parse */
- for (s = options; s && *s; s++) {
- len = 0;
- for (end = s; *end && *end != ','; end++, len++)
- ;
- if (!strncmp(s, "device", len))
- _tree_switches[TR_DEVICE] = 1;
- else if (!strncmp(s, "blkdevname", len))
- _tree_switches[TR_BLKDEVNAME] = 1;
- else if (!strncmp(s, "nodevice", len))
- _tree_switches[TR_DEVICE] = 0;
- else if (!strncmp(s, "status", len))
- _tree_switches[TR_STATUS] = 1;
- else if (!strncmp(s, "table", len))
- _tree_switches[TR_TABLE] = 1;
- else if (!strncmp(s, "active", len))
- _tree_switches[TR_ACTIVE] = 1;
- else if (!strncmp(s, "open", len))
- _tree_switches[TR_OPENCOUNT] = 1;
- else if (!strncmp(s, "uuid", len))
- _tree_switches[TR_UUID] = 1;
- else if (!strncmp(s, "rw", len))
- _tree_switches[TR_RW] = 1;
- else if (!strncmp(s, "utf", len))
- _tsym = &_tsym_utf;
- else if (!strncmp(s, "vt100", len))
- _tsym = &_tsym_vt100;
- else if (!strncmp(s, "ascii", len))
- _tsym = &_tsym_ascii;
- else if (!strncmp(s, "inverted", len))
- _tree_switches[TR_BOTTOMUP] = 1;
- else if (!strncmp(s, "compact", len))
- _tree_switches[TR_COMPACT] = 1;
- else if (!strncmp(s, "notrunc", len))
- _tree_switches[TR_TRUNCATE] = 0;
- else {
- log_error("Tree options not recognised: %s.", s);
- return 0;
- }
- if (!*end)
- break;
- s = end;
- }
-
- /* Truncation doesn't work well with vt100 drawing char */
- if (_tsym != &_tsym_vt100)
- if (ioctl(1, (unsigned long) TIOCGWINSZ, &winsz) >= 0 && winsz.ws_col > 3)
- _termwidth = winsz.ws_col - 3;
-
- return 1;
-}
-
-static char *_parse_loop_device_name(const char *dev, const char *dev_dir)
-{
- char *buf;
- char *device = NULL;
-
- if (!(buf = malloc(PATH_MAX)))
- return_NULL;
-
- if (dev[0] == '/') {
- if (!(device = _get_abspath(dev)))
- goto_bad;
-
- if (strncmp(device, dev_dir, strlen(dev_dir)))
- goto_bad;
-
- /* If dev_dir does not end in a slash, ensure that the
- following byte in the device string is "/". */
- if (dev_dir[strlen(dev_dir) - 1] != '/' &&
- device[strlen(dev_dir)] != '/')
- goto_bad;
-
- if (!dm_strncpy(buf, strrchr(device, '/') + 1, PATH_MAX))
- goto_bad;
- free(device);
- } else {
- /* check for device number */
- if (strncmp(dev, "loop", sizeof("loop") - 1))
- goto_bad;
-
- if (!dm_strncpy(buf, dev, PATH_MAX))
- goto_bad;
- }
-
- return buf;
-bad:
- free(device);
- free(buf);
-
- return NULL;
-}
-
-/*
- * create a table for a mapped device using the loop target.
- */
-static int _loop_table(char *table, size_t tlen, char *file,
- char *dev __attribute__((unused)), off_t off)
-{
- struct stat fbuf;
- off_t size, sectors;
- int fd = -1;
-#ifdef HAVE_SYS_STATVFS_H
- struct statvfs fsbuf;
- off_t blksize;
-#endif
-
- if (!_switches[READ_ONLY])
- fd = open(file, O_RDWR);
-
- if (fd < 0) {
- _switches[READ_ONLY]++;
- fd = open(file, O_RDONLY);
- }
-
- if (fd < 0)
- goto_bad;
-
- if (fstat(fd, &fbuf))
- goto_bad;
-
- size = (fbuf.st_size - off);
- sectors = size >> SECTOR_SHIFT;
-
- if (_switches[VERBOSE_ARG])
- log_error(LOSETUP_CMD_NAME ": set loop size to %llukB (%llu sectors).",
- (long long unsigned) sectors >> 1,
- (long long unsigned) sectors);
-
-#ifdef HAVE_SYS_STATVFS_H
- if (fstatvfs(fd, &fsbuf))
- goto_bad;
-
- /* FIXME Fragment size currently unused */
- blksize = fsbuf.f_frsize;
-#endif
-
- if (close(fd))
- log_sys_debug("close", file);
-
- if (dm_snprintf(table, tlen, "%llu %llu loop %s %llu\n", 0ULL,
- (long long unsigned)sectors, file, (long long unsigned)off) < 0)
- return_0;
-
- if (_switches[VERBOSE_ARG] > 1)
- log_verbose("Table: %s", table);
-
- return 1;
-
-bad:
- if (fd > -1 && close(fd))
- log_sys_error("close", file);
-
- return 0;
-}
-
-static int _process_losetup_switches(const char *base, int *argcp, char ***argvp,
- const char *dev_dir)
-{
- int c;
- int encrypt_loop = 0, delete = 0, find = 0, show_all = 0;
- char *device_name = NULL;
- char *loop_file = NULL;
- off_t offset = 0;
-
-#ifdef HAVE_GETOPTLONG
- static struct option long_options[] = {
- {0, 0, 0, 0}
- };
-#endif
-
- optarg = 0;
- optind = OPTIND_INIT;
- while ((c = GETOPTLONG_FN(*argcp, *argvp, "ade:fo:v",
- long_options, NULL)) != -1 ) {
- if (c == ':' || c == '?')
- return_0;
- if (c == 'a')
- show_all++;
- if (c == 'd')
- delete++;
- if (c == 'e')
- encrypt_loop++;
- if (c == 'f')
- find++;
- if (c == 'o')
- offset = atoi(optarg);
- if (c == 'v')
- _switches[VERBOSE_ARG]++;
- }
-
- *argvp += optind ;
- *argcp -= optind ;
-
- if (encrypt_loop){
- log_error("%s: Sorry, cryptoloop is not yet implemented "
- "in this version.", base);
- return 0;
- }
-
- if (show_all) {
- log_error("%s: Sorry, show all is not yet implemented "
- "in this version.", base);
- return 0;
- }
-
- if (find) {
- log_error("%s: Sorry, find is not yet implemented "
- "in this version.", base);
- if (!*argcp)
- return 0;
- }
-
- if (!*argcp) {
- log_error("%s: Please specify loop_device.", base);
- _usage(stderr);
- return 0;
- }
-
- if (!(device_name = _parse_loop_device_name((*argvp)[0], dev_dir))) {
- log_error("%s: Could not parse loop_device %s", base, (*argvp)[0]);
- _usage(stderr);
- return 0;
- }
-
- if (delete) {
- *argcp = 1;
-
- (*argvp)[0] = device_name;
- _command = "remove";
-
- return 1;
- }
-
- if (*argcp != 2) {
- log_error("%s: Too few arguments.", base);
- _usage(stderr);
- free(device_name);
- return 0;
- }
-
- /* FIXME move these to make them available to native dmsetup */
- if (!(loop_file = _get_abspath((*argvp)[(find) ? 0 : 1]))) {
- log_error("%s: Could not parse loop file name %s.",
- base, (*argvp)[1]);
- _usage(stderr);
- free(device_name);
- return 0;
- }
-
- _table = malloc(LOOP_TABLE_SIZE);
- if (!_table ||
- !_loop_table(_table, (size_t) LOOP_TABLE_SIZE, loop_file, device_name, offset)) {
- log_error("Could not build device-mapper table for %s.", (*argvp)[0]);
- free(device_name);
- return 0;
- }
- _switches[TABLE_ARG]++;
-
- _command = "create";
- (*argvp)[0] = device_name ;
- *argcp = 1;
-
- return 1;
-}
-
-static int _process_options(const char *options)
-{
- const char *s, *end;
- size_t len;
-
- /* Tree options are processed separately. */
- if (_switches[TREE_ARG])
- return _process_tree_options(_string_args[OPTIONS_ARG]);
-
- /* Column options are processed separately by _report_init (called later). */
- if (_switches[COLS_ARG])
- return 1;
-
- /* No options specified. */
- if (!_switches[OPTIONS_ARG])
- return 1;
-
- /* Set defaults. */
- _dev_name_type = DN_DEVNO;
-
- /* Parse. */
- for (s = options; s && *s; s++) {
- len = 0;
- for (end = s; *end && *end != ','; end++, len++)
- ;
- if (!strncmp(s, "devno", len))
- _dev_name_type = DN_DEVNO;
- else if (!strncmp(s, "blkdevname", len))
- _dev_name_type = DN_BLK;
- else if (!strncmp(s, "devname", len))
- _dev_name_type = DN_MAP;
- else {
- log_error("Option not recognised: %s.", s);
- return 0;
- }
-
- if (!*end)
- break;
- s = end;
- }
-
- return 1;
-}
-
-static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
-{
- const char *base;
- char *namebase, *s;
- static int ind;
- int c, r, i;
-
-#ifdef HAVE_GETOPTLONG
- static struct option long_options[] = {
- {"readonly", 0, &ind, READ_ONLY},
- {"alias", 1, &ind, ALIAS_ARG},
- {"alldevices", 0, &ind, ALL_DEVICES_ARG},
- {"allprograms", 0, &ind, ALL_PROGRAMS_ARG},
- {"allregions", 0, &ind, ALL_REGIONS_ARG},
- {"area", 0, &ind, AREA_ARG},
- {"areas", 1, &ind, AREAS_ARG},
- {"areasize", 1, &ind, AREA_SIZE_ARG},
- {"bounds", 1, &ind, BOUNDS_ARG},
- {"checks", 0, &ind, CHECKS_ARG},
- {"clear", 0, &ind, CLEAR_ARG},
- {"columns", 0, &ind, COLS_ARG},
- {"concise", 0, &ind, CONCISE_ARG},
- {"count", 1, &ind, COUNT_ARG},
- {"deferred", 0, &ind, DEFERRED_ARG},
- {"select", 1, &ind, SELECT_ARG},
- {"exec", 1, &ind, EXEC_ARG},
- {"filemap", 0, &ind, FILEMAP_ARG},
- {"follow", 1, &ind, FOLLOW_ARG},
- {"force", 0, &ind, FORCE_ARG},
- {"foreground", 0, &ind, FOREGROUND_ARG},
- {"gid", 1, &ind, GID_ARG},
- {"group", 0, &ind, GROUP_ARG},
- {"groupid", 1, &ind, GROUP_ID_ARG},
- {"help", 0, &ind, HELP_ARG},
- {"histogram", 0, &ind, HISTOGRAM_ARG},
- {"inactive", 0, &ind, INACTIVE_ARG},
- {"interval", 1, &ind, INTERVAL_ARG},
- {"length", 1, &ind, LENGTH_ARG},
- {"manglename", 1, &ind, MANGLENAME_ARG},
- {"major", 1, &ind, MAJOR_ARG},
- {"minor", 1, &ind, MINOR_ARG},
- {"mode", 1, &ind, MODE_ARG},
- {"nameprefixes", 0, &ind, NAMEPREFIXES_ARG},
- {"nogroup", 0, &ind, NOGROUP_ARG},
- {"noflush", 0, &ind, NOFLUSH_ARG},
- {"noheadings", 0, &ind, NOHEADINGS_ARG},
- {"nolockfs", 0, &ind, NOLOCKFS_ARG},
- {"noopencount", 0, &ind, NOOPENCOUNT_ARG},
- {"nosuffix", 0, &ind, NOSUFFIX_ARG},
- {"notable", 0, &ind, NOTABLE_ARG},
- {"notimesuffix", 0, &ind, NOTIMESUFFIX_ARG},
- {"udevcookie", 1, &ind, UDEVCOOKIE_ARG},
- {"nomonitor", 0, &ind, NOMONITOR_ARG},
- {"noudevrules", 0, &ind, NOUDEVRULES_ARG},
- {"noudevsync", 0, &ind, NOUDEVSYNC_ARG},
- {"options", 1, &ind, OPTIONS_ARG},
- {"precise", 0, &ind, PRECISE_ARG},
- {"programid", 1, &ind, PROGRAM_ID_ARG},
- {"raw", 0, &ind, RAW_ARG},
- {"readahead", 1, &ind, READAHEAD_ARG},
- {"region", 0, &ind, REGION_ARG},
- {"regions", 1, &ind, REGIONS_ARG},
- {"regionid", 1, &ind, REGION_ID_ARG},
- {"relative", 0, &ind, RELATIVE_ARG},
- {"retry", 0, &ind, RETRY_ARG},
- {"rows", 0, &ind, ROWS_ARG},
- {"segments", 0, &ind, SEGMENTS_ARG},
- {"separator", 1, &ind, SEPARATOR_ARG},
- {"setuuid", 0, &ind, SETUUID_ARG},
- {"showkeys", 0, &ind, SHOWKEYS_ARG},
- {"sort", 1, &ind, SORT_ARG},
- {"start", 1, &ind, START_ARG},
- {"table", 1, &ind, TABLE_ARG},
- {"target", 1, &ind, TARGET_ARG},
- {"tree", 0, &ind, TREE_ARG},
- {"uid", 1, &ind, UID_ARG},
- {"units", 1, &ind, UNITS_ARG},
- {"uuid", 1, &ind, UUID_ARG},
- {"unbuffered", 0, &ind, UNBUFFERED_ARG},
- {"unquoted", 0, &ind, UNQUOTED_ARG},
- {"userdata", 1, &ind, USER_DATA_ARG},
- {"verbose", 1, &ind, VERBOSE_ARG},
- {"verifyudev", 0, &ind, VERIFYUDEV_ARG},
- {"version", 0, &ind, VERSION_ARG},
- {"yes", 0, &ind, YES_ARG},
- {"addnodeonresume", 0, &ind, ADD_NODE_ON_RESUME_ARG},
- {"addnodeoncreate", 0, &ind, ADD_NODE_ON_CREATE_ARG},
- {0, 0, 0, 0}
- };
-#else
- struct option long_options;
-#endif
-
- /*
- * Zero all the index counts.
- */
- memset(&_switches, 0, sizeof(_switches));
- memset(&_int_args, 0, sizeof(_int_args));
- _read_ahead_flags = 0;
-
- if (!(namebase = strdup((*argvp)[0]))) {
- log_error("Failed to duplicate name.");
- return 0;
- }
-
- base = dm_basename(namebase);
-
- i = 0;
- do {
- if (!strcmp(base, _base_commands[i].name)) {
- _base_command = _base_commands[i].command;
- _base_command_type = _base_commands[i].type;
- break;
- }
- } while (++i < _num_base_commands);
-
- free(namebase);
-
- if (_base_command_type == DEVMAP_NAME_TYPE) {
- _switches[COLS_ARG]++;
- _switches[NOHEADINGS_ARG]++;
- _switches[OPTIONS_ARG]++;
- _switches[MAJOR_ARG]++;
- _switches[MINOR_ARG]++;
- _string_args[OPTIONS_ARG] = (char *) "name";
-
- if (*argcp == 3) {
- _int_args[MAJOR_ARG] = atoi((*argvp)[1]);
- _int_args[MINOR_ARG] = atoi((*argvp)[2]);
- *argcp -= 2;
- *argvp += 2;
- } else if ((*argcp == 2) &&
- (2 == sscanf((*argvp)[1], "%i:%i",
- &_int_args[MAJOR_ARG],
- &_int_args[MINOR_ARG]))) {
- *argcp -= 1;
- *argvp += 1;
- } else {
- _usage(stderr);
- return 0;
- }
-
- _command = "info";
- (*argvp)++;
- (*argcp)--;
-
- return 1;
- }
-
- if (_base_command_type == LOSETUP_TYPE) {
- r = _process_losetup_switches(_base_commands[_base_command].name, argcp, argvp, dev_dir);
- return r;
- }
-
- optarg = 0;
- optind = OPTIND_INIT;
- while ((ind = -1, c = GETOPTLONG_FN(*argcp, *argvp, "cCfG:hj:m:M:no:O:rS:u:U:vy",
- long_options, NULL)) != -1) {
- if (ind == ALIAS_ARG) {
- _switches[ALIAS_ARG]++;
- _string_args[ALIAS_ARG] = optarg;
- }
- if (ind == ALL_DEVICES_ARG)
- _switches[ALL_DEVICES_ARG]++;
- if (ind == ALL_PROGRAMS_ARG)
- _switches[ALL_PROGRAMS_ARG]++;
- if (ind == ALL_REGIONS_ARG)
- _switches[ALL_REGIONS_ARG]++;
- if (ind == AREA_ARG)
- _switches[AREA_ARG]++;
- if (ind == AREAS_ARG) {
- _switches[AREAS_ARG]++;
- _int_args[AREAS_ARG] = atoi(optarg);
- }
- if (ind == AREA_SIZE_ARG) {
- _switches[AREA_SIZE_ARG]++;
- _string_args[AREA_SIZE_ARG] = optarg;
- }
- if (ind == USER_DATA_ARG) {
- _switches[USER_DATA_ARG]++;
- _string_args[USER_DATA_ARG] = optarg;
- }
- if (c == ':' || c == '?')
- return_0;
- if (c == 'h' || ind == HELP_ARG)
- _switches[HELP_ARG]++;
- if (ind == CONCISE_ARG)
- _switches[CONCISE_ARG]++;
- if (ind == BOUNDS_ARG) {
- _switches[BOUNDS_ARG]++;
- _string_args[BOUNDS_ARG] = optarg;
- }
- if (ind == CLEAR_ARG)
- _switches[CLEAR_ARG]++;
- if (c == 'c' || c == 'C' || ind == COLS_ARG)
- _switches[COLS_ARG]++;
- if (ind == FILEMAP_ARG)
- _switches[FILEMAP_ARG]++;
- if (ind == FOLLOW_ARG) {
- _switches[FOLLOW_ARG]++;
- _string_args[FOLLOW_ARG] = optarg;
- }
- if (c == 'f' || ind == FORCE_ARG)
- _switches[FORCE_ARG]++;
- if (ind == FOREGROUND_ARG)
- _switches[FOREGROUND_ARG]++;
- if (c == 'r' || ind == READ_ONLY)
- _switches[READ_ONLY]++;
- if (ind == HISTOGRAM_ARG)
- _switches[HISTOGRAM_ARG]++;
- if (ind == LENGTH_ARG) {
- _switches[LENGTH_ARG]++;
- _string_args[LENGTH_ARG] = optarg;
- }
- if (c == 'j' || ind == MAJOR_ARG) {
- _switches[MAJOR_ARG]++;
- _int_args[MAJOR_ARG] = atoi(optarg);
- }
- if (ind == REGIONS_ARG) {
- _switches[REGIONS_ARG]++;
- _string_args[REGIONS_ARG] = optarg;
- }
- if (c == 'm' || ind == MINOR_ARG) {
- _switches[MINOR_ARG]++;
- _int_args[MINOR_ARG] = atoi(optarg);
- }
- if (ind == NOSUFFIX_ARG)
- _switches[NOSUFFIX_ARG]++;
- if (c == 'n' || ind == NOTABLE_ARG)
- _switches[NOTABLE_ARG]++;
- if (ind == NOTIMESUFFIX_ARG)
- _switches[NOTIMESUFFIX_ARG]++;
- if (c == 'o' || ind == OPTIONS_ARG) {
- _switches[OPTIONS_ARG]++;
- _string_args[OPTIONS_ARG] = optarg;
- }
- if (ind == PROGRAM_ID_ARG) {
- _switches[PROGRAM_ID_ARG]++;
- _string_args[PROGRAM_ID_ARG] = optarg;
- }
- if (ind == PRECISE_ARG)
- _switches[PRECISE_ARG]++;
- if (ind == RAW_ARG)
- _switches[RAW_ARG]++;
- if (ind == REGION_ARG)
- _switches[REGION_ARG]++;
- if (ind == REGION_ID_ARG) {
- _switches[REGION_ID_ARG]++;
- _int_args[REGION_ID_ARG] = atoi(optarg);
- }
- if (ind == RELATIVE_ARG)
- _switches[RELATIVE_ARG]++;
- if (ind == SEPARATOR_ARG) {
- _switches[SEPARATOR_ARG]++;
- _string_args[SEPARATOR_ARG] = optarg;
- }
- if (ind == UNITS_ARG) {
- _switches[UNITS_ARG]++;
- _string_args[UNITS_ARG] = optarg;
- }
- if (c == 'O' || ind == SORT_ARG) {
- _switches[SORT_ARG]++;
- _string_args[SORT_ARG] = optarg;
- }
- if (c == 'S' || ind == SELECT_ARG) {
- _switches[SELECT_ARG]++;
- _string_args[SELECT_ARG] = optarg;
- }
- if (ind == START_ARG) {
- _switches[START_ARG]++;
- _string_args[START_ARG] = optarg;
- }
- if (c == 'v' || ind == VERBOSE_ARG)
- _switches[VERBOSE_ARG]++;
- if (c == 'u' || ind == UUID_ARG) {
- _switches[UUID_ARG]++;
- _uuid = optarg;
- }
- if (c == 'y' || ind == YES_ARG)
- _switches[YES_ARG]++;
- if (ind == ADD_NODE_ON_RESUME_ARG)
- _switches[ADD_NODE_ON_RESUME_ARG]++;
- if (ind == ADD_NODE_ON_CREATE_ARG)
- _switches[ADD_NODE_ON_CREATE_ARG]++;
- if (ind == CHECKS_ARG)
- _switches[CHECKS_ARG]++;
- if (ind == COUNT_ARG) {
- _switches[COUNT_ARG]++;
- _int_args[COUNT_ARG] = atoi(optarg);
- if (_int_args[COUNT_ARG] < 0) {
- log_error("Count must be zero or greater.");
- return 0;
- }
- }
- if (ind == UDEVCOOKIE_ARG) {
- _switches[UDEVCOOKIE_ARG]++;
- _udev_cookie = _get_cookie_value(optarg);
- }
- if (ind == NOMONITOR_ARG)
- _switches[NOMONITOR_ARG]++;
- if (ind == NOUDEVRULES_ARG)
- _switches[NOUDEVRULES_ARG]++;
- if (ind == NOUDEVSYNC_ARG)
- _switches[NOUDEVSYNC_ARG]++;
- if (ind == VERIFYUDEV_ARG)
- _switches[VERIFYUDEV_ARG]++;
- if (c == 'G' || ind == GID_ARG) {
- _switches[GID_ARG]++;
- _int_args[GID_ARG] = atoi(optarg);
- }
- if (ind == GROUP_ARG)
- _switches[GROUP_ARG]++;
- if (ind == GROUP_ID_ARG) {
- _switches[GROUP_ID_ARG]++;
- _int_args[GROUP_ID_ARG] = atoi(optarg);
- }
- if (c == 'U' || ind == UID_ARG) {
- _switches[UID_ARG]++;
- _int_args[UID_ARG] = atoi(optarg);
- }
- if (c == 'M' || ind == MODE_ARG) {
- _switches[MODE_ARG]++;
- /* FIXME Accept modes as per chmod */
- errno = 0;
- _int_args[MODE_ARG] = (int) strtol(optarg, &s, 8);
- if (errno || !s || *s || !_int_args[MODE_ARG]) {
- log_error("Invalid argument for --mode: %s. %s",
- optarg, errno ? strerror(errno) : "");
- return 0;
- }
- }
- if (ind == DEFERRED_ARG)
- _switches[DEFERRED_ARG]++;
- if (ind == EXEC_ARG) {
- _switches[EXEC_ARG]++;
- _command_to_exec = optarg;
- }
- if (ind == TARGET_ARG) {
- _switches[TARGET_ARG]++;
- _target = optarg;
- }
- if (ind == SEGMENTS_ARG)
- _switches[SEGMENTS_ARG]++;
- if (ind == INACTIVE_ARG)
- _switches[INACTIVE_ARG]++;
- if (ind == INTERVAL_ARG) {
- _switches[INTERVAL_ARG]++;
- _int_args[INTERVAL_ARG] = atoi(optarg);
- if (_int_args[INTERVAL_ARG] <= 0) {
- log_error("Interval must be a positive integer.");
- return 0;
- }
- }
- if (ind == MANGLENAME_ARG) {
- _switches[MANGLENAME_ARG]++;
- if (!strcasecmp(optarg, "none"))
- _int_args[MANGLENAME_ARG] = DM_STRING_MANGLING_NONE;
- else if (!strcasecmp(optarg, "auto"))
- _int_args[MANGLENAME_ARG] = DM_STRING_MANGLING_AUTO;
- else if (!strcasecmp(optarg, "hex"))
- _int_args[MANGLENAME_ARG] = DM_STRING_MANGLING_HEX;
- else {
- log_error("Unknown name mangling mode.");
- return 0;
- }
- dm_set_name_mangling_mode((dm_string_mangling_t) _int_args[MANGLENAME_ARG]);
- }
- if (ind == NAMEPREFIXES_ARG)
- _switches[NAMEPREFIXES_ARG]++;
- if (ind == NOFLUSH_ARG)
- _switches[NOFLUSH_ARG]++;
- if (ind == NOGROUP_ARG)
- _switches[NOGROUP_ARG]++;
- if (ind == NOHEADINGS_ARG)
- _switches[NOHEADINGS_ARG]++;
- if (ind == NOLOCKFS_ARG)
- _switches[NOLOCKFS_ARG]++;
- if (ind == NOOPENCOUNT_ARG)
- _switches[NOOPENCOUNT_ARG]++;
- if (ind == READAHEAD_ARG) {
- _switches[READAHEAD_ARG]++;
- if (!strcasecmp(optarg, "auto"))
- _int_args[READAHEAD_ARG] = DM_READ_AHEAD_AUTO;
- else if (!strcasecmp(optarg, "none"))
- _int_args[READAHEAD_ARG] = DM_READ_AHEAD_NONE;
- else {
- for (s = optarg; isspace(*s); s++)
- ;
- if (*s == '+')
- _read_ahead_flags = DM_READ_AHEAD_MINIMUM_FLAG;
- _int_args[READAHEAD_ARG] = atoi(optarg);
- if (_int_args[READAHEAD_ARG] < -1) {
- log_error("Negative read ahead value "
- "(%d) is not understood.",
- _int_args[READAHEAD_ARG]);
- return 0;
- }
- }
- }
- if (ind == RETRY_ARG)
- _switches[RETRY_ARG]++;
- if (ind == ROWS_ARG)
- _switches[ROWS_ARG]++;
- if (ind == SETUUID_ARG)
- _switches[SETUUID_ARG]++;
- if (ind == SHOWKEYS_ARG)
- _switches[SHOWKEYS_ARG]++;
- if (ind == TABLE_ARG) {
- _switches[TABLE_ARG]++;
- if (!(_table = strdup(optarg))) {
- log_error("Could not allocate memory for table string.");
- return 0;
- }
- }
- if (ind == TREE_ARG)
- _switches[TREE_ARG]++;
- if (ind == UNQUOTED_ARG)
- _switches[UNQUOTED_ARG]++;
- if (ind == VERSION_ARG)
- _switches[VERSION_ARG]++;
- }
-
- if (_switches[VERBOSE_ARG] > 1) {
- dm_log_init_verbose(_switches[VERBOSE_ARG] - 1);
- if (_switches[VERBOSE_ARG] > 2) {
- if (!(_initial_timestamp = dm_timestamp_alloc()))
- stack;
- else if (!dm_timestamp_get(_initial_timestamp))
- stack;
- else
- log_debug("Timestamp: 0.000000000 seconds");
- }
- }
-
- if ((_switches[MAJOR_ARG] && !_switches[MINOR_ARG]) ||
- (!_switches[MAJOR_ARG] && _switches[MINOR_ARG])) {
- log_error("Please specify both major number and minor number.");
- return 0;
- }
-
- if (_switches[TABLE_ARG] && _switches[NOTABLE_ARG]) {
- log_error("--table and --notable are incompatible.");
- return 0;
- }
-
- if (_switches[ADD_NODE_ON_RESUME_ARG] && _switches[ADD_NODE_ON_CREATE_ARG]) {
- log_error("--addnodeonresume and --addnodeoncreate are incompatible.");
- return 0;
- }
-
- *argvp += optind;
- *argcp -= optind;
-
- if (!*argcp)
- _command = NULL;
- else if (!strcmp((*argvp)[0], "stats")) {
- _base_command = DMSETUP_STATS_CMD;
- _base_command_type = STATS_TYPE;
- _command = "stats";
- (*argvp)++;
- (*argcp)--;
- } else if (_base_command == DMSTATS_CMD) {
- _command = "stats";
- } else if (*argcp) {
- _command = (*argvp)[0];
- (*argvp)++;
- (*argcp)--;
- }
-
- return 1;
-}
-
-static int _perform_command_for_all_repeatable_args(CMD_ARGS)
-{
- do {
- if (!cmd->fn(cmd, subcommand, argc, argv++, NULL, multiple_devices)) {
- log_error("Command failed.");
- return 0;
- }
- } while (cmd->repeatable_cmd && argc-- > 1);
-
- return 1;
-}
-
-static int _do_report_wait(void)
-{
- return _do_timer_wait();
-}
-
-int main(int argc, char **argv)
-{
- int ret = 1, r;
- const char *dev_dir;
- const struct command *cmd;
- const char *subcommand = "";
- int multiple_devices;
-
- (void) setlocale(LC_ALL, "");
-
- dev_dir = getenv(DM_DEV_DIR_ENV_VAR_NAME);
- if (dev_dir && *dev_dir) {
- if (!dm_set_dev_dir(dev_dir)) {
- log_error("Invalid DM_DEV_DIR environment variable value.");
- goto out;
- }
- } else
- dev_dir = DEFAULT_DM_DEV_DIR;
-
- if (!_process_switches(&argc, &argv, dev_dir)) {
- log_error("Couldn't process command line.");
- goto out;
- }
-
- if (_switches[HELP_ARG]) {
- switch (_base_command_type) {
- case STATS_TYPE:
- if ((cmd = _find_stats_subcommand("help")))
- goto doit;
- goto unknown;
- default:
- if ((cmd = _find_dmsetup_command("help")))
- goto doit;
- goto unknown;
- }
- }
-
- if (_switches[VERSION_ARG]) {
- switch (_base_command_type) {
- case STATS_TYPE:
- if ((cmd = _find_stats_subcommand("version")))
- goto doit;
- goto unknown;
- default:
- if ((cmd = _find_dmsetup_command("version")))
- goto doit;
- goto unknown;
- }
- }
-
- if (!_command) {
- _usage(stderr);
- goto out;
- }
-
- if (!(cmd = _find_dmsetup_command(_command))) {
-unknown:
- log_error("Unknown command.");
- _usage(stderr);
- goto out;
- }
-
- if (argc < cmd->min_args ||
- (cmd->max_args >= 0 && argc > cmd->max_args)) {
- log_error("Incorrect number of arguments.");
- _usage(stderr);
- goto out;
- }
-
- if (!_switches[COLS_ARG] && !strcmp(cmd->name, "splitname"))
- _switches[COLS_ARG]++;
-
- if (!strcmp(cmd->name, "stats")) {
- _switches[COLS_ARG]++;
- if (!_switches[UNITS_ARG]) {
- _switches[UNITS_ARG]++;
- _string_args[UNITS_ARG] = (char *) "h";
- }
- }
-
- if (!strcmp(cmd->name, "mangle"))
- dm_set_name_mangling_mode(DM_STRING_MANGLING_NONE);
-
- if (!_process_options(_string_args[OPTIONS_ARG])) {
- log_error("Couldn't process command line.");
- goto out;
- }
-
-#ifdef UDEV_SYNC_SUPPORT
- if (!_set_up_udev_support(dev_dir))
- goto_out;
-#endif
-
- /*
- * Extract subcommand?
- * dmsetup <command> <subcommand> [args...]
- */
- if (cmd->has_subcommands) {
- subcommand = argv[0];
- argc--, argv++;
- }
-
- /* Default to success */
- ret = 0;
-
- /* When -S is given, store the real command for later and run "info -c" first */
- if (_switches[SELECT_ARG] && (cmd->repeatable_cmd == 2)) {
- _selection_cmd = cmd;
- _switches[COLS_ARG] = 1;
- if (!(cmd = _find_dmsetup_command("info"))) {
- log_error(INTERNAL_ERROR "finding dmsetup info command struct.");
- goto out;
- }
- }
-
- if (_switches[COLS_ARG]) {
- if (!_report_init(cmd, subcommand))
- ret = 1;
- if (ret || !_report)
- goto_out;
- }
-
- if (_switches[COUNT_ARG] && _int_args[COUNT_ARG])
- _count = (uint64_t)_int_args[COUNT_ARG];
- else if (_switches[COUNT_ARG] || _switches[INTERVAL_ARG])
- _count = UINT64_MAX;
-
- if (_switches[UNITS_ARG]) {
- _disp_factor = _factor_from_units(_string_args[UNITS_ARG],
- &_disp_units);
- if (!_disp_factor) {
- log_error("Invalid --units argument.");
- ret = 1;
- goto out;
- }
- }
-
- /* Start interval timer. */
- if (_count > 1)
- if (!_start_timer()) {
- ret = 1;
- goto_out;
- }
-
-doit:
- multiple_devices = (cmd->repeatable_cmd && argc != 1 &&
- (argc || (!_switches[UUID_ARG] && !_switches[MAJOR_ARG])));
-
- do {
- r = _perform_command_for_all_repeatable_args(cmd, subcommand, argc, argv, NULL, multiple_devices);
- if (_concise_output_produced) {
- putchar('\n');
- fflush(stdout);
- }
- if (_report) {
- /* only output headings for repeating reports */
- if (_int_args[COUNT_ARG] != 1 && !dm_report_is_empty(_report))
- dm_report_column_headings(_report);
- dm_report_output(_report);
-
- if (_count > 1 && r) {
- putchar('\n');
- fflush(stdout);
- /* wait for --interval and update timestamps */
- if (!_do_report_wait()) {
- ret = 1;
- goto_out;
- }
- }
- }
-
- if (!r) {
- ret = 1;
- goto_out;
- }
- } while (--_count);
-
-out:
- if (_report)
- dm_report_free(_report);
-
- if (_dtree)
- dm_tree_free(_dtree);
-
- free(_table);
-
- if (_initial_timestamp)
- dm_timestamp_destroy(_initial_timestamp);
-
- return (_switches[HELP_ARG] || _switches[VERSION_ARG]) ? 0 : ret;
-}
5 years, 10 months
master - lvmlockd: update method for changing clustered VG
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=e53cfc6a88d7acafde6...
Commit: e53cfc6a88d7acafde6ba3fa293bcf15b9a033bc
Parent: 9b79f0244a17c91d790e2114e1c912e5efe00a2f
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Wed Jun 13 15:30:28 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Jun 13 15:30:28 2018 -0500
lvmlockd: update method for changing clustered VG
The previous method for forcibly changing a clustered VG
to a local VG involved using -cn and locking_type 0.
Since those options are deprecated, replace it with
the same command used for other forced lock type changes:
vgchange --locktype none --lockopt force.
---
lib/commands/toolcontext.h | 1 +
lib/metadata/metadata.c | 9 +++++++++
man/lvmlockd.8_main | 9 +++------
tools/vgchange.c | 1 +
4 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index baecfbd..9849e5f 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -153,6 +153,7 @@ struct cmd_context {
unsigned include_shared_vgs:1; /* report/display cmds can reveal lockd VGs */
unsigned include_active_foreign_vgs:1; /* cmd should process foreign VGs with active LVs */
unsigned vg_read_print_access_error:1; /* print access errors from vg_read */
+ unsigned force_access_clustered:1;
unsigned lockd_gl_disable:1;
unsigned lockd_vg_disable:1;
unsigned lockd_lv_disable:1;
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index 40854de..113c87c 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -4689,6 +4689,15 @@ int vg_flag_write_locked(struct volume_group *vg)
static int _access_vg_clustered(struct cmd_context *cmd, const struct volume_group *vg)
{
if (vg_is_clustered(vg)) {
+ /*
+ * force_access_clustered is only set when forcibly
+ * converting a clustered vg to lock type none.
+ */
+ if (cmd->force_access_clustered) {
+ log_debug("Allowing forced access to clustered vg %s", vg->name);
+ return 1;
+ }
+
if (!cmd->ignore_clustered_vgs)
log_error("Skipping clustered volume group %s", vg->name);
else
diff --git a/man/lvmlockd.8_main b/man/lvmlockd.8_main
index cf4c52e..6411c01 100644
--- a/man/lvmlockd.8_main
+++ b/man/lvmlockd.8_main
@@ -814,8 +814,8 @@ dlm), first change it to a local VG, then to the new type.
All LVs must be inactive to change the lock type.
-First change the clvm VG to a local VG. Within a running clvm cluster,
-change a clvm VG to a local VG with the command:
+First change the clvm/clustered VG to a local VG. Within a running clvm
+cluster, change a clustered VG to a local VG with the command:
vgchange -cn <vgname>
@@ -823,10 +823,7 @@ If the clvm cluster is no longer running on any nodes, then extra options
can be used to forcibly make the VG local. Caution: this is only safe if
all nodes have stopped using the VG:
-vgchange --config 'global/locking_type=0 global/use_lvmlockd=0'
-.RS
--cn <vgname>
-.RE
+vgchange --lock-type none --lock-opt force <vgname>
After the VG is local, follow the steps described in "changing a local VG
to a lockd VG".
diff --git a/tools/vgchange.c b/tools/vgchange.c
index b427a81..891f22a 100644
--- a/tools/vgchange.c
+++ b/tools/vgchange.c
@@ -1039,6 +1039,7 @@ int vgchange_locktype_cmd(struct cmd_context *cmd, int argc, char **argv)
cmd->lockd_vg_disable = 1;
cmd->lockd_lv_disable = 1;
cmd->handles_missing_pvs = 1;
+ cmd->force_access_clustered = 1;
goto process;
}
5 years, 10 months
master - Remove makefile entries for removed script
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=9b79f0244a17c91d790...
Commit: 9b79f0244a17c91d790e2114e1c912e5efe00a2f
Parent: fa00fce97c1eabef9f1ec88dd420660cd4d6f004
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Wed Jun 13 15:02:12 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Jun 13 15:04:26 2018 -0500
Remove makefile entries for removed script
---
configure.ac | 1 -
scripts/.gitignore | 1 -
scripts/Makefile.in | 2 --
3 files changed, 0 insertions(+), 4 deletions(-)
diff --git a/configure.ac b/configure.ac
index 7d490f6..4c431e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1804,7 +1804,6 @@ scripts/lvm2_lvmpolld_init_red_hat
scripts/lvm2_lvmpolld_systemd_red_hat.service
scripts/lvm2_lvmpolld_systemd_red_hat.socket
scripts/lvm2_lvmlockd_systemd_red_hat.service
-scripts/lvm2_lvmlocking_systemd_red_hat.service
scripts/lvm2_monitoring_init_red_hat
scripts/lvm2_monitoring_systemd_red_hat.service
scripts/lvm2_pvscan_systemd_red_hat@.service
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 0368f23..314bdb5 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -16,7 +16,6 @@ lvm2_lvmetad_init_red_hat
lvm2_lvmetad_systemd_red_hat.service
lvm2_lvmetad_systemd_red_hat.socket
lvm2_lvmlockd_systemd_red_hat.service
-lvm2_lvmlocking_systemd_red_hat.service
lvm2_lvmpolld_init_red_hat
lvm2_lvmpolld_systemd_red_hat.service
lvm2_lvmpolld_systemd_red_hat.socket
diff --git a/scripts/Makefile.in b/scripts/Makefile.in
index 208aebf..f0addc3 100644
--- a/scripts/Makefile.in
+++ b/scripts/Makefile.in
@@ -109,7 +109,6 @@ ifeq ("@BUILD_LVMPOLLD@", "yes")
endif
ifeq ("@BUILD_LVMLOCKD@", "yes")
$(INSTALL_DATA) lvm2_lvmlockd_systemd_red_hat.service $(systemd_unit_dir)/lvm2-lvmlockd.service
- $(INSTALL_DATA) lvm2_lvmlocking_systemd_red_hat.service $(systemd_unit_dir)/lvm2-lvmlocking.service
endif
ifneq ("@CLVMD@", "none")
$(INSTALL_DATA) lvm2_clvmd_systemd_red_hat.service $(systemd_unit_dir)/lvm2-clvmd.service
@@ -159,7 +158,6 @@ DISTCLEAN_TARGETS += \
lvm2_lvmpolld_systemd_red_hat.service \
lvm2_lvmpolld_systemd_red_hat.socket \
lvm2_lvmlockd_systemd_red_hat.service \
- lvm2_lvmlocking_systemd_red_hat.service \
lvm2_monitoring_init_red_hat \
lvm2_monitoring_systemd_red_hat.service \
lvm2_pvscan_systemd_red_hat@.service \
5 years, 10 months
master - Remove systemd script for starting shared VG
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=fa00fce97c1eabef9f1...
Commit: fa00fce97c1eabef9f1ec88dd420660cd4d6f004
Parent: a163d5341a90c1c2f656b0d15c055bbd90333a60
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Tue Jun 12 14:35:27 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Jun 13 14:37:16 2018 -0500
Remove systemd script for starting shared VG
Shared VGs will generally be started and activated by
the resource agent. Without the agent, this script doesn't
have a good way to know which LVs to activate.
---
scripts/lvm2_lvmlocking_systemd_red_hat.service.in | 24 --------------------
1 files changed, 0 insertions(+), 24 deletions(-)
diff --git a/scripts/lvm2_lvmlocking_systemd_red_hat.service.in b/scripts/lvm2_lvmlocking_systemd_red_hat.service.in
deleted file mode 100644
index 62d8177..0000000
--- a/scripts/lvm2_lvmlocking_systemd_red_hat.service.in
+++ /dev/null
@@ -1,24 +0,0 @@
-[Unit]
-Description=Availability of lockspaces in lvmlockd
-Documentation=man:lvmlockd(8)
-After=lvm2-lvmlockd.service sanlock.service dlm.service
-
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-
-# start lockspaces and wait for them to finish starting
-ExecStart=@SBINDIR@/lvm vgchange --lock-start --lock-opt autowait
-
-# auto activate LVs in the newly started lockd VGs
-ExecStart=@SBINDIR@/lvm vgchange -aay -S 'locktype=sanlock || locktype=dlm'
-
-# deactivate LVs in lockd VGs
-ExecStop=@SBINDIR@/lvm vgchange -an -S 'locktype=sanlock || locktype=dlm'
-
-# stop lockspaces and wait for them to finish stopping
-ExecStop=@SBINDIR@/lvmlockctl --stop-lockspaces --wait 1
-
-[Install]
-WantedBy=multi-user.target
-
5 years, 10 months
master - tests: remove metadata-dirs
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=d067263f51f782661d6...
Commit: d067263f51f782661d6faa00b106b872b3e1dc6f
Parent: 5fca75877d8af5fe6f277159490eda4817740060
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Wed Jun 13 14:14:23 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Jun 13 14:14:23 2018 -0500
tests: remove metadata-dirs
metadata dirs are removed
---
test/shell/metadata-dirs.sh | 48 -------------------------------------------
1 files changed, 0 insertions(+), 48 deletions(-)
diff --git a/test/shell/metadata-dirs.sh b/test/shell/metadata-dirs.sh
deleted file mode 100644
index 2a43340..0000000
--- a/test/shell/metadata-dirs.sh
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright (C) 2011 Red Hat, Inc. All rights reserved.
-#
-# This copyrighted material is made available to anyone wishing to use,
-# modify, copy, or redistribute it subject to the terms and conditions
-# of the GNU General Public License v.2.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-SKIP_WITH_LVMPOLLD=1
-
-. lib/inittest
-
-aux prepare_devs 3
-get_devs
-
-pvcreate --metadatacopies 0 "${DEVICES[@]}"
-not vgcreate $SHARED "$vg" "${DEVICES[@]}"
-
-aux lvmconf "metadata/dirs = [ \"$TESTDIR/mda\" ]"
-
-vgcreate $SHARED $vg "$dev1"
-check vg_field $vg vg_mda_count 1
-vgremove -ff $vg
-
-vgcreate $SHARED "$vg" "${DEVICES[@]}"
-check vg_field $vg vg_mda_count 1
-vgremove -ff $vg
-
-pvcreate --metadatacopies 1 --metadataignore y "$dev1"
-vgcreate $SHARED "$vg" "${DEVICES[@]}"
-check vg_field $vg vg_mda_count 2
-vgremove -ff $vg
-
-pvcreate --metadatacopies 1 --metadataignore n "$dev1"
-vgcreate $SHARED "$vg" "${DEVICES[@]}"
-check vg_field $vg vg_mda_count 2
-vgremove -ff $vg
-
-pvcreate --metadatacopies 0 "$dev1"
-aux lvmconf "metadata/dirs = [ \"$TESTDIR/mda\", \"$TESTDIR/mda2\" ]"
-vgcreate $SHARED "$vg" "${DEVICES[@]}"
-check vg_field $vg vg_mda_count 2
-vgremove -ff $vg
5 years, 10 months
master - tests: remove vgconvert usage
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=a163d5341a90c1c2f65...
Commit: a163d5341a90c1c2f656b0d15c055bbd90333a60
Parent: d067263f51f782661d6faa00b106b872b3e1dc6f
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Wed Jun 13 14:16:28 2018 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Jun 13 14:16:28 2018 -0500
tests: remove vgconvert usage
---
test/shell/metadata.sh | 38 --------------------------------------
test/shell/pvcreate-usage.sh | 19 -------------------
2 files changed, 0 insertions(+), 57 deletions(-)
diff --git a/test/shell/metadata.sh b/test/shell/metadata.sh
index aae749a..a86bc0b 100644
--- a/test/shell/metadata.sh
+++ b/test/shell/metadata.sh
@@ -46,41 +46,3 @@ for mdacp in 1 0; do
vgremove -f $vg
done
not grep "Cached VG .* incorrect PV list" out0
-
-# begin M1 metadata tests
-if test -n "$LVM_TEST_LVM1" ; then
-
-pvcreate -M1 "$dev1" "$dev2" "$dev3"
-pv3_uuid=$(get pv_field "$dev3" pv_uuid)
-vgcreate $SHARED -M1 $vg "$dev1" "$dev2" "$dev3"
-pvchange --uuid "$dev1"
-
-# verify pe_start of all M1 PVs
-pv_align="128.00k"
-check pv_field "$dev1" pe_start $pv_align
-check pv_field "$dev2" pe_start $pv_align
-check pv_field "$dev3" pe_start $pv_align
-
-pvs --units k -o name,pe_start,vg_mda_size,vg_name "${DEVICES[@]}"
-
-# upgrade from v1 to v2 metadata
-vgconvert -M2 $vg
-
-# verify pe_start of all M2 PVs
-check pv_field "$dev1" pe_start $pv_align
-check pv_field "$dev2" pe_start $pv_align
-check pv_field "$dev3" pe_start $pv_align
-
-pvs --units k -o name,pe_start,vg_mda_size,vg_name "${DEVICES[@]}"
-
-# create backup and then restore $dev3
-vgcfgbackup -f "$TESTDIR/bak-%s" "$vg"
-pvcreate -ff -y --restorefile "$TESTDIR/bak-$vg" --uuid "$pv3_uuid" "$dev3"
-vgcfgrestore -f "$TESTDIR/bak-$vg" "$vg"
-
-# verify pe_start of $dev3
-check pv_field "$dev3" pe_start $pv_align
-
-fi
-# end M1 metadata tests
-
diff --git a/test/shell/pvcreate-usage.sh b/test/shell/pvcreate-usage.sh
index 4ece05f..0a6f1ea 100644
--- a/test/shell/pvcreate-usage.sh
+++ b/test/shell/pvcreate-usage.sh
@@ -143,25 +143,6 @@ pvcreate --metadatasize 128k --metadatacopies 2 --dataalignmentoffset 7s "$dev1"
check pv_field "$dev1" pv_mda_count 2
# FIXME: compare start of 2nd mda with and without --dataalignmentoffset
-#COMM 'pv with LVM1 compatible data alignment can be convereted'
-#compatible == LVM1_PE_ALIGN == 64k
-if test -n "$LVM_TEST_LVM1" ; then
-pvcreate --dataalignment 256k "$dev1"
-vgcreate $SHARED -s 1m $vg "$dev1"
-vgconvert -M1 $vg
-vgconvert -M2 $vg
-check pv_field "$dev1" pe_start 256.00k
-vgremove $vg
-fi
-
-#COMM 'pv with LVM1 incompatible data alignment cannot be convereted'
-if test -n "$LVM_TEST_LVM1" ; then
-pvcreate --dataalignment 10k "$dev1"
-vgcreate $SHARED -s 1m $vg "$dev1"
-not vgconvert -M1 $vg
-vgremove $vg
-fi
-
#COMM 'vgcfgrestore allows pe_start=0'
#basically it produces nonsense, but it tests vgcfgrestore,
#not that final cfg is usable...
5 years, 10 months