--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+\f
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
--- /dev/null
+Installation
+============
+
+1) Generate custom makefiles.
+
+ Run the 'configure' script from the top directory.
+
+ If you don't want to include the LVM1 backwards-compatibility code use:
+ ./configure --with-lvm1=none
+
+ To separate the LVM1 support into a shared library loaded by lvm.conf use:
+ ./configure --with-lvm1=shared
+
+ Use ./configure --help to see other options.
+
+2) Build and install.
+
+ Run 'make' from the top directory to build everything you configured.
+ Run 'make install' to build and install everything you configured.
+
+ If you only want the device-mapper libraries and tools use
+ 'make device-mapper' or 'make install_device-mapper'.
+
+3) If using LVM2, create a configuration file.
+
+ The tools will work fine without a configuration file being
+ present, but you ought to review the example file in doc/example.conf.
+
+Please also refer to the WHATS_NEW file and the manual pages for the
+individual commands.
+
--- /dev/null
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SUBDIRS = doc include man scripts
+
+ifeq ("@UDEV_RULES@", "yes")
+ SUBDIRS += udev
+endif
+
+ifeq ("@INTL@", "yes")
+ SUBDIRS += po
+endif
+
+SUBDIRS += lib tools daemons libdm
+
+ifeq ("@APPLIB@", "yes")
+ SUBDIRS += liblvm
+endif
+
+ifeq ($(MAKECMDGOALS),distclean)
+ SUBDIRS = doc include man scripts \
+ lib tools daemons libdm \
+ udev po liblvm test/api test
+endif
+DISTCLEAN_DIRS += lcov_reports*
+DISTCLEAN_TARGETS += config.cache config.log config.status make.tmpl
+
+include make.tmpl
+
+libdm: include
+lib: libdm
+liblvm: lib
+daemons: lib tools
+tools: lib device-mapper
+po: tools daemons
+
+lib.device-mapper: include.device-mapper
+libdm.device-mapper: include.device-mapper
+liblvm.device-mapper: include.device-mapper
+daemons.device-mapper: libdm.device-mapper
+tools.device-mapper: libdm.device-mapper
+device-mapper: tools.device-mapper daemons.device-mapper man.device-mapper
+
+ifeq ("@INTL@", "yes")
+lib.pofile: include.pofile
+tools.pofile: lib.pofile
+daemons.pofile: lib.pofile
+po.pofile: tools.pofile daemons.pofile
+pofile: po.pofile
+endif
+
+ifneq ("$(CFLOW_CMD)", "")
+tools.cflow: libdm.cflow lib.cflow
+daemons.cflow: tools.cflow
+cflow: include.cflow
+endif
+
+ifneq ("@CSCOPE_CMD@", "")
+cscope.out:
+ @CSCOPE_CMD@ -b -R -s$(top_srcdir)
+all: cscope.out
+endif
+DISTCLEAN_TARGETS += cscope.out
+
+check check_cluster check_local: all
+ $(MAKE) -C test $(@)
+
+install_system_dirs:
+ $(INSTALL_DIR) $(DESTDIR)$(DEFAULT_SYS_DIR)
+ $(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_ARCHIVE_DIR)
+ $(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_BACKUP_DIR)
+ $(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_CACHE_DIR)
+ $(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_LOCK_DIR)
+ $(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_RUN_DIR)
+ $(INSTALL_ROOT_DATA) /dev/null $(DESTDIR)$(DEFAULT_CACHE_DIR)/.cache
+
+install_initscripts:
+ $(MAKE) -C scripts install_initscripts
+
+LCOV_TRACES = libdm.info lib.info tools.info \
+ daemons/dmeventd.info daemons/clvmd.info
+CLEAN_TARGETS += $(LCOV_TRACES)
+
+ifneq ("$(LCOV)", "")
+.PHONY: lcov-reset lcov lcov-dated $(LCOV_TRACES)
+
+ifeq ($(MAKECMDGOALS),lcov-dated)
+LCOV_REPORTS_DIR := lcov_reports-$(shell date +%Y%m%d%k%M%S)
+lcov-dated: lcov
+else
+LCOV_REPORTS_DIR := lcov_reports
+endif
+
+lcov-reset:
+ $(LCOV) --zerocounters $(addprefix -d , $(basename $(LCOV_TRACES)))
+
+# maybe use subdirs processing to create tracefiles...
+$(LCOV_TRACES):
+ $(LCOV) -b $(top_srcdir)/$(basename $@) \
+ -d $(basename $@) -c -o - | $(SED) \
+ -e "s/\(dmeventd_lvm.[ch]\)/plugins\/lvm2\/\1/" \
+ -e "s/\(dmeventd_mirror.c\)/plugins\/mirror\/\1/" \
+ -e "s/\(dmeventd_snapshot.c\)/plugins\/snapshot\/\1/" \
+ >$@
+
+ifneq ("$(GENHTML)", "")
+lcov: $(LCOV_TRACES)
+ $(RM) -r $(LCOV_REPORTS_DIR)
+ $(MKDIR_P) $(LCOV_REPORTS_DIR)
+ for i in $(LCOV_TRACES); do \
+ test -s $$i && lc="$$lc $$i"; \
+ done; \
+ test -z "$$lc" || $(GENHTML) -p @abs_top_builddir@ \
+ -o $(LCOV_REPORTS_DIR) $$lc
+endif
+
+endif
+
+ifeq ("$(TESTING)", "yes")
+# testing and report generation
+RUBY=ruby1.9 -Ireport-generators/lib -Ireport-generators/test
+
+.PHONEY: unit-test ruby-test test-programs
+
+# FIXME: put dependencies on libdm and liblvm
+test-programs:
+ cd unit-tests/regex && $(MAKE)
+ cd unit-tests/datastruct && $(MAKE)
+ cd unit-tests/mm && $(MAKE)
+
+unit-test: test-programs
+ $(RUBY) report-generators/unit_test.rb $(shell find . -name TESTS)
+ $(RUBY) report-generators/title_page.rb
+
+memcheck: test-programs
+ $(RUBY) report-generators/memcheck.rb $(shell find . -name TESTS)
+ $(RUBY) report-generators/title_page.rb
+
+ruby-test:
+ $(RUBY) report-generators/test/ts.rb
+endif
--- /dev/null
+This tree contains the LVM2 and device-mapper tools and libraries.
+
+For more information about LVM2 read the changelog in the WHATS_NEW file.
+Installation instructions are in INSTALL.
+
+There is no warranty - see COPYING and COPYING.LIB.
+
+Tarballs are available from:
+ ftp://sources.redhat.com/pub/lvm2/
+
+To access the CVS tree use:
+ cvs -d :pserver:cvs@sources.redhat.com:/cvs/lvm2 login
+ CVS password: cvs
+ cvs -d :pserver:cvs@sources.redhat.com:/cvs/lvm2 co LVM2
+
+Mailing list for general discussion related to LVM2:
+ linux-lvm@redhat.com
+ Subscribe from https://www.redhat.com/mailman/listinfo/linux-lvm
+
+Mailing list for LVM2 development, patches and commits:
+ lvm-devel@redhat.com
+ Subscribe from https://www.redhat.com/mailman/listinfo/linux-lvm
+
+Mailing list for device-mapper development, including kernel patches
+and multipath-tools:
+ dm-devel@redhat.com
+ Subscribe from https://www.redhat.com/mailman/listinfo/dm-devel
--- /dev/null
+2.02.79(2) (2010-12-20)
--- /dev/null
+1.02.60 (2010-12-20)
--- /dev/null
+Version 2.02.79 - 20th December 2010
+====================================
+ Remove some unused variables.
+ Add missing test for reallocation error in _find_parallel_space().
+ Add checks for allocation errors in config node clonning.
+ Fix error path if regex engine cannot be created in _build_matcher().
+ Use char* arithmetic in target_version(), _process_all() & _targets().
+ Fixing const cast gcc warnings in the code.
+ Check read() and close() results in _get_cmdline().
+ Add const for struct config_node usage.
+ Fix NULL pointer check in error path in clvmd do_command(). (2.02.78)
+ Fix device.c #include to ensure 64-bit fopen64 use. (2.02.51)
+ Add copy_percent and snap_percent to liblvm.
+ Enhance vg_validate to ensure integrity of LV and PV structs referenced.
+ Enhance vg_validate to check composition of pvmove LVs.
+ Create /var/run/lvm directory during clvmd initialisation if missing.
+ Use new dm_prepare_selinux_context instead of dm_set_selinux_context.
+ Avoid revalidating the label cache immediately after scanning.
+ Support scanning for a single VG in independent mdas.
+ Don't skip full scan when independent mdas are present even if memlock is set.
+ Set cmd->independent_metadata_areas if metadata/dirs or disk_areas in use.
+ Cope better with an undefined target_percent operation in _percent_run.
+ Avoid writing to freed memory in vg_release and rename to free_vg. (2.02.78)
+
+Version 2.02.78 - 6th December 2010
+===================================
+ Abort if segment tag allocation fails in pool format _add_stripe_seg.
+ Abort in _mirrored_transient_status if referenced log/image LV is not active.
+ Add backtraces for dev_set() and dev_close_immediate() errors in set_lv().
+ Log any unlink() error in clvmd remove_lockfile().
+ Log any pipe write() or close() errors in clvmd child_init_signal().
+ Detect if orphan vginfo was lost from cache before _lvmcache_update_vgname().
+ Do a full rescan if some device is missing in lvm1 format read_pvs_in_vg.
+ Add missing check that dm_pool_create succeeded in write_config_node().
+ Use dm_snprintf in clvmd-command.c to ensure an overlong buffer is truncated.
+ Don't write to buffer if its reallocation failed in clvmd do_command().
+ Switch from float to fixed point percentage handling.
+ Avoid misleading missing PV warnings in vgextend --restoremissing.
+ Fix memory leak when VG allocation policy in metadata is invalid.
+ Ignore unrecognised allocation policy found in metadata instead of aborting.
+ Factor out tag printing into _out_tags and avoid leaking string buffer.
+ Remove some unused variables & assignments.
+ Add missing vg_release calls in _vg_read_by_vgid.
+ Fix debug logging of derived flag LCK_CACHE in clvmd.
+ Fix test for no system_dir in _init_backup().
+ Disallow lvconvert ops that both allocate & free supplied PEs in a single cmd.
+ Fix liblvm seg_size to give bytes not sectors.
+ Add functions to look up LV/PV by name/uuid to liblvm.
+ Free cmd_context if fallback to LVM1 fails in lvm2_main().
+ Free device name buffer in dmsetup parse_loop_device_name() error paths.
+ Close format lib if init_format_fn fails in _init_formats().
+ Don't leave /proc/mounts open after dmeventd snapshot event processing.
+ Fix out-of-scope arg_vgnames use in process_each_lv().
+ Remove incorrect dm_task_destroy(NULL) from _node_clear_table() error path.
+ Add missing closedir in _rm_blks after removing stray LVM1 VG files.
+ Suppress 'No PV label' message when removing several PVs without mdas.
+ Fix default /etc/lvm permissions to be 0755. (2.02.66)
+
+Version 2.02.77 - 22nd November 2010
+====================================
+ Allocate a pool for dummy VG in _pvsegs_sub_single.
+ Add PV and LV segment types and functions to liblvm.
+ Add set_property functions to liblvm.
+ Remove tag length restriction and allow / = ! : # & characters.
+ Support repetition of --addtag and --deltag arguments.
+ Add infrastructure for specific cmdline arguments to be repeated in groups.
+ Split the_args cmdline arguments and values into arg_props and arg_values.
+ Fix fsadm no longer to require '-f' to resize an unmounted filesystem.
+ Fix fsadm to detect mounted filesystems on older systems. (2.0.75)
+ Extend cling allocation policy to recognise PV tags (cling_by_tags).
+ Add allocation/cling_tag_list to lvm.conf.
+ Regenerate configure with 'autoreconf' for --enable-ocf. (2.02.76)
+
+Version 2.02.76 - 8th November 2010
+===================================
+ Clarify error messages when activation fails due to activation filter use.
+ Add pacemaker script VolumeGroup.ocf with configure --enable-ocf.
+ Import make.tmpl into include/ Makefile.
+ Fix handling of online filesystem resize (using new fsadm return code).
+ Add DIAGNOSTICS section to fsadm man page.
+ Modify fsadm to return different status code for check of mounted filesystem.
+ Update VG metadata only once in vgchange when making multiple changes.
+ Allow independent vgchange arguments to be used together.
+ Automatically unmount invalidated snapshots in dmeventd.
+ Suppress some superfluous messages from clang static analysis.
+ Fix a deadlock caused by double close in clvmd.
+ Fix NULL pointer dereference on too-large MDA error path in _vg_read_raw_area.
+ Use static for internal _align_chunk() and _new_chunk() from pool-fast.c.
+ Fix vgchange to process -a, --refresh, --monitor and --poll like lvchange.
+ Add lvm2app functions to query any pv, vg, or lv property / report field.
+
+Version 2.02.75 - 25th October 2010
+===================================
+ Annotate more variables and parameters as const.
+ Fix missing variable initialization in cluster_send() function from cmirrord.
+ Fix pointer for VG name in _pv_resize_single error code path.
+ Fix warning for changed alignment requirements for dmeventd read/write func.
+ Add global/metadata_read_only to use unrepaired metadata in read-only cmds.
+ Don't take write lock in vgchange --refresh, --poll or --monitor.
+ Skip dm devices in scan if they contain only error targets or are empty.
+ Fix strict-aliasing compile warning in partition table scanning.
+ Fix pthread mutex usage deadlock in clvmd.
+ Automatically extend snapshots with dmeventd according to policy in lvm.conf.
+ Add activation/snapshot_autoextend_threshold/percent to lvm.conf.
+ Fix liblvm2cmd link order to support --as-needed.
+ Remove dependency on libm by replacing floor() by an integer-based algorithm.
+ Fix hang when repairing a mirrored-log that had both devs fail.
+ Convey need for snapshot-merge target in lvconvert error message and man page.
+ Add devices/disable_after_error_count config to limit access to failing devs.
+ Give correct error message when creating a too-small snapshot.
+ Implement vgextend --restoremissing to reinstate missing devs that return.
+ Make lvconvert respect --yes and --force when converting an inactive log.
+ Refactor and add 'get' functions for lv properties/fields.
+ Update script for fsadm testing.
+ Better support of noninteractive shell execution of fsadm.
+ Fix usage of --yes flag for ReiserFS resize in fsadm.
+ Fix detection of mounted filesystems for fsadm when udev is used.
+ Fix assignment of default value to LVM variable in fsadm.
+ Fix support for --yes flag for fsadm.
+ Do not execute lvresize from fsadm --dry-run.
+ Fix fsadm return error code from user's break action.
+ Allow CC to be overridden at build time (for 'scan-build make').
+ Rename 'flags' to 'status' in struct metadata_area.
+ Avoid segfault by limiting partial mode for lvm1 metadata. (2.02.74)
+ Use dm_zalloc and dm_pool_zalloc throughout.
+ Add pv_get_property and create generic internal _get_property function.
+ Add 'get' functions for pv and vg properties/fields.
+ Make generic GET_*_PROPERTY_FN macros with secondary macro for vg, pv & lv.
+ Add tags_format_and_copy() common function and call from _tags_disp.
+ Add id_format_and_copy() common function and call from _uuid_disp.
+ Refactor report.c '*_disp' functions to call supporting functions.
+ Move parts of metadata*.[ch] into new {pv|vg|lv}.[ch] files.
+ Fix vg_read memory leak with directory-based metadata.
+ Fix memory leak of config_tree in reinitialization code path.
+ Fix pool destruction order in dmeventd_lvm2_exit() to avoid leak debug mesg.
+ Read whole /proc/self/maps file before working with maps entries.
+ Speed up unquoting of quoted double quotes and backslashes.
+ Speed up CRC32 calculations by using a larger lookup table.
+
+Version 2.02.74 - 24th September 2010
+=====================================
+ Allow : and @ to be escaped with \ in device names of PVs.
+ Replace alloca with dm_malloc in _aligned_io to avoid stack corruption.
+ Fix partial mode operations for lvm1 metadata format.
+ Track recursive filter iteration to avoid refreshing while in use. (2.02.56)
+ Revert to old glibc vsnprintf behaviour in emit_to_buffer() to catch overflow.
+ Allocate buffer for metadata tags dynamically to remove 4k limit.
+ Add random suffix to archive file names to prevent races when being created.
+ Reinitialize archive and backup handling on toolcontext refresh.
+ Make poll_mirror_progress report PROGRESS_CHECK_FAILED if LV is not a mirror.
+ Like mirrors, don't scan origins if ignore_suspended_devices() is set.
+ Fix return type qualifier to avoid compiler warning. (2.02.69)
+ Automatically generate LSB Requires-Start for clvmd init script.
+ Fix return code of pvmove --abort PV.
+ Fix pvmove --abort to remove even for empty pvmove LV.
+ Add configure --with-default-data-alignment.
+ Update heuristic used for default and detected data alignment.
+ Add "devices/default_data_alignment" to lvm.conf.
+ Add implementation for simple numeric 'get' property functions.
+ Define GET_NUM_PROPERTY_FN macro to simplify numeric property 'get' function
+ Add properties.[ch] to lib/report using columns.h.
+ Add macro definitions to report infrastructure for character array length.
+ Remove explicit double quotes from columns.h 'id' entries.
+ Add 'flags' field to columns.h and define FIELD_MODIFIABLE.
+ Add vg_mda_size and vg_mda_free functions.
+ Simplify MD/swap signature detection in pvcreate and allow aborting.
+ Allow --yes to be used without --force mode.
+ Fix file descriptor leak in swap signature detection error path.
+ Detect and allow abort in pvcreate if LUKS signature is detected.
+ Always mask lock flags correctly when checking for LCK_WRITE.
+
+Version 2.02.73 - 18th August 2010
+==================================
+ Fix potential for corruption during cluster mirror device failure.
+ Use 'SINGLENODE' instead of 'dead' in clvmd singlenode messages.
+ Ignore snapshots when performing mirror recovery beneath an origin.
+ Pass LCK_ORIGIN_ONLY flag around cluster.
+ Add suspend_lv_origin and resume_lv_origin using LCK_ORIGIN_ONLY.
+ Allow internal suspend and resume of origin without its snapshots.
+ Fix dev_manager_transient to access -real device not snapshot-origin.
+ Monitor origin -real device below snapshot instead of overlay device.
+ Don't really change monitoring status when in test mode.
+ Fix some exit statuses when starting/stopping monitoring fails.
+ Enable snapshot monitoring by default when dmeventd is enabled.
+ Move cloned libdevmapper-event client code from segments into lib/activate.
+ Fix 'lvconvert --splitmirrors' in cluster operation.
+ Fix clvmd init script exit code to return 4 when executed as non-root user.
+ Change default alignment of pe_start to 1MB.
+ Add --norestorefile option to pvcreate.
+ Require --restorefile when using pvcreate --uuid.
+ Recognise and give preference to md device partitions (blkext major).
+ Never scan internal LVM devices.
+ Don't ignore user-specified PVs in split-mirror operations. (2.02.71)
+ Fix data corruption bug in cluster mirrors.
+ Require logical volume(s) to be explicitly named for lvconvert --merge.
+ Avoid changing aligned pe_start as a side-effect of very verbose logging.
+ Use built-in rule for device aliases: block/ < dm- < disk/ < mapper/ < other.
+ Fix const warning in dev_manager_info() and _dev_manager_lv_rmnodes().
+ Fix const warning in archive_file structure from archive.c.
+ Clean generated files .exported_symbols_generated, example.conf for distclean.
+ Handle failure of all mirrored log devices and all but one mirror leg.
+ Disallow 'mirrored' log type for cluster mirrors.
+ Do not use VPATH in include/Makefile.
+ Fix exported_symbols generation to use standard compiler arguments.
+ Use #include <> not "" in lvm2app.h which gets installed on the system.
+ Make lib and liblvm.device-mapper wait for include file generation.
+ Fix configure to supply DEFAULT_RUN_DIR to Makefiles.
+ Fix allocation of wrong number of mirror logs with 'remove' fault policy.
+
+Version 2.02.72 - 28th July 2010 [CVE-2010-2526]
+=================================================
+ Change clvmd to communicate with lvm2 via a socket in /var/run/lvm.
+ Return controlled error if clvmd is run by non-root user.
+ Add configure --default-run-dir for /var/run/lvm.
+ Never use clvmd singlenode unless explicitly requested with -Isinglenode.
+
+Version 2.02.71 - 28th July 2010
+================================
+ Document LVM fault handling in doc/lvm_fault_handling.txt.
+ Make vgck warn about missing PVs.
+ Clarify help text for vg_mda_count.
+ Check if cluster log daemon is running before allowing cmirror create.
+ Add unit-tests dir.
+ Add configure --enable-testing and reports and report-generators dirs.
+ Correct LV list order used by lvconvert when splitting a mirror.
+ Check if LV with specified name already exists when splitting a mirror.
+ Fix suspend/resume logic for LVs resulting from splitting a mirror.
+ Update pvcreate, {pv|vg}change, and lvm.conf man pages about metadataignore.
+ Switch cmirrord and clvmd to use dm_create_lockfile.
+ Allow clvmd pidfile to be configurable.
+ Update comments about memory handling in lvm2app.h.
+ Add more verbose messages while checking volume_list and hosttags settings.
+ Add log_error when strdup fails in {vg|lv}_change_tag().
+ Remove unnecessary includes in liblvm files.
+ Use __attribute__ consistently throughout.
+ Fix redundant declarations and always compile with -Wredundant-decls.
+ Fix possible hang when all mirror images of a mirrored log fail.
+ Pass metadataignore to pv_create, pv_setup, _mda_setup, and add_mda.
+ Init mda->list in mda_copy.
+ Do not log backtrace in valid _lv_resume() code path.
+ Cleanup help strings in configure.in.
+ Prompt if metadataignore with vgextend or pvchange would adjust vg_mda_copies.
+ Adjust vg_mda_copies if metadataignore given with vgextend or pvchange.
+ Adjust auto-metadata repair and caching logic to try to cope with empty mdas.
+
+Version 2.02.70 - 6th July 2010
+===============================
+ Remove log directly if all mirror images of a mirrored log fail.
+ Randomly select which mdas to use or ignore.
+ Add some missing standard configure.in checks.
+ Add printf format attributes to yes_no_prompt and fix a caller.
+ Always pass unsuspended dm devices through persistent filter to other filters.
+ Move test for suspended dm devices ahead of other filters.
+ Fix another segfault in clvmd -R if no response from daemon. (2.02.68)
+ Remove superfluous suspended device counter from clvmd.
+ Fix lvm shell crash when input is entirely whitespace.
+ Update partial mode warning message.
+ Preserve memlock balance in clvmd when activation triggers a resume.
+ Restore the removemissing behaviour of lvconvert --repair --use-policies.
+
+Version 2.02.69 - 30th June 2010
+================================
+ Fix vgremove to allow removal of VG with missing PVs. (2.02.52)
+ Add metadata/vgmetadatacopies to lvm.conf.
+ Add --metadataignore to pvcreate and vgextend.
+ Add vg_mda_copies, pv_mda_used_count and vg_mda_used_count to reports.
+ Describe --vgmetadatacopies in lvm.conf and other man pages.
+ Add --[vg]metadatacopies to select number of mdas to use in a VG.
+ Make the metadata ignore bit control read/write metadata areas in a PV.
+ Add pvchange --metadataignore to set or clear a metadata ignore bit.
+ Refactor metadata code to prepare for --metadataignore / --vgmetadatacopies.
+ Ensure region_size of mirrored log does not exceed its full size.
+ Generate liblvm2app exported symbols from header file.
+ Preload libc locale messages to prevent reading it in memory locked state.
+ Fix handling of simultaneous mirror image and mirrored log image failure.
+
+Version 2.02.68 - 23rd June 2010
+================================
+ Fix clvmd initscript status to print only active clustered LVs.
+ Add lv_path to reports to offer full /dev pathname.
+ Fix typo in warning message about missing device with allocated data areas.
+ Add device name and offset to raw_read_mda_header error messages.
+ Honour log argument when down-converting stacked mirror.
+ Sleep to workaround clvmd -S race: socket closed early and server drops cmd.
+ Use early udev synchronisation and update of dev nodes for clustered mirrors.
+ Remove incorrect inclusion of kdev_t.h from cmirrord/functions.h.
+ Add man pages for lvmconf and non-existent lvmsadc and lvmsar tools.
+ Exit successfully when using -o help (but not -o +help) with LVM reports.
+ Do not use internal DLM lock definitions in generic LVM2 clvmd code.
+ Add --force, --nofsck and --resizefs to lvresize/extend/reduce man pages.
+ Fix lvm2cmd example in documentation.
+ Allow use of lvm2app and lvm2cmd headers in C++ code.
+ Remove unused #includes from clvmd files and introduce clvmd-common.h.
+ Move common inclusions to clvmd-common.h.
+ Use #include "" for libdevmapper.h and configure.h throughout tree.
+ Fix LVM_PATH expansion when exec_prefix=NONE. (2.02.67)
+ Fix segfault in clvmd -R if no response from daemon received.
+
+Version 2.02.67 - 4th June 2010
+===============================
+ Handle failed restart of clvmd using -S switch properly.
+ Fix clvmd initscript restart command to start clvmd if not yet running.
+ Use built-in absolute paths in clvmd (clvmd restart and PV and LV queries).
+ Require partial option in lvchange --refresh for partial LVs.
+ Do not fail lvm_init() if init_logging() or _init_rand() generates an errno.
+ Don't merge unchanged persistent cache file before dumping if tool scanned.
+ Fix incorrect memory pool deallocation while using vg_read for files.
+ Add --type parameter description to the lvcreate man page.
+ Replace strncmp kernel version number checks with proper ones.
+ Avoid selecting names under /dev/block if there is an alternative.
+ Update clustered log kernel module name to log-userspace for 2.6.31 onwards.
+ Add replicators' LVs to dtree for activation.
+ Supress activation message if there is a missing replicator VG.
+ Fix scripts/relpath.awk to work in mawk
+ Extend lock_vol to check for missing replicator VGs first.
+ Update _process_one_vg and process_each_lv_in_vg to populate cmd_vg.
+ Add cmd_vg structure and associated functions for replicator.
+ Extend _lv_each_dependency() to handle replicator dependencies.
+ Add check_replicator_segment() to catch internal replicator errors.
+ Initial support for replicator metadata.
+ Extend process_each_lv_in_vg() to provide list of failed lvnames.
+ Consistently return ECMD_FAILED if process_each_*lv() is interrupted.
+
+Version 2.02.66 - 20th May 2010
+===============================
+ If unable to obtain snapshot percentage leave value blank on reports.
+ Add install_system_dirs and install_initscripts makefile targets.
+ Add configure options for system and locking directories.
+ Generate example.conf so default lvm.conf contents can be configured.
+ Install lvmconf script by default.
+ Remove unnecessary versioned dmeventd plugin symlinks.
+ Add tests for lvm_vgname_from_{pvid|device}.
+ Add lvm2app interfaces to lookup a vgname from a pvid and pvname.
+ Update pvchange to always obtain a vg handle for each pv to process.
+ Add find_vgname_from_{pvname|pvid} functions.
+ Add pvid_from_devname and lvmcache_vgname_from_pvid lvmcache functions.
+ Validate orphan and VG_GLOBAL lock order too.
+ Accept orphan VG names as parameters to lock_vol() and related functions.
+ Use is_orphan_vg in place of hard-coded prefix tests and add is_global_vg.
+
+Version 2.02.65 - 17th May 2010
+===============================
+ Fix clvmd init script never to deactivate non-clustered volume groups.
+ Disallow vgchange --clustered if there are active mirrors or snapshots.
+ Introduce lv_is_mirrored.
+ Use /bin/bash for scripts with bashisms.
+ Skip internal lvm devices in scan if ignore_suspended_devices is set.
+ Do not merge old device cache after we run full scan. (2.02.56)
+ Add pkgconfigdir Makefile variable for make install override.
+ Configure pkgconfig udev and selinux dependencies.
+ Switch Libs.private to Requires.private in devmapper.pc and lvm2app.pc.
+ Use pkgconfig Requires.private for devmapper-event.pc.
+ Add libdevmapper to linked libdevmapper-event.so.
+ Link liblvm2cmd.so with libdevmapper-event and libdevmapper.
+ Fix truncated total size displayed by pvscan.
+ Add new --sysinit compound option to vgchange and lvchange.
+ Drop duplicate errors for read failures and missing devices to verbose level.
+ Use $(libdir)/lvm2 with make install_lvm2_plugin.
+ Use $(libdir)/device-mapper with make install_dm_plugin.
+ Add dm_list_splice() function to join two lists together.
+
+Version 2.02.64 - 30th April 2010
+=================================
+ Avoid pointless initialisation when the 'version' command is run directly.
+ Fix memory leak for invalid regex pattern input.
+ Display invalid regex pattern for filter configuration in case of error.
+ Remove no-longer-used arg_ptr_value.
+ Fix -M and --type to use strings, not pointers that change on config refresh.
+ Fix lvconvert error message when existing mirrored LV is not found.
+ Set appropriate udev flags for reserved LVs.
+ Disallow the direct removal of a merging snapshot.
+ Don't preload the origin when removing a snapshot whose merge is pending.
+ Disallow the addition of mirror images while a conversion is happening.
+ Disallow primary mirror image removal when mirror is not in-sync.
+ Remove obsolete --name parameter from vgcfgrestore.
+ Add -S command to clvmd to restart the daemon preserving exclusive locks.
+ Increment lvm2app version from 1 to 2 (memory allocation changes).
+ Change lvm2app memory alloc/free for pv/vg/lv properties.
+ Change daemon lock filename from lvm2_monitor to lvm2-monitor for consistency.
+ Install symbolic .so links with relative paths between usrlibdir and libdir.
+ Add awk script relpath.awk to calculate paths for relative symlinks.
+ Use @AWK@ in makefiles.
+ Fix double DESTDIR usage for infodir and mandir.
+
+Version 2.02.63 - 14th April 2010
+=================================
+ Rename lvm_dump.sh to lvmdump.sh.
+ Allow incomplete mirror restore in lvconvert --repair upon insufficient space.
+ Do not reset position in metadata ring buffer on vgrename and vgcfgrestore.
+ Allow VGs with active LVs to be renamed.
+ Use UUIDs instead of names while processing event handlers.
+ Only pass visible LVs to tools in cmdline VG name/tag expansions without -a.
+ Use typedefs for toollib process_each functions.
+ Use C locales and use_mlockall for clvmd.
+ Refactor code related to vg->pvs list and add pv->vg link.
+ Mask LCK_HOLD flag in cluster VG locks for backwards compatibility.
+ Add activation/polling_interval to lvm.conf as --interval default.
+ Don't ignore error if resuming any LV fails in resume_lvs.
+ Skip closing persistent filter cache file if open failed.
+ Install .a and .so links into $(usrlibdir).
+ Add --enable-write_install options to install user-writable files.
+ Use INSTALL_PROGRAM/DATA/WDATA target.
+ Switch from using VPATH to vpath in Makefiles.
+ Permit mimage LVs to be striped in lvcreate, lvresize and lvconvert.
+ Fix pvmove allocation to take existing parallel stripes into account.
+ Add pvmove_source_seg to struct lv_segment.
+ Fix incorrect removal of symlinks after LV deactivation fails.
+ Fix is_partitioned_dev not to attempt to reopen device.
+ Fix another thread race in clvmd.
+ Refactor management of vg->pvs list.
+ Fix lcov rules and generate better coverage report.
+ Improve vg_validate to detect some loops in lists.
+ Change most remaining log_error WARNING messages to log_warn.
+ Always use blocking lock for VGs and orphan locks.
+ Allocate all memory for segments from private VG mempool.
+ Return newly allocated PV segment after segment split.
+ Optimise searching PV segments for seeking the most recently-added.
+ Remove vg_validate call when parsing cached metadata.
+ Use hash table of LVs to speed up parsing of text metadata with many LVs.
+ Fix two vg_validate messages, adding whitespace and parentheses.
+ When dmeventd is not forking because of -d flag, don't kill parent process.
+ Fix 'make install' when $(builddir) is different from $(srcdir).
+ Fix dso resource leak in error path of dmeventd.
+ Use C locales and use_mlockall for dmeventd.
+ Fix --alloc contiguous policy only to allocate one set of parallel areas.
+ Do not allow {vg|lv}change --ignoremonitoring if on clustered VG.
+ Improved dependency tracking for dmeventd and liblvm2cmd sources.
+ Improved Makefile rules for distclean and cflow targets.
+ Add ability to create mirrored logs for mirror LVs.
+ Fix clvmd cluster propagation of dmeventd monitoring mode.
+ Allow ALLOC_ANYWHERE to split contiguous areas.
+ Use INTERNAL_ERROR for internal errors throughout tree.
+ Add some assertions to allocation code.
+ Introduce pv_area_used into allocation algorithm and add debug messages.
+ Add activation/monitoring to lvm.conf.
+ Add --monitor and --ignoremonitoring to lvcreate.
+ Allow dynamic extension of array of areas selected as allocation candidates.
+ Export and use only valid cookie value in test suite.
+ Remove const modifier for struct volume_group* from process_each_lv_in_vg().
+ Don't allow resizing of internal logical volumes.
+ Fix libdevmapper-event pkgconfig version string to match libdevmapper.
+ Avoid scanning all pvs in the system if operating on a device with mdas.
+ Add configure --with-clvmd=singlenode to use clvmd w/o cluster infrastructure.
+ Get stacktrace if testsuite test drops core and lvm was built with debugging.
+ Disable long living process flag in lvm2app.
+ Fix pvcreate device md filter check.
+ Suppress repeated errors about the same missing PV uuids.
+ Bypass full device scans when using internally-cached VG metadata.
+ Only do one full device scan during each read of text format metadata.
+ Remove unnecessary full_scan parameter from get_vgids and get_vgnames calls.
+ Look up missing PVs by uuid not dev_name in _pvs_single to avoid invalid stat.
+ Make find_pv_in_vg_by_uuid() return same type as related functions.
+ Introduce is_missing_pv().
+ Fix clvmd Makefile to not overwrite LIBS from template definition.
+
+Version 2.02.62 - 9th March 2010
+================================
+ Add use_mlockall and mlock_filter to activation section of lvm.conf.
+ Add default alternative to mlockall using mlock to reduce pinned memory size.
+ Remove -rdynamic from static builds.
+ Update checks for pthread, readline & selinux libs and link only when needed.
+ Introduce makefile vars UDEV_LIBS, DL_LIBS, SELINUX_LIBS, STATIC_LIBS.
+ Introduce makefile vars LVMINTERNAL_LIBS, READLINE_LIBS, PTHREAD_LIBS.
+ Toggle configure help to print --disable-fsadm.
+ Use $() instead of ${} consistently for all Makefile variables.
+ Replace CFLOW_CMD only in make.tmpl and use it as variable elsewhere.
+ Use $(top_builddir) for inclusion of make.tmpl in Makefiles.
+ Fix autoconf warning about ignored datarootdir.
+ Increase AC_PREREQ version to 2.61 (for AC_PROC_SED, AC_PROG_MKDIR_P).
+ Handle misaligned devices that report alignment_offset of -1.
+ Extend core allocation code in preparation for mirrored log areas.
+ Rewrite clvmd init script.
+ Remove lvs_in_vg_activated_by_uuid_only call.
+ No longer fall back to looking up active devices by name if uuid not found.
+ Don't touch /dev in vgmknodes if activation is disabled.
+ Update lvm2app.h Doxygen comments and add lvm2app Doxygen config file.
+ Update nightly tests and lvm2app unit tests to cover tags.
+ Add lvm2app functions lvm_{vg|lv}_{get|add|remove}_tag() functions.
+ Add dm_pool_strdup to allocate and copy memory in tag library function.
+ Refactor vgcreate, vgchange, and lvchange for tag library function.
+ Refactor snapshot-merge deptree and device removal to support info-by-uuid.
+
+Version 2.02.61 - 15th February 2010
+====================================
+ Fix some consts and floating point gcc warnings.
+ Fix dm_report_field_uint64 function to accept 64-bit ints.
+ Change readhead display to use 32-bit -1 const instead of 64-bit.
+ Add LVM_SUPPRESS_LOCKING_FAILURE_MESSAGES environment variable.
+ Remove hard-coding that skipped _mimage devices from 11-dm-lvm.rules.
+ Use udev transactions in test suite.
+ Set udev state automatically instead of using LVM_UDEV_DISABLE_CHECKING.
+ Add lvm_pv_get_size, lvm_pv_get_free and lvm_pv_get_dev_size to lvm2app.
+ Change lvm2app to return all sizes in bytes as documented (not sectors).
+ Add 'fail_if_percent_unsupported' arg to _percent and _percent_run.
+ Remove false "failed to find tree node" error when activating merging origin.
+ Exit with success when lvconvert --repair --use-policies performs no action.
+ Accept a list of LVs with 'lvconvert --merge @tag' using process_each_lv.
+ Avoid unnecessary second resync when adding mimage to core-logged mirror.
+ Exclude internal VG names and uuids from lists returned through lvm2app.
+ Add %ORIGIN support to lv{create,extend,reduce,resize} --extents.
+ Add _mda_copy to clone a struct metadata_area.
+ Remove pointless versioned symlinks to dmeventd plugin libraries.
+ Fix dmeventd snapshot plugin build dependency.
+ Make clvmd -V return status zero.
+ Remove unnecessary 'dmsetup resume' following 'dmsetup create' in tests.
+ Fix cmirrord segfault in clog_cpg list processing when converting mirror log.
+ Deactivate temporary pvmove mirror cluster-wide when activating it fails.
+ Always query device by uuid and not name in clvmd.
+ Add missing metadata vg_reverts in pvmove error paths.
+ Unlock shared lock in clvmd if activation calls fail.
+ Return success from dev_manager_info with non-existent uuid if ioctl succeeds.
+
+Version 2.02.60 - 23rd January 2010
+===================================
+ Extend cmirrord man page.
+ Sleep before first progress check if pvmove/lvconvert interval has prefix '+'.
+ Default to checking progress before waiting in _wait_for_single_lv.
+ Fix cmirror initscript (including syntax error).
+ Eliminate avoidable ioctls for checking open_count in _add_new_lv_to_dtree.
+ Disable memory debugging if dmeventd is configured. (Not thread-safe.)
+ Fix first log message prefix in syslog for dmeventd plugins.
+ Fix exported symbols names for dmeventd lvm2 wrapper plugin.
+ Make failed locking initialisation messages more descriptive.
+
+Version 2.02.59 - 21st January 2010
+===================================
+ Add libdevmapper-event-lvm2.so to serialise dmeventd plugin liblvm2cmd use.
+ Cleanup memory initialization and freeing in pv_read() and pv_create().
+ Clear pointer and counters after their release in _fin_commands().
+ Stop dmeventd trying to access already-removed snapshots.
+ Remove (fallback) /dev mknod from cmirrord.
+ Add t-topology-support.sh and t-snapshot-merge.sh tests.
+ Fix clvmd to never scan suspended devices.
+ Fix dmeventd build outside source tree.
+ Assorted cmirror code changes to remove various compiler warnings.
+ Fix detection of completed snapshot merge.
+ Add Red Hat cmirror initscript (unfinished).
+ Add cmirrord man page (incomplete).
+ Make cluster log communication structures architecture independant.
+ Fix cluster log in-memory bitmap handling.
+ Improve snapshot merge metadata import validation.
+ Improve target type compatibility checking in _percent_run().
+ Add 'target_status_compatible' method to 'struct segtype_handler'.
+ Change underscore to hyphen in table line for clustered log type.
+
+Version 2.02.58 - 14th January 2010
+===================================
+ Cleanup some minor gcc warnings.
+ Add --merge to lvconvert to merge a snapshot into its origin.
+ Fix clvmd automatic target module loading crash (no reset_locking fn).
+ Fix allocation code not to stop at the first area of a PV that fits.
+
+Version 2.02.57 - 12th January 2010
+===================================
+ Ensure exactly one process returns from poll_daemon(), never two.
+ Reset _vgs_locked in lvmcache_init() in child after forking.
+ Define {DM, LVM}_UDEV_DISABLE_CHECKING=1 environment variables during tests.
+ Enable udev_sync and udev_rules in lvm.conf by default while running tests.
+ If LVM_UDEV_DISABLE_CHECKING in set in environment, disable udev warnings.
+ Add --splitmirrors to lvconvert to split off part of a mirror.
+ Change background polldaemon's process name to "(lvm2)".
+ Allow vgremove to remove a VG with PVs missing after a prompt.
+ Return success in lvconvert --repair --use-policies on failed allocation.
+ Keep log type consistent when changing mirror image count.
+ Always set environment variables for an LVM2 device in 11-dm-lvm.rules.
+ Add activation/udev_rules config option in lvm.conf.
+ Add consts to text metadata flag structs.
+ Add macros outfc, outsize, outhint and function out_text_with_comment.
+ Reimplement report FIELD macro using offsetof instead of static structs.
+ Fix fsadm man page typo (fsdam).
+ Rename mirror_device_fault_policy to mirror_image_fault policy.
+ Remove empty PV devices if lvconvert --repair is using defined policies.
+ Use fixed buffer to prevent stack overflow in persistent filter dump.
+ Use extended status of new kernel snapshot target 1.8.0 to detect when empty.
+ Insert stack macros in suspend_lv, resume_lv & (de)activate_lv callers.
+ Add --poll flag to vgchange and lvchange to control background daemon launch.
+ Propagate metadata commit and revert notifications to other cluster nodes.
+ Use proper mask for VG lock mode in clvmd.
+ Allow precommitted metadata to be dropped from lvmcache.
+ Move processing of VG locks to separate function in clvmd.
+ Properly decode all flags in clvmd messages including VG locks.
+ Properly handle precommitted cache flag when only committed metadata present.
+ Resume renamed volumes in reverse order to preserve memlock pairing.
+ Drop cached metadata after device was auto-repaired and removed from VG.
+ Clear MISSING_PV flag if PV reappeared and is empty.
+ Fix removal of multiple devices from a mirror.
+ Also clean up PVs flagged as missing in vgreduce --removemissing --force.
+ Introduce INTERNAL_ERROR macro for error messages and use throughout.
+ Remove superfluous returns from void functions.
+ Destroy allocated mempool in _vg_read_orphans() error path.
+ Fix some pvresize and toollib error paths with missing VG releases/unlocks.
+ Explicitly call suspend for temporary mirror layer.
+ Allow use of precommitted metadata when a PV is missing.
+ Add memlock information to do_lock_lv debug output.
+ Always bypass calls to remote cluster nodes for non-clustered VGs.
+ Permit implicit cluster lock conversion in pre/post callbacks on local node.
+ Permit implicit cluster lock conversion to the lock mode already held.
+ Fix lock flag masking in clvmd so intended code paths get invoked.
+ Replace magic masks in cluster locking code by defined masks.
+ Remove newly-created mirror log from metadata if initial deactivation fails.
+ Correct activated or deactivated text in vgchange summary message.
+ Improve pvmove error message when all source LVs are skipped.
+ Fix memlock imbalance in lv_suspend if already suspended.
+ Fix pvmove test mode not to poll (and fail).
+ Fix vgcreate error message if VG already exists.
+ Fix tools to use log_error when aborted due to user response to prompt.
+ Fix ignored readahead setting in lvcreate --readahead.
+ Fix clvmd memory leak in lv_info_by_lvid by calling release_vg.
+ If aborting due to internal error, always send that message to stderr.
+ Add global/abort_on_internal_errors to lvm.conf to assist testing.
+ Fix test Makefiles when builddir and srcdir differ.
+ Impose limit of 8 mirror images to match the in-kernel kcopyd restriction.
+ Use locking_type 3 (compiled in) for lvmconf --enable-cluster.
+ Remove list.c and list.h with no-longer-used dm_list macros and functions.
+ Log failure type and recognise type 'F' (flush) in dmeventd mirror plugin.
+ Extend internal PV/VG/LV/segment status variables from 32-bit to 64-bit.
+
+Version 2.02.56 - 24th November 2009
+====================================
+ Add missing vg_release to pvs and pvdisplay to fix memory leak.
+ Do not try to unlock VG which is not locked in _process_one_vg.
+ Move is_long_lived persistent_filter_dump to happen after every full scan.
+ Refresh device filters before full device rescan in lvmcache.
+ Return error status if vgchange fails to activate some volume.
+ Fix suspend/resume lock type test causing unbalanced memory locking.
+ Revert vg_read_internal change as clvmd was not ready for vg_read. (2.02.55)
+
+Version 2.02.55 - 19th November 2009
+====================================
+ Fix deadlock when changing mirrors due to unpaired memlock refcount changes.
+ Use separate memlock counter for dmeventd handlers to permit device scanning.
+ Directly restrict vgchange to activating visible LVs.
+ Fix pvmove region_size overflow for very large PVs.
+ Fix lvcreate and lvresize %PVS argument always to use sensible total size.
+ Tidy some uses of arg_count and introduce arg_is_set.
+ Export outnl and indent functions for modules.
+ Flush stdout after yes/no prompt.
+ Update vgsplit and vgcreate to use vg_set_clustered.
+ Add vg_mda_count and vg_set_clustered library functions.
+ Add more vgcreate and vgsplit nightly tests.
+ Insert some missing stack macros into activation code.
+ Recognise DRBD devices and handle them like md devices.
+
+Version 2.02.54 - 26th October 2009
+===================================
+ Update lvcreate/lvconvert man pages to explain PhysicalVolume parameter.
+ Document --all option in man pages, cleanup {pv|vg|lv}{s|display} man pages.
+ Permit snapshots of mirrors.
+ Cleanup mimagetmp LV if allocation fails for new lvconvert mimage.
+ Fix clvmd segfault when refresh_toolcontext fails.
+ Remember to clear 'global lock held during cache refresh' state after use.
+ Use udev flags support in LVM and apply various fixes to udev rules.
+ Delay announcing mirror monitoring to syslog until initialisation succeeded.
+ Handle metadata with unknown segment types more gracefully.
+ Set default owner and group to null.
+ Add dmeventd.static to the build.
+ Disable realtime support code by default.
+ Make clvmd return 0 on success rather than 1.
+ Add --pvmetadatacopies for pvcreate, vgcreate, vgextend, vgconvert.
+ Add implict pvcreate support to vgcreate and vgextend.
+ Correct example.conf to indicate that lvm2 not lvm1 is the default format.
+ Remove an unused stray LVM1_SUPPORT ifdef.
+ Only include selinux libs in libdevmapper.pc when selinux build enabled.
+ Allow for a build directory separate from the source.
+ Update distclean target for rename clogd to cmirrord. (2.02.52)
+ Only do lock conversions in clvmd if we are explicitly asked for one.
+ Introduce percent_range_t and centralise snapshot full/mirror in-sync checks.
+ Factor out poll_mirror_progress and introduce progress_t.
+ Distinguish between powers of 1000 and powers of 1024 in unit suffixes.
+ Restart lvconverts in vgchange by sharing lv_spawn_background_polling.
+ Generalise polldaemon code by changing mirror-specific variable names.
+ Don't attempt to deactivate an LV if any of its snapshots are in use.
+ Return error if lv_deactivate fails to remove device from kernel.
+ Provide alternative implementation of obsolete siginterrupt().
+ Consolidate LV allocation into alloc_lv().
+ Treat input units of both 's' and 'S' as 512-byte sectors. (2.02.49)
+ Use standard output units for 'PE Size' and 'Stripe size' in pv/lvdisplay.
+ Add configure --enable-units-compat to set si_unit_consistency off by default.
+ Add global/si_unit_consistency to enable cleaned-up use of units in output.
+
+Version 2.02.53 - 25th September 2009
+=====================================
+ Create any directories in /dev with DM_DEV_DIR_UMASK (022).
+ Enable dmeventd monitoring section of config file by default.
+ Update lvm2 monitoring script to lvm2_monitoring_init_red_hat.in.
+ Fix lvm2app test to run under test/api subdirectory only when configured.
+ Add vg_is_resizeable() and cleanup reference to VG_RESIZEABLE.
+
+Version 2.02.52 - 15th September 2009
+=====================================
+ Update _process_one_vg to cleanup properly after vg_read_error.
+ Add lots of missing stack debug messages to tools.
+ Make readonly locking available as locking type 4.
+ Fix readonly locking to permit writeable global locks (for vgscan). (2.02.49)
+ Add DM_UDEV_RULES_VSN environment variable to udev rules.
+ Update vgsplit, vgmerge, and vgrename to obey new vgname ordering rules.
+ Make lvm2app pv_t, lv_t, vg_t handle definitions consistent with lvm_t.
+ Enforce an alphabetical lock ordering on vgname locking.
+ Prioritise write locks over read locks by default for file locking.
+ Add local lock files with suffix ':aux' to serialise locking requests.
+ Fix global locking in PV reporting commands (2.02.49).
+ Fix pvcreate string termination in duplicate uuid warning message.
+ Don't loop reading sysfs with pvcreate on a non-blkext partition (2.02.51).
+ Fix vgcfgrestore error paths when locking fails (2.02.49).
+ Update Makefile distclean target.
+ Add libudev configuration check.
+ Make clvmd check corosync to see what cluster interface it should use.
+ Add clvmd autodetection check and cleanup related configure messages.
+ Rewrite clvmd configuration code to cope with all combinations of libs.
+ Added configure --enable-cmirrord to build the cluster mirror log daemon.
+ Rename clogd to cmirrord.
+ Make lvchange --refresh only take a read lock on volume group.
+ Fix race where non-blocking file locks could be granted in error.
+ Fix vgextend error path - if ORPHAN lock fails, unlock / release vg (2.02.49).
+ Fix compile warning in clvmd.
+ Clarify use of PE ranges in lv{convert|create|extend|resize} man pages.
+ Remove useless _pv_write wrapper.
+ Add lvm2app.sh to tests conditional upon configure --enable-applib.
+ Add lvm_vg_is_clustered, lvm_vg_is_exported, and lvm_vg_is_partial.
+ Update lvm_vg_remove to require lvm_vg_write to commit remove to disk.
+ Update test/api/test.c to call lvm_vg_create and lvm_vg_remove.
+
+Version 2.02.51 - 6th August 2009
+=================================
+ Fix locking in clvmd (2.02.50).
+ Add --noudevsync option for relevant LVM tools.
+ Add activation/udev_sync to lvm.conf.
+ Only change LV symlinks on ACTIVATE not PRELOAD.
+ Make lvconvert honour log mirror options combined with downconversion.
+ Allow LV suspend while --ignorelockingfailure is in force.
+ Update synopsis in lvconvert manpage to mention --repair.
+ Set cookies in activation code and wait for udev to complete processing.
+ Added configure --enable-udev_rules --enable-udev_sync.
+ Added configure --with-udev-prefix --with-udevdir.
+ Added udev dir to hold udev rules.
+ Add devices/data_alignment_detection to lvm.conf.
+ Add devices/data_alignment_offset_detection to lvm.conf.
+ Add --dataalignmentoffset to pvcreate to shift start of aligned data area.
+ Fix _mda_setup() to not check first mda's size before pe_align rounding.
+ Document -I option of clvmd in the man page.
+ Fix configure script to handle multiple clvmd selections.
+ Fix lvm2app.pc installation filename.
+ Remove pv_t, vg_t & lv_t handles from lib. Only liblvm uses them.
+ Rename lvm.h to lvm2app.h for now.
+
+Version 2.02.50 - 28th July 2009
+================================
+ Change test/api/test.c prompt so it's not confused with the main lvm prompt.
+ Update liblvm unit tests in test/api to cover latest liblvm changes.
+ Add unimplemented lvm_lv_resize and lvm_pv_resize skeletons to liblvm.
+ Add lvm_library_get_version to liblvm.
+ Add lvm_config_override to liblvm to allow caller to override LVM config.
+ Add lvm_lv_is_active and lvm_lv_is_suspended to liblvm.
+ Add lvm_lv_activate and lvm_lv_deactivate to liblvm.
+ Add lvm_scan, lvm_vg_reduce and lvm_vg_remove_lv to liblvm.
+ Add functions to get numeric properties to liblvm.
+ Add lvm_{pv|vg|lv}_get_{name|uuid} to liblvm.
+ Add lvm_vg_list_pvs and lvm_vg_list_lvs to liblvm.
+ Add lvm_vg_open and lvm_vg_create_lv_linear to liblvm.
+ Add lvm_list_vg_names/uuids to liblvm.
+ Add lvm_errno and lvm_errmsg to liblvm to obtain failure information.
+ Rename lvm_create/destroy to lvm_init/quit.
+ Rename lvm_reload_config to lvm_config_reload.
+ Refactor _override_settings to use new override_config_tree_from_string.
+ Add vg_reduce to metadata.c and metadata-exported.h.
+ Update lvm.h to clarify API behavior and return codes.
+ Update lvm_vg_extend to do an implicit pvcreate on the device.
+ Update display.c to use vg_free(vg) instead of duplicating the calculation.
+ Refactor vg_size, vg_free, and pv_mda_count field calculations for liblvm.
+ Refactor pvcreate and lvcreate for liblvm.
+ Add global/wait_for_locks to lvm.conf so blocking for locks can be disabled.
+ All LV locks are non-blocking so remove LCK_NONBLOCK from separate macros.
+ Fix race condition with vgcreate and vgextend on same device (2.02.49).
+ Remove redundant validate_name call from vgreduce.
+ Remove unused handles lvseg, pvseg inside liblvm/lvm.h.
+ Add liblvm2app Makefile installation targets.
+ Add liblvm pkgconfig file.
+ Use newly-independent LVM_LIBAPI in liblvm soname. E.g. liblvm2app.so.2.1.
+ Add an API version number, LVM_LIBAPI, to the VERSION string for liblvm.
+ Pass a pointer to struct cmd_context to init_multiple_segtypes
+ Return EINVALID_CMD_LINE not success when invalid VG name format is used.
+ Remove unnecessary messages after vgcreate/vgsplit refactor (2.02.49).
+ Add log_errno to set a specific errno and replace log_error in due course.
+ Change create_toolcontext to still return an object if it fails part-way.
+ Add EUNCLASSIFIED (-1) as the default LVM errno code.
+ Store any errno and error messages issued while processing each command.
+ Use log_error macro consistently throughout in place of log_err.
+
+Version 2.02.49 - 15th July 2009
+================================
+ Add readonly locking type to replace implementation of --ignorelockingfailure.
+ Exclude VG_GLOBAL from vg_write_lock_held so scans open devs read-only again.
+ Add unit test case for liblvm VG create/delete APIs.
+ Add liblvm APIs to implement creation and deletion of VGs.
+ Initialize cmd->cmd_line to "liblvm" in new liblvm library.
+ Place handles to liblvm objects for pv, vg, lv, lvseg, pvseg inside lvm.h.
+ Refactor vgsplit and vgextend to remove READ_REQUIRE_RESIZEABLE flag.
+ Use _exit() not exit() after forking to avoid flushing libc buffers twice.
+ Add cast to log_info arg in _find_labeller to avoid Sparc64 warning.
+ Make cmd->cmd_line const.
+ Fix dev name mismatch in vgcreate man page example.
+ Refactor vg_remove_single for use in liblvm.
+ Make all tools use consistent lock ordering obtaining VG_ORPHAN lock second.
+ Check md devices for a partition table during device scan.
+ Add extended device (blkext) and md partition (mdp) types to filters.
+ Make text metadata read errors for segment areas more precise.
+ Fix text segment metadata read errors to mention correct segment name.
+ Include segment and LV names in text segment import error messages.
+ Add parent node to config_node structure.
+ Update vgsplit and vgcreate to call new vg_create and 'set' functions.
+ Change vg_create to take minimal parameters, obtain a lock, and return vg_t.
+ Refactor vgchange extent_size, max_lv, max_pv, and alloc_policy for liblvm.
+ Update t-vgcreate-usage.sh to test for default vg properties.
+ Fix memory leak in vgsplit when re-reading the vg.
+ Make various exit/cleanup paths more robust after lvm init failures.
+ Use LCK_NONBLOCK implicitly instead of explicit vg_read() flag.
+ Remove unnecessary locking and existence tests from new vg_read() interface.
+ Permit several segment types to be registered by a single shared object.
+ Update the man pages to document size units uniformly.
+ Allow commandline sizes to be specified in terms of bytes and sectors.
+ Update 'md_chunk_alignment' to use stripe-width to align PV data area.
+ Update test/t-inconsistent-metadata.sh to match new vg_read interface.
+ Add lvmcache_init() to polldaemon initialization.
+ Convert tools to use new vg_read / vg_read_for_update.
+ Fix segfault in vg_release when vg->cmd is NULL.
+
+Version 2.02.48 - 30th June 2009
+================================
+ Abort if automatic metadata correction fails when reading VG to update it.
+ Explicitly request fallback to default major number in device mapper.
+ Ignore suspended devices during repair.
+ Call vgreduce --removemissing automatically to fix missing PVs in dmeventd.
+ Suggest using lvchange --resync when adding leg to not-yet-synced mirror.
+ Destroy toolcontext on clvmd exit to avoid memory pool leaks.
+ Fix lvconvert not to poll mirror if no conversion in progress.
+ Fix memory leaks in toolcontext error path.
+ Reinstate partial activation support in clustered mode. (2.02.40)
+ Allow metadata correction even when PVs are missing.
+ Use 'lvm lvresize' instead of 'lvresize' in fsadm.
+ Do not use '-n' realine option in fsadm for busybox compatiblity.
+ Add vg_lock_newname() library function for vgrename, vgsplit and vgcreate.
+ Round up requested readahead to at least one page and print warning.
+ Try to repair vg before actual vgremove when force flag provided.
+ Fix possible double release of VG after recovery.
+ Add parameter to process_each_vg specifying what to do with inconsistent VG.
+ Unify error messages when processing inconsistent volume group.
+ Use lvconvert --repair instead of vgreduce in mirror dmeventd DSO.
+ Introduce lvconvert --use_policies (repair policy according to lvm.conf).
+ Update clvmd-corosync to match new corosync API.
+ Fix lib Makefile to include any shared libraries in default target.
+ Fix rename of active snapshot with virtual origin.
+ Fix convert polling to ignore LV with different UUID.
+ Cache underlying device readahead only before activation calls.
+ Fix segfault when calculating readahead on missing device in vgreduce.
+ Remove verbose 'visited' messages.
+ Handle multi-extent mirror log allocation when smallest PV has only 1 extent.
+ Add LSB standard headers and functions (incl. reload) to clvmd initscript.
+ When creating new LV, double-check that name is not already in use.
+ Remove /dev/vgname/lvname symlink automatically if LV is no longer visible.
+ Rename internal vorigin LV to match visible LV.
+ Suppress 'removed' messages displayed when internal LVs are removed.
+ Fix lvchange -a and -p for sparse LVs.
+ Fix lvcreate --virtualsize to activate the new device immediately.
+ Make --snapshot optional with lvcreate --virtualsize.
+ Generalise --virtualoriginsize to --virtualsize.
+ Skip virtual origins in process_each_lv_in_vg() without --all.
+ Fix counting of virtual origin LVs in vg_validate.
+ Attempt to load dm-zero module if zero target needed but not present.
+
+Version 2.02.47 - 22nd May 2009
+===============================
+ Rename liblvm.so to liblvm2app.so and use configure --enable-applib.
+ Reinstate version in liblvm2cmd.so soname. (2.02.44)
+
+Version 2.02.46 - 21st May 2009
+===============================
+ Inherit readahead setting from underlying devices during activation.
+ Detect LVs active on remote nodes by querying locks if supported.
+ Enable online resizing of mirrors.
+ Use suspend with flush when device size was changed during table preload.
+ Implement query_resource_fn for cluster_locking.
+ Support query_resource_fn in locking modules.
+ Introduce CLVMD_CMD_LOCK_QUERY command for clvmd.
+ Fix pvmove to revert operation if temporary mirror creation fails.
+ Fix metadata export for VG with missing PVs.
+ Add vgimportclone and install it and the man page by default.
+ Force max_lv restriction only for newly created LV.
+ Remove unneeded import parameter from lv_create_empty.
+ Merge lv_is_displayable and lv_is_visible functions.
+ Introduce lv_set_visible & lv_set_hidden functions.
+ Fix lv_is_visible to handle virtual origin.
+ Introduce link_lv_to_vg and unlink_lv_from_vg functions.
+ Remove lv_count from VG and use counter function instead.
+ Fix snapshot segment import to not use duplicate segments & replace.
+ Do not query nonexistent devices for readahead.
+ Remove NON_BLOCKING lock flag from tools and set a policy to auto-set.
+ Remove snapshot_count from VG and use function instead.
+ Fix first_seg() call for empty segment list.
+ Add install_lvm2 makefile target to install only the LVM2 components.
+ Reject missing PVs from allocation in toollib.
+ Fix PV datalignment for values starting prior to MDA area. (2.02.45)
+ Add sparse devices: lvcreate -s --virtualoriginsize (hidden zero origin).
+ Fix minimum width of devices column in reports.
+ Add lvs origin_size field.
+ Fix linux configure --enable-debug to exclude -O2.
+ Implement lvconvert --repair for repairing partially-failed mirrors.
+ Fix vgreduce --removemissing failure exit code.
+ Fix remote metadata backup for clvmd.
+ Introduce unlock_and_release_vg macro.
+ Introduce vg_release() to be called to free every struct volume_group.
+ Alloc PV internal structure from VG mempool if possible.
+ Fix metadata backup to run after vg_commit always.
+ Tidy clvmd volume lock cache functions.
+ Fix pvs report for orphan PVs when segment attributes are requested.
+ Fix pvs -a output to not read volume groups from non-PV devices.
+ Add MMC (mmcblk) device type to filters.
+ Introduce memory pools per volume group (to reduce memory for large VGs).
+ Use copy of PV structure when manipulating global PV lists.
+ Always return exit error status when locking of volume group fails.
+ Fix mirror log convert validation question.
+ Avoid referencing files from DESTDIR during build process.
+ Avoid creating some static libraries unless configured --enable-static_link.
+ Enable use of cached metadata for pvs and pvdisplay commands.
+ Add missing 'device-mapper' internal subdir build dependency.
+ Fix memory leak in mirror allocation code.
+ Save and restore the previous logging level when log level is changed.
+ Fix error message when archive initialization fails.
+ Make sure clvmd-corosync releases the lockspace when it exits.
+ Fix segfault for vgcfgrestore on VG with missing PVs.
+ Block SIGTERM & SIGINT in clvmd subthreads.
+ Detect and conditionally wipe swapspace signatures in pvcreate.
+ Fix maximal volume count check for snapshots if max_lv set for volume group.
+ Fix lvcreate to remove unused cow volume if the snapshot creation fails.
+ Fix error messages when PV uuid or pe_start reading fails.
+ Build new liblvm application-level library.
+ Rename liblvm.a to liblvm-internal.a.
+ Flush memory pool and fix locking in clvmd refresh and backup command.
+ Fix unlocks in clvmd-corosync. (2.02.45)
+ Fix error message when adding metadata directory to internal list fails.
+ Fix size and error message of memory allocation at backup initialization.
+ Remove old metadata backup file after renaming VG.
+ Restore log_suppress state when metadata backup file is up-to-date.
+
+Version 2.02.45 - 3rd March 2009
+================================
+ Avoid scanning empty metadata areas for VG names.
+ Attempt proper clean up in child before executing new binary in exec_cmd().
+ Do not scan devices if reporting only attributes from PV label.
+ Use pkgconfig to obtain corosync library details during configuration.
+ Fix error returns in clvmd-corosync interface to DLM.
+ Add --refresh to vgchange and vgmknodes man pages.
+ Pass --test from lvresize to fsadm as --dry-run.
+ Supply argv[] list to exec_cmd() to allow for variable number of parameters.
+ Prevent fsadm from checking mounted filesystems.
+ No longer treats any other key as 'no' when prompting in fsadm.
+ Tidy fsadm command line processing.
+ Add lib/lvm.h and lib/lvm_base.c for the new library interface.
+ Move tools/version.h to lib/misc/lvm-version.h.
+ Split LVM_VERSION into MAJOR, MINOR, PATCHLEVEL, RELEASE and RELEASE_DATE.
+ Add system_dir parameter to create_toolcontext().
+ Add --dataalignment to pvcreate to specify alignment of data area.
+ Exclude LCK_CACHE locks from _vg_lock_count, fixing interrupt unblocking.
+ Provide da and mda locations in debug message when writing text format label.
+ Mention the restriction on file descriptors at invocation on the lvm man page.
+ Index cached vgmetadata by vgid not vgname to cope with duplicate vgnames.
+ No longer require kernel and metadata major numbers to match.
+ Add a fully-functional get_cluster_name() to clvmd corosync interface.
+ Remove duplicate cpg_initialize from clvmd startup.
+ Add option to /etc/sysconfig/cluster to select cluster type for clvmd.
+ Allow clvmd to start up if its lockspace already exists.
+ Separate PV label attributes which do not need parse metadata when reporting.
+ Remove external dependency on the 'cut' command from fsadm.
+ Fix pvs segfault when pv mda attributes requested for not available PV.
+ Add fsadm support for reszing ext4 filesysystems.
+ Move locking_type reading inside init_locking().
+ Rename get_vgs() to get_vgnames() and clarify related error messages.
+ Allow clvmd to be built with all cluster managers & select one on cmdline.
+ Mention --with-clvmd=corosync in ./configure.
+ Replace internal vg_check_status() implementation.
+ Rename vg_read() to vg_read_internal().
+
+Version 2.02.44 - 26th January 2009
+===================================
+ Fix --enable-static_link after the recent repository changes.
+ Add corosync/DLM cluster interface to clvmd.
+ Add --nameprefixes, --unquoted, --rows to pvs, vgs, lvs man pages.
+ Fix lvresize size conversion for fsadm when block size is not 1K.
+ Fix pvs segfault when run with orphan PV and some VG fields.
+ Display a 'dev_size' of zero for missing devices in reports.
+ Add pv_mda_size to pvs and vg_mda_size to vgs.
+ Fix lvmdump /sys listing to include virtual devices directory.
+ Add "--refresh" functionality to vgchange and vgmknodes.
+ Avoid exceeding LV size when wiping device.
+ Calculate mirror log size instead of using 1 extent.
+ Ensure requested device number is available before activating with it.
+ Fix incorrect exit status from 'help <command>'.
+ Fix vgrename using UUID if there are VGs with identical names.
+ Fix segfault when invalid field given in reporting commands.
+ Move is_static from cmd to global is_static().
+ Refactor init_lvm() for lvmcmdline and clvmd.
+ Add liblvm interactive test infrastructure to build.
+ Add skeleton lvm2.h file in preparation for a shared library interface.
+ Use better random seed value in temp file creation.
+ Add read_urandom to read /dev/urandom. Use in uuid calculation.
+ Use displayable_lvs_in_vg and lv_is_displayable for consistency throughout.
+ Fix race in vgcreate that would result in second caller overwriting first.
+ Fix uninitialised lv_count in vgdisplay -c.
+ Don't skip updating pvid hash when lvmcache_info struct got swapped.
+ Add tinfo to termcap search path for pld-linux.
+ Fix startup race in clvmd.
+ Generate Red Hat clvmd startup script at config time with correct paths.
+ Fix clvmd & dmeventd builds after tree restructuring.
+ Cope with snapshot dependencies when removing a whole VG with lvremove.
+ Make man pages and tool help text consistent using | for alternative options.
+
+Version 2.02.43 - 10th November 2008
+====================================
+ Merge device-mapper into the lvm2 tree.
+ Correct prototype for --permission on lvchange and lvcreate man pages.
+ Exit with non-zero status from vgdisplay if couldn't show any requested VG.
+ Move list.c into libdevmapper and rename functions.
+ Rename a couple of variables that matched function names.
+ Use simplified x.y.z version number in libdevmapper.pc.
+ Remove ancient debian directory.
+ Split out lvm-logging.h from log.h and lvm-globals.[ch] from log.[ch].
+
+Version 2.02.42 - 26th October 2008
+===================================
+ Accept locking fallback_to_* options in the global section as documented.
+ Fix temp table activation in mirror conversions not to happen in other cmds.
+ Fix temp table in mirror conversions to use always-present error not zero.
+
+Version 2.02.41 - 17th October 2008
+===================================
+ Use temp table to set device size when converting mirrors.
+ In resume_mirror_images replace activate_lv with resume_lv as workaround.
+ Avoid overwriting in-use on-disk text metadata by forgetting MDA_HEADER_SIZE.
+ Fix snapshot monitoring library to not cancel monitoring invalid snapshot.
+ Generate man pages from templates and include version.
+ Add usrlibdir and usrsbindir to configure.
+ Fix conversion of md chunk size into sectors.
+ Free text metadata buffer after a failure writing it.
+ Fix misleading error message when there are no allocatable extents in VG.
+ Fix handling of PVs which reappeared with old metadata version.
+ Fix mirror DSO to call vgreduce with proper parameters.
+ Fix validation of --minor and --major in lvcreate to require -My always.
+ Fix release: clvmd build, vgreduce consolidate & tests, /dev/ioerror warning.
+
+Version 2.02.40 - 19th September 2008
+=====================================
+ Allow lvremove to remove LVs from VGs with missing PVs.
+ In VG with PVs missing, by default allow activation of LVs that are complete.
+ Track PARTIAL_LV and MISSING_PV flags internally.
+ Require --force with --removemissing in vgreduce to remove partial LVs.
+ No longer write out PARTIAL flag into metadata backups.
+ Treat new default activation/missing_stripe_filler "error" as an error target.
+ Remove internal partial_mode.
+ Add devices/md_chunk_alignment to lvm.conf.
+ Pass struct physical_volume to pe_align and adjust for md chunk size.
+ Store sysfs location in struct cmd_context.
+ Avoid shuffling remaining mirror images when removing one, retaining primary.
+ Add missing LV error target activation in _remove_mirror_images.
+ Prevent resizing an LV while lvconvert is using it.
+ Avoid repeatedly wiping cache while VG_GLOBAL is held in vgscan & pvscan.
+ Fix pvresize to not allow resize if PV has two metadata areas.
+ Fix setting of volume limit count if converting to lvm1 format.
+ Fix vgconvert logical volume id metadata validation.
+ Fix lvmdump metadata gather option (-m) to work correctly.
+ Fix allocation bug in text metadata format write error path.
+ Fix vgcfgbackup to properly check filename if template is used.
+ configure aborts if lcov or genhtml are missing with --enable-profiling
+ vgremove tries to remove lv snapshot first.
+ Added function lv_remove_with_dependencies().
+ Improve file descriptor leak detection to display likely culprit and filename.
+ Change clustered mirror kernel module name from cmirror to dm-log-clustered.
+ Avoid looping forever in _pv_analyze_mda_raw used by pvck.
+ Change lvchange exit status to indicate if any part of the operation failed.
+ Fix pvchange and pvremove to handle PVs without mdas.
+ Refactor _text_pv_read and always return mda list if requested.
+ Fix configure to work w/o readline unless --enable-readline used. (2.02.39)
+ Remove is_lvm_partition template which has not yet been coded.
+ Refactor pvcreate to separate parameter parsing from validation logic.
+ Check for label_write() failure in _text_pv_write().
+ Add pvcreate tests and update vgsplit tests to handle lvm1 and lvm2 metadata.
+ Fix pvchange -M1 -u to preserve existing extent locations when there's a VG.
+ Cease recognising snapshot-in-use percentages returned by early devt kernels.
+ Add backward-compatible flags field to on-disk format_text metadata.
+ Fix dmeventd monitoring libraries to link against liblvm2cmd again. (2.02.39)
+
+Version 2.02.39 - 27th June 2008
+================================
+ Enable readline by default if available.
+ Update autoconf to 2008-01-16.
+ Add $DISTCLEAN_DIRS to make.tmpl.in.
+ Create coverage reports with --enable-profiling and make lcov or lcov-dated.
+ Fix up cache for PVs without mdas after consistent VG metadata is processed.
+ Update validation of safe mirror log type conversions in lvconvert.
+ Fix lvconvert to disallow snapshot and mirror combinations.
+ Fix reporting of LV fields alongside unallocated PV segments.
+ Add --unquoted and --rows to reporting tools.
+ Add and use uninitialized_var() macro to suppress invalid compiler warnings.
+ Introduce enum for md minor sb version to suppress compiler warning.
+ Avoid undefined return value after _memlock manipulation in lvm2_run.
+ Avoid link failure if configured without --enable-cmdlib or --enable-readline.
+ Make clvmd return at once if other nodes down in a gulm or openais cluster.
+ Fix and improve readahead 'auto' calculation for stripe_size.
+ Fix lvchange output for -r auto setting if auto is already set.
+ Add test case for readahead.
+ Avoid ambiguous use of identifier error_message_produced.
+ Begin syncing configure.in for merge/unification with device-mapper.
+ Fix add_mirror_images not to dereference uninitialized log_lv upon failure.
+ Don't call openlog for every debug line output by clvmd.
+ Add --force to lvextend and lvresize.
+ Fix vgchange not to activate component mirror volumes directly.
+ Fix test directory clean up in make distclean.
+
+Version 2.02.38 - 11th June 2008
+================================
+ Fix tracking of validity of PVs with no mdas in lvmcache.
+ Fix return values for reporting commands when run with no PVs, LVs, or VGs.
+ Add omitted unlock_vg() call when sigint_caught() during vg processing.
+ Fix free_count when reading pool metadata.
+ Fix segfault when using pvcreate on a device containing pool metadata.
+ Fix segfault after _free_vginfo by remembering to remove vginfo from list.
+ Tweak detection of invalid fid after changes to PVs in VG in _vg_read.
+ Revert assuming precommitted metadata is live when activating (unnecessary).
+ Drop cached metadata for disappearing VG in vgmerge.
+ In script-processing mode, stop if any command fails.
+ Warn if command exits with non-zero status code without a prior log_error.
+ Check lv_count in vg_validate.
+ Add --nameprefixes to reporting tools for field name prefix output format.
+
+Version 2.02.37 - 6th June 2008
+===============================
+ Make clvmd-cman use a hash rather than an array for node updown info.
+ Correct config file line numbers in messages when parsing comments.
+ Drop cached metadata when renaming a VG.
+ Allow for vginfo changing during _vg_read.
+ Decode numbers in clvmd debugging output.
+ Add missing deactivation after activation failure in lvcreate -Zy.
+ When activating, if precommitted metadata is still cached, assume it's live.
+ When removing LV symlinks, skip any where the VG name is not determined.
+ Drop metadata cache if update fails in vg_revert or vg_commit.
+ Avoid spurious duplicate VG messages referring to VGs that are gone.
+ Drop dev_name_confirmed error message to debug level.
+ Fix setpriority error message to signed int.
+ Temporarily disable dmeventd mirror monitoring during lvchange --resync.
+ Refactor some vginfo manipulation code.
+ Add assertions to trap deprecated P_ and V_ lock usage.
+ Add missing mutex around clvmd lvmcache_drop_metadata library call.
+ Fix uninitialised mutex in clvmd if all daemons are not running at startup.
+ Avoid using DLM locks with LCK_CACHE type P_ lock requests.
+ When asked to drop cached committed VG metadata, invalidate cached PV labels.
+ Drop metadata cache before writing precommitted metadata instead of after.
+ Don't touch /dev in vgrename if activation is disabled.
+
+Version 2.02.36 - 29th April 2008
+=================================
+ Fix fsadm.sh to work with older blockdev, blkid & readlink binaries.
+ Fix lvresize to pass new size to fsadm when extending device.
+ Remove unused struct in clvmd-openais, and use correct node count.
+ Fix nodes list in clvmd-openais, and allow for broadcast messages.
+ Exclude VG_GLOBAL from internal concurrent VG lock counter.
+ Fix vgsplit internal counting of snapshot LVs.
+ Fix vgmerge snapshot_count when source VG contains snapshots.
+ Simplify clvmd-openais by using non-async saLckResourceLock.
+ Fix internal LV counter when a snapshot is removed.
+ Fix metadata corruption writing lvm1-formatted metadata with snapshots.
+ Fix lvconvert -m0 allocatable space check.
+
+Version 2.02.35 - 15th April 2008
+=================================
+ Drop cached VG metadata before and after committing changes to it.
+ Rename P_global to P_#global.
+ Don't attempt remote metadata backups of non-clustered VGs. (2.02.29)
+ Don't store fid in VG metadata cache to avoid clvmd segfault. (2.02.34)
+ Update vgsplit tests to verify loosening of active LV restriction.
+ Update vgsplit to only restrict split with active LVs involved in split.
+ Add lv_is_active() to determine whether an lv is active.
+
+Version 2.02.34 - 10th April 2008
+=================================
+ Improve preferred_names lvm.conf example.
+ Fix vgdisplay 'Cur LV' field to match lvdisplay output.
+ Fix lv_count report field to exclude hidden LVs.
+ Add vg_is_clustered() helper function.
+ Fix vgsplit to only move hidden 'snapshotN' LVs when necessary.
+ Update vgsplit tests for lvnames on the cmdline.
+ Update vgsplit man page to reflect lvnames on the cmdline.
+ Update vgsplit to take "-n LogicalVolumeName" on the cmdline.
+ Use clustered mirror log with pvmove in clustered VGs, if available.
+ Fix some pvmove error status codes.
+ Fix vgsplit error paths to release vg_to lock.
+ Indicate whether or not VG is clustered in vgcreate log message.
+ Mention default --clustered setting in vgcreate man page.
+ Add config file overrides to clvmd when it reads the active LVs list.
+ Fix vgreduce to use vg_split_mdas to check sufficient mdas remain.
+ Add (empty) orphan VGs to lvmcache during initialisation.
+ Fix orphan VG name used for format_pool.
+ Create a fid for internal orphan VGs.
+ Update lvmcache VG lock state for all locking types now.
+ Fix output if overriding command_names on cmdline.
+ Add detection of clustered mirror log capability.
+ Add check to vg_commit() ensuring VG lock held before writing new VG metadata.
+ Add validation of LV name to pvmove -n.
+ Make clvmd refresh the context correctly when lvm.conf is updated.
+ Add some basic internal VG lock validation.
+ Add per-command flags to control which commands use the VG metadata cache.
+ Fix vgsplit locking of new VG (2.02.30).
+ Avoid erroneous vgsplit error message for new VG. (2.02.29)
+ Suppress duplicate message when lvresize fails because of invalid vgname.
+ Cache VG metadata internally while VG lock is held.
+ Fix redundant lvresize message if vg doesn't exist.
+ Fix another allocation bug with clvmd and large node IDs.
+ Add find_lv_in_lv_list() and find_pv_in_pv_list().
+ Fix uninitialised variable in clvmd that could cause odd hangs.
+ Add vgmerge tests.
+ Add pvseg_is_allocated() for identifying a PV segment allocated to a LV.
+ Add list_move() for moving elements from one list to another.
+ Add 'is_reserved_lvname()' for identifying hidden LVs.
+ Correct command name in lvmdiskscan man page.
+ clvmd no longer crashes if it sees nodeids over 50.
+ Fix potential deadlock in clvmd thread handling.
+ Refactor text format initialisation into _init_text_import.
+ Escape double quotes and backslashes in external metadata and config data.
+ Add functions for escaping double quotes in strings.
+ Rename count_chars_len to count_chars.
+ Use return_0 in a couple more places.
+ Correct a function name typo in _line_append error message.
+ Include limits.h in clvmd so it compiles with newer headers.
+ Add VirtIO disks (virtblk) to filters.
+ Fix resetting of MIRROR_IMAGE and VISIBLE_LV after removal of LV. (2.02.30)
+ Fix remove_layer_from_lv to empty the LV before removing it. (2.02.30)
+ Add missing no-longer-used segs_using_this_lv test to check_lv_segments.
+ Remove redundant non-NULL tests before calling free in clvmd.c.
+ Avoid a compiler warning: make is_orphan's parameter const.
+ Fix lvconvert detection of mirror conversion in progress. (2.02.30)
+ Avoid automatic lvconvert polldaemon invocation when -R specified. (2.02.30)
+ Fix 'pvs -a' to detect VGs of PVs without metadata areas.
+ Divide up internal orphan volume group by format type.
+ Update usage message for clvmd.
+ Fix clvmd man page not to print <br> and clarified debug options.
+ Fix lvresize to support /dev/mapper prefix in the LV name.
+ Fix unfilled parameter passed to fsadm from lvresize.
+ Update fsadm to call lvresize if the partition size differs (with option -l).
+ Fix fsadm to support VG/LV names.
+
+Version 2.02.33 - 31st January 2008
+===================================
+ Fix mirror log name construction during lvconvert. (2.02.30)
+ Make monitor_dev_for_events recurse through the stack of LVs.
+ Clean up some more compiler warnings.
+ Some whitespace tidy-ups.
+ Use stack return macros throughout.
+ Rely upon internally-cached PV labels while corresponding VG lock is held.
+
+Version 2.02.32 - 29th January 2008
+===================================
+ Fix two check_lv_segments error messages to show whole segment.
+ Refactor mirror log attachment code.
+ Fix internal metadata corruption in lvchange --resync. (2.02.30)
+ Fix new parameter validation in vgsplit and test mode. (2.02.30)
+ Remove redundant cnxman-socket.h file from clvmd directory.
+ Fix pvs, vgs, lvs error exit status on some error paths.
+
+Version 2.02.31 - 19th January 2008
+===================================
+ Fix lvcreate --nosync not to wait for non-happening sync. (2.02.30)
+ Add very_verbose lvconvert messages.
+ Avoid readahead error message with default setting of lvcreate -M1. (2.02.29)
+
+Version 2.02.30 - 17th January 2008
+===================================
+ Set default readahead to twice maximium stripe size.
+ Reinstate VG extent size and stripe size defaults (halved). (2.02.29)
+ Add lists of stacked LV segments using each LV to the internal metadata.
+ Change vgsplit -l (for unimplemented --list) into --maxlogicalvolumes.
+ Fix process_all_pvs to detect non-orphans with no MDAs correctly.
+ Don't use block_on_error with mirror targets version 1.12 and above.
+ Update vgsplit to accept vgcreate options when new VG is destination.
+ Update vgsplit to accept existing VG as destination.
+ lvconvert waits for completion of initial sync by default.
+ Refactor vgcreate for parameter validation and add tests.
+ Add new convert_lv field to lvs output.
+ Print warning when lvm tools are running as non-root.
+ Add snapshot dmeventd library (enables dmeventd snapshot monitoring).
+ Prevent pvcreate from overwriting MDA-less PVs belonging to active VGs.
+ Fix a segfault if using pvs with --all argument. (2.02.29)
+ Update --uuid argument description in man pages.
+ Fix vgreduce PV list processing not to process every PV in the VG. (2.02.29)
+ Extend lvconvert to use polldaemon.
+ Add support for stacked mirrors.
+ Major restructuring of pvmove and lvconvert layer manipulation code.
+ Replace tools/fsadm with scripts/fsadm.sh.
+ Append fields to report/pvsegs_cols_verbose.
+ Permit LV segment fields with PV segment reports.
+ Add seg_start_pe and seg_pe_ranges to reports.
+
+Version 2.02.29 - 5th December 2007
+===================================
+ Make clvmd backup vg metadata on remote nodes.
+ Refactor pvmove allocation code.
+ Decode cluster locking state in log message.
+ Change file locking state messages from debug to very verbose.
+ Fix --addtag to drop @ prefix from name.
+ Stop clvmd going haywire if a pre_function fails.
+ Convert some vg_reads into vg_lock_and_reads.
+ Avoid nested vg_reads when processing PVs in VGs and fix associated locking.
+ Accept sizes with --readahead argument.
+ Store size arguments as sectors internally.
+ Attempt to remove incomplete LVs with lvcreate zeroing/activation problems.
+ Add read_ahead activation code.
+ Add activation/readahead configuration option and FMT_RESTRICTED_READAHEAD.
+ Extend readahead arg to accept "auto" and "none".
+ Add lv_read_ahead and lv_kernel_read_ahead fields to reports and lvdisplay.
+ Prevent lvconvert -s from using same LV as origin and snapshot.
+ Fix human-readable output of odd numbers of sectors.
+ Add pv_mda_free and vg_mda_free fields to reports for raw text format.
+ Add LVM2 version to 'Generated by' comment in metadata.
+ Show 'not usable' space when PV is too large for device in pvdisplay.
+ Ignore and fix up any excessive device size found in metadata.
+ Fix error message when fixing up PV size in lvm2 metadata (2.02.11).
+ Fix orphan-related locking in pvdisplay and pvs.
+ Fix missing VG unlocks in some pvchange error paths.
+ Add some missing validation of VG names.
+ Rename validate_vg_name() to validate_new_vg_name().
+ Change orphan lock to VG_ORPHANS.
+ Change format1 to use ORPHAN as orphan VG name.
+ Convert pvchange, pvdisplay, pvscan to use is_orphan()
+ Add is_orphan_vg() and change all hard-coded checks to use it.
+ Detect md superblocks version 1.0, 1.1 and 1.2.
+ Add _alloc_pv() and _free_pv() from _pv_create() code and fix error paths.
+ Add pv_dev_name() to access PV device name.
+ Add const attributes to pv accessor functions.
+ Refactor vg_add_snapshot() and lv_create_empty().
+ Handle new sysfs subsystem/block/devices directory structure.
+ Run test with LVM_SYSTEM_DIR pointing to private root and /dev dirs.
+ Fix a bug in lvm_dump.sh checks for lvm/dmsetup binaries.
+ Fix underquotations in lvm_dump.sh.
+ Refactor lvcreate stripe and mirror parameter validation.
+ Print --help output to stdout, not stderr.
+ After a cmdline processing error, don't print help text but suggest --help.
+ Add %PVS extents option to lvresize, lvextend, and lvcreate.
+ Add 'make check' to run tests in new subdirectory 'test'.
+ Moved the obsolete test subdirectory to old-tests.
+ Cope with relative paths in configure --with-dmdir.
+ Remove no-longer-correct restrictions on PV arg count with stripes/mirrors.
+ Fix strdup memory leak in str_list_dup().
+ Link with -lpthread when static SELinux libraries require that.
+ Detect command line PE values that exceed their 32-bit range.
+ Include strerror string in dev_open_flags' stat failure message.
+ Move guts of pvresize into library.
+ Avoid error when --corelog is provided without --mirrorlog. (2.02.28)
+ Correct --mirrorlog argument name in man pages (not --log).
+ Clear MIRROR_NOTSYNCED LV flag when converting from mirror to linear.
+ Modify lvremove to prompt for removal if LV active on other cluster nodes.
+ Add '-f' to vgremove to force removal of VG even if LVs exist.
+
+Version 2.02.28 - 24th August 2007
+==================================
+ Fix clvmd logging so you can get lvm-level debugging out of it.
+ Introduce VG_GLOBAL lock type for vgscan/pvscan to trigger clvmd -R.
+ Change locking_flags from int to uint32_t.
+ Fix clvmd -R, so it fully refreshes the caches.
+ Change lvconvert_mirrors to use mirror segtype not striped.
+ Fix lvconvert_mirrors detection of number of existing mirrors.
+ Clean up numerous compiler warnings that appeared in recent releases.
+ Remove several unused parameters from _allocate().
+ Only permit --force, --verbose and --debug arguments to be repeated.
+ Fix inconsistent licence notices: executables are GPLv2; libraries LGPLv2.1.
+ Move guts of vgremove and lvremove into library, including yes_no_prompt.
+ Allow clvmd debug to be turned on in a running daemon using clvmd -d [-C].
+ Update to use autoconf 2.61, while still supporting 2.57.
+ Add more cluster info to lvmdump.
+ Add further const attributes throughout.
+ Add support for renaming mirrored LVs.
+ Factor out core of lvrename() to library function.
+ Add --mirrorlog argument to specify log type for mirrors.
+ Don't attempt to monitor devices if their creation failed in _lv_activate.
+ Don't leak a file descriptor in fcntl_lock_file() when fcntl fails.
+ Replace create_dir with dm_create_dir.
+ Detect stream write failure reliably with lvm_fclose using dm_fclose.
+ Fix clvmd if compiled with gulm support. (2.02.26)
+ Fix lvdisplay man page to say LV size is reported in sectors, not KB.
+ Add vg_lock_and_read() external library function.
+ Fix loading of persistent cache if cache_dir is used. (2.02.23)
+ Reduce _compare_paths lstat error message from log_error to log_very_verbose.
+ Create util.h with last_path_component replacing strdup + basename.
+ Use gcc's printf attribute wherever possible.
+ In _line_append, use "sizeof buf - 1" rather than equivalent "4095".
+ Introduce is_same_inode macro, now including a comparison of st_dev.
+ Don't leak a file descriptor in _lock_file() when flock fails.
+ Add SUN's LDOM virtual block device (vdisk) and ps3disk to filters.
+ Split metadata-external.h out from metadata.h for the tools to use.
+
+Version 2.02.27 - 17th July 2007
+================================
+ Fix snapshot cow area deactivation if origin is not active. (2.02.13)
+ Fix configure libdevmapper.h check when --with-dmdir is used.
+ Turn _add_pv_to_vg() into external library function add_pv_to_vg().
+ Add pv_by_path() external library function.
+ Tidy clvmd-openais of redundant bits, and improve an error report.
+ Cope with find_seg_by_le() failure in check_lv_segments().
+ Call dev_iter_destroy() if _process_all_devs() is interrupted by sigint.
+ Add vg_mda_count and pv_mda_count columns to reports.
+ Fix dumpconfig to use log_print instead of stdout directly.
+ Remove unused parameter 'fid' from _add_pv_to_vg.
+ Add kernel and device-mapper targets versions to lvmdump.
+ Replace BSD (r)index with C89 str(r)chr.
+ Handle vgsplit of an entire VG as a vgrename.
+ Reinitialise internal lvmdiskscan variables when called repeatedly.
+ Fix missing lvm_shell symbol in lvm2cmd library. (2.02.23)
+ Add vg_status function and clean up vg->status in tools directory.
+ Add --ignoremonitoring to disable all dmeventd interaction.
+ Remove get_ prefix from get_pv_* functions.
+ clvmd-openais now uses cpg_local_get() to get nodeid, rather than Clm.
+ Print warnings to stderr instead of stdout.
+
+Version 2.02.26 - 15th June 2007
+================================
+ Update vgcfgrestore man page.
+ Allow keyboard interrupt during user prompts when appropriate.
+ Remove unused clvmd system-lv code.
+ Replace many physical_volume struct dereferences with new get_pv_* functions.
+ Suppress a benign compile-time warning.
+ Convert find_pv_in_vg_by_uuid and pv_create to use PV handles.
+ Add wrappers to some functions in preparation for external LVM library.
+ Add -f to vgcfgrestore to list metadata backup files.
+ Add vg_check_status to consolidate vg status checks and error messages.
+ Add pvdisplay --maps implementation.
+ Remove unsupported LVM1 options from vgcfgrestore man page.
+ Update vgcfgrestore man page to show mandatory VG name.
+ Update vgrename man page to include UUID and be consistent with lvrename.
+ Add (experimental) OpenAIS support to clvmd.
+ Fix deactivation code to follow dependencies and remove symlinks.
+ Fix and clarify vgsplit error messages.
+ Fix a segfault in device_is_usable() if a device has no table.
+ Add some more debug messages to clvmd startup.
+ Misc clvmd cleanups.
+
+Version 2.02.25 - 27th April 2007
+=================================
+ Fix get_config_uint64() to read a 64-bit value not a 32-bit one.
+ Add -Wformat-security and change one fprintf() to fputs().
+ Move regex functions into libdevmapper.
+ Change some #include lines to search only standard system directories.
+ Add devices/preferred_names config regex list for displayed device names.
+ Free a temporary dir string in fcntl_lock_file() after use.
+ Fix a dm_pool_destroy() in matcher_create().
+ Introduce goto_bad macro.
+ Fix warnings on x86_64 involving ptrdiff_t in log_error messages.
+ Update pvck to include text metadata area and record detection.
+ Add support functions for token counting in config file extracts.
+ Update pvck to read labels on disk, with --labelsector parameter.
+ Add count_chars and count_chars_len functions.
+ Add /sys/block listings to lvm_dump.sh.
+ Make lvm_dump.sh list /dev recursively.
+ Fix thread race in clvmd.
+ Add scan_sector param to label_read and _find_labeller.
+ Make clvmd cope with quorum devices.
+ Add extra internal error checking to clvmd.
+ Add dev_read_circular.
+ Add pvck command stub.
+ Update lists of attribute characters in man pages.
+ Change cling alloc policy attribute character from 'C' to l'.
+ Fix creation and conversion of mirrors with tags.
+ Fix vgsplit for lvm1 format (set and validate VG name in PVs metadata).
+ Split metadata areas in vgsplit properly.
+
+Version 2.02.24 - 19th March 2007
+=================================
+ Fix processing of exit status in init scripts
+ Fix vgremove to require at least one vg argument.
+ Fix reading of striped LVs in LVM1 format.
+ Flag nolocking as clustered so clvmd startup sees clustered LVs. (2.02.10)
+ Add a few missing pieces of vgname command line validation.
+ Support the /dev/mapper prefix on most command lines.
+
+Version 2.02.23 - 8th March 2007
+================================
+ Fix vgrename active LV check to ignore differing vgids.
+ Remove no-longer-used uuid_out parameter from activation info functions.
+ Fix two more segfaults if an empty config file section encountered.
+ Move .cache file into a new /etc/lvm/cache directory by default.
+ Add devices/cache_dir & devices/cache_file_prefix, deprecating devices/cache.
+ Create directory in fcntl_lock_file() if required.
+ Exclude readline support from lvm.static.
+ Fix a leak in a reporting error path (2.02.19).
+
+Version 2.02.22 - 13th February 2007
+====================================
+ Correct -b and -P on a couple of man pages.
+ Add global/units to example.conf.
+ Fix loading of segment_libraries.
+ If a PV reappears after it was removed from its VG, make it an orphan.
+ Don't update metadata automatically if VGIDs don't match.
+ Fix some vgreduce --removemissing command line validation.
+
+Version 2.02.21 - 30th January 2007
+===================================
+ Add warning to lvm2_monitoring_init_rhel4 if attempting to stop monitoring.
+ Fix vgsplit to handle mirrors.
+ Reorder fields in reporting field definitions.
+ Fix vgs to treat args as VGs even when PV fields are displayed.
+ Fix md signature check to handle both endiannesses.
+
+Version 2.02.20 - 25th January 2007
+===================================
+ dmeventd mirror sets ignore_suspended_devices and avoids scanning mirrors.
+ Add devices/ignore_suspended_devices to ignore suspended dm devices.
+ Add some missing close() and fclose() return code checks.
+ Fix exit statuses of reporting tools (2.02.19).
+ Add init script for dmeventd monitoring.
+ lvm.static no longer interacts with dmeventd unless explicitly asked to.
+ Add field definitions to report help text.
+ Remove unnecessary cmd arg from target_*monitor_events().
+ Add private variable to dmeventd shared library interface.
+ Long-lived processes write out persistent dev cache in refresh_toolcontext().
+ Fix refresh_toolcontext() always to wipe persistent device filter cache.
+ Add is_long_lived to toolcontext.
+ Add --clustered to man pages.
+ Streamline dm_report_field_* interface.
+ Change remaining dmeventd terminology 'register' to 'monitor'.
+ Update reporting man pages.
+ No longer necessary to specify alignment type for report fields.
+
+Version 2.02.19 - 17th January 2007
+===================================
+ Fix a segfault if an empty config file section encountered.
+ Move basic reporting functions into libdevmapper.
+ Fix partition table processing after sparc changes (2.02.16).
+ Fix cmdline PE range processing segfault (2.02.13).
+ Some libdevmapper-event interface changes.
+ Report dmeventd mirror monitoring status.
+ Fix dmeventd mirror status line processing.
+
+Version 2.02.18 - 11th January 2007
+===================================
+ Revised libdevmapper-event interface for dmeventd.
+ Remove dmeventd mirror status line word limit.
+ Use CFLAGS when linking so mixed sparc builds can supply -m64.
+ Prevent permission changes on active mirrors.
+ Print warning instead of error message if lvconvert cannot zero volume.
+ Add snapshot options to lvconvert man page.
+ dumpconfig accepts a list of configuration variables to display.
+ Change dumpconfig to use --file to redirect output to a file.
+ Avoid vgreduce error when mirror code removes the log LV.
+ Remove 3 redundant AC_MSG_RESULTs from configure.in.
+ Free memory in _raw_read_mda_header() error paths.
+ Fix ambiguous vgsplit error message for split LV.
+ Fix lvextend man page typo.
+ Add configure --with-dmdir to compile against a device-mapper source tree.
+ Use no flush suspending for mirrors.
+ Add dmeventd_mirror register_mutex, tidy initialisation & add memlock.
+ Fix create mirror with name longer than 22 chars.
+ Fix some activate.c prototypes when compiled without devmapper.
+ Fix dmeventd mirror to cope if monitored device disappears.
+
+Version 2.02.17 - 14th December 2006
+====================================
+ Add missing pvremove error message when device doesn't exist.
+ When lvconvert allocates a mirror log, respect parallel area constraints.
+ Use loop to iterate through the now-ordered policy list in _allocate().
+ Check for failure to allocate just the mirror log.
+ Introduce calc_area_multiple().
+ Support mirror log allocation when there is only one PV: area_count now 0.
+ Fix detection of smallest area in _alloc_parallel_area() for cling policy.
+ Add manpage entry for clvmd -T
+ Fix gulm operation of clvmd, including a hang when doing lvchange -aey
+ Fix hang in clvmd if a pre-command failed.
+
+Version 2.02.16 - 1st December 2006
+===================================
+ Fix VG clustered read locks to use PR not CR.
+ Adjust some alignments for ia64/sparc.
+ Fix mirror segment removal to use temporary error segment.
+ Always compile debug logging into clvmd.
+ Add startup timeout to RHEL4 clvmd startup script.
+ Add -T (startup timeout) switch to clvmd.
+ Improve lvm_dump.sh robustness.
+ Update lvm2create_initrd to support gentoo.
+
+Version 2.02.15 - 21st November 2006
+====================================
+ Fix clvmd_init_rhel4 line truncation (2.02.14).
+ Install lvmdump by default.
+ Fix check for snapshot module when activating snapshot.
+ Fix pvremove error path for case when PV is in use.
+ Warn if certain duplicate config file entries are seen.
+ Enhance lvm_dump.sh for sysreport integration and add man page.
+ Fix --autobackup argument which could never disable backups.
+ Fix a label_verify error path.
+
+Version 2.02.14 - 10th November 2006
+====================================
+ Fix adjusted_mirror_region_size() to handle 64-bit size.
+ Add some missing bounds checks on 32-bit extent counters.
+ Add Petabyte and Exabyte support.
+ Fix lvcreate error message when 0 extents requested.
+ lvremove man page: volumes must be cluster inactive before being removed.
+ Protect .cache manipulations with fcntl locking.
+ Change .cache timestamp comparisons to use ctime.
+ Fix mirror log LV writing to set all bits in whole LV.
+ Fix clustered VG detection and default runlevels in clvmd_init_rhel4.
+ Fix high-level free space check for partial allocations.
+
+Version 2.02.13 - 27th October 2006
+===================================
+ Add couple of missing files to tools/Makefile CLEAN_TARGETS.
+ When adding snapshot leave cow LV mapped device active after zeroing.
+ Fix a clvmd debug message.
+ Add dev_flush() to set_lv().
+ Add lvchange --resync.
+ Perform high-level free space check before each allocation attempt.
+ Don't allow a node to remove an LV that's exclusively active on anther node.
+ Cope if same PV is included more than once in cmdline PE range list.
+ Set PV size to current device size if it is found to be zero.
+ Add segment parameter to target_present functions.
+
+Version 2.02.12 - 16th October 2006
+===================================
+ Fix pvdisplay to use vg_read() for non-orphans.
+ Fall back to internal locking if external locking lib is missing or fails.
+ Retain activation state after changing LV minor number with --force.
+ Propagate clustered flag in vgsplit and require resizeable flag.
+
+Version 2.02.11 - 12th October 2006
+===================================
+ Add clvmd function to return the cluster name. not used by LVM yet.
+ Add cling allocation policy.
+ Change _check_contiguous() to use _for_each_pv().
+ Extend _for_each_pv() to allow termination without error.
+ Abstract _is_contiguous().
+ Remove duplicated pv arg from _check_contiguous().
+ Accept regionsize with lvconvert.
+ Add report columns with underscore before field names ending 'size'.
+ Correct regionsize default on lvcreate man page (MB).
+ Fix clvmd bug that could cause it to die when a node with a long name crashed.
+ Add device size to text metadata.
+ Fix format_text mda_setup pv->size and pv_setup pe_count calculations.
+ Fix _for_each_pv() for mirror with core log.
+ Add lvm_dump.sh script to create a tarball of debugging info from a system.
+ Capture error messages in clvmd and pass them back to the user.
+ Remove unused #defines from filter-md.c.
+ Make clvmd restart init script wait until clvmd has died before starting it.
+ Add -R to clvmd which tells running clvmds to reload their device cache.
+ Add LV column to reports listing kernel modules needed for activation.
+ Show available fields if report given invalid field. (e.g. lvs -o list)
+ Add timestamp functions with --disable-realtime configure option.
+ Add %VG, %LV and %FREE suffices to lvcreate/lvresize --extents arg.
+ Fix two potential NULL pointer derefs in error cases in vg_read().
+ Separate --enable-cluster from locking lib options in lvmconf.sh.
+ Add a missing comma in lvcreate man page.
+
+Version 2.02.10 - 19th September 2006
+=====================================
+ Fix lvconvert mirror change case detection logic.
+ Fix mirror log detachment so it correctly becomes a standalone LV.
+ Extend _check_contiguous() to detect single-area LVs.
+ Include mirror log (untested) in _for_each_pv() processing.
+ Use MIRROR_LOG_SIZE constant.
+ Remove struct seg_pvs from _for_each_pv() to generalise.
+ Avoid adding duplicates to list of parallel PVs to avoid.
+ Fix several incorrect comparisons in parallel area avoidance code.
+ Fix segment lengths when flattening existing parallel areas.
+ Log existing parallel areas prior to allocation.
+ Fix mirror log creation when activation disabled.
+ Don't attempt automatic recovery without proper locking.
+ When using local file locking, skip clustered VGs.
+ Add fallback_to_clustered_locking and fallback_to_local_locking parameters.
+ lvm.static uses built-in cluster locking instead of external locking.
+ Don't attempt to load shared libraries if built statically.
+ Change default locking_lib to liblvm2clusterlock.so.
+ Add skip_dev_dir() to process command line VGs.
+ Stop clvmd complaining about nodes that have left the cluster.
+ Move lvm_snprintf(), split_words() and split_dm_name() into libdevmapper.
+ Add lvconvert man page.
+ Add mirror options to man pages.
+ Prevent mirror renames.
+ Move CMDLIB code into separate file and record whether static build.
+
+Version 2.02.09 - 17th August 2006
+==================================
+ Fix PE_ALIGN for pagesize over 32KB.
+ Separate out LVM1_PE_ALIGN and pe_align().
+ Add lvm_getpagesize wrapper.
+ Add --maxphysicalvolumes to vgchange.
+
+Version 2.02.08 - 15th August 2006
+==================================
+ Add checks for duplicate LV name, lvid and PV id before writing metadata.
+ Report all sanity check failures, not just the first.
+ Fix missing lockfs on first snapshot creation.
+ Add unreliable --trustcache option to reporting commands.
+ Fix locking for mimage removal.
+ Fix clvmd_init_rhel4 'status' exit code.
+
+Version 2.02.07 - 17th July 2006
+================================
+ Fix activation logic in lvchange --persistent.
+ Don't ignore persistent minor numbers when activating.
+ Use RTLD_GLOBAL when loading shared libraries.
+ Add some forgotten memlock checks to _vg_read to protect against full scans.
+ Add mutex to dmeventd_mirror to avoid concurrent execution.
+ Fix vgreduce --removemissing to return success if VG is already consistent.
+ Fix return code if VG specified on command line is not found.
+ Fix PV tools to include orphaned PVs in default output again.
+ Fixed unaligned access when using clvm.
+ Fix an extra dev_close in a label_read error path.
+ Append patches to commit emails.
+ Fix target_register_events args.
+ Prevent snapshots of mirrors.
+ Add DISTCLEAN_TARGETS to make template for configure.h.
+ More fixes to error paths.
+ Fix lvcreate corelog validation.
+ Add --config for overriding most config file settings from cmdline.
+ Quote arguments when printing command line.
+ Remove linefeed from 'initialising logging' message.
+ Add 'Completed' debug message.
+ Don't attempt library exit after reloading config files.
+ Always compile with libdevmapper, even if device-mapper is disabled.
+
+Version 2.02.06 - 12th May 2006
+===============================
+ Propagate --monitor around cluster.
+ Add --monitor to vgcreate and lvcreate to control dmeventd registration.
+ Filter LCK_NONBLOCK in clvmd lock_vg.
+ Add --nosync to lvcreate with LV flag NOTSYNCED.
+ Use mirror's uuid for a core log.
+ Add mirror log fault-handling policy.
+ Improve mirror warning messages and tidy dmeventd syslog output.
+ Propagate nosync flag around cluster.
+ Allow vgreduce to handle mirror log failures.
+ Add --corelog to lvcreate and lvconvert.
+ Create a log header for replacement in-sync mirror log.
+ Use set_lv() and dev_set() to wipe sections of devices.
+ Add mirror_in_sync() flag to avoid unnecessary resync on activation.
+ Add mirror_library description to example.conf.
+ Fix uuid_from_num() buffer overrun.
+ Make SIZE_SHORT the default for display_size().
+ Fix some memory leaks in error paths found by coverity.
+ Use C99 struct initialisers.
+ Move DEFS into configure.h.
+ Clean-ups to remove miscellaneous compiler warnings.
+ Improve stripe size validation.
+ Increase maximum stripe size limit to physical extent size for lvm2 metadata.
+ Fix activation code to check for pre-existing mirror logs.
+ Tighten region size validation.
+ Ignore empty strings in config files.
+ Require non-zero regionsize and document parameter on lvcreate man page.
+ Invalidate cache if composition of VG changed externally.
+
+Version 2.02.05 - 21st April 2006
+=================================
+ Fix vgid string termination in recent cache code.
+
+Version 2.02.04 - 19th April 2006
+=================================
+ Check for libsepol.
+ Add some cflow & scope support.
+ Separate out DEFS from CFLAGS.
+ Remove inlines and use unique function names.
+
+Version 2.02.03 - 14th April 2006
+=================================
+ vgrename accepts vgid and exported VG.
+ Add --partial to pvs.
+ When choosing between identically-named VGs, also consider creation_host.
+ Provide total log suppression with 2.
+ Fix vgexport/vgimport to set/reset PV exported flag so pv_attr is correct.
+ Add vgid to struct physical_volume and pass with vg_name to some functions.
+ If two or more VGs are found with the same name, use one that is not exported.
+ Whenever vgname is captured, also capture vgid and whether exported.
+ Remove an incorrect unlock_vg() from process_each_lv().
+ Update extent size information in vgchange and vgcreate man pages.
+ Introduce origin_from_cow() and lv_is_visible().
+ pvremove without -f now fails if there's no PV label.
+ Support lvconvert -s.
+ Suppress locking library load failure message if --ignorelockingfailure.
+ Propagate partial mode around cluster.
+ Fix archive file expiration.
+ Fix dmeventd build.
+ clvmd now uses libcman rather than cman ioctls.
+ clvmd will allow new cman to shutdown on request.
+
+Version 2.02.02 - 7th February 2006
+===================================
+ Add %.so: %.a make template rule.
+ Switchover library building to use LIB_SUFFIX.
+ Only do lockfs filesystem sync when suspending snapshots.
+ Always print warning if activation is disabled.
+ vgreduce removes mirror images.
+ Add --mirrorsonly to vgreduce.
+ vgreduce replaces active LVs with error segment before removing them.
+ Set block_on_error parameter if available.
+ Add target_version.
+ Add details to format1 'Invalid LV in extent map' error message.
+ Fix lvscan snapshot full display.
+ Bring lvdisplay man page example into line.
+ Add mirror dmeventd library.
+ Add some activation logic to remove_mirror_images().
+ lvconvert can remove specified PVs from a mirror.
+ lvconvert turns an existing LV into a mirror.
+ Allow signed mirrors arguments.
+ Move create_mirror_log() into toollib.
+ Determine parallel PVs to avoid with ALLOC_NORMAL allocation.
+ Fix lv_empty.
+
+Version 2.02.01 - 23rd November 2005
+====================================
+ Fix lvdisplay cmdline to accept snapshots.
+ Fix open RO->RW promotion.
+ Fix missing vg_revert in lvcreate error path.
+
+Version 2.02.00 - 10th November 2005
+====================================
+ Extend allocation areas to avoid overflow with contiguous with other PVs.
+ Stop lvcreate attempting to wipe zero or error segments.
+ Added new lvs table attributes.
+ Separated out activation preload.
+ Moved activation functions into libdevmapper.
+ Fixed build_dm_name.
+ Add return macros.
+ Added xen xvd devices.
+ Clear up precommitted metadata better.
+ A pvresize implementation.
+ Fix contiguous allocation when there are no preceding segments.
+ Add mirror_seg pointer to lv_segment struct.
+ Only keep a device open if it's known to belong to a locked VG.
+ Fix lvdisplay to show all mirror destinations.
+ Replacement suspend code using libdevmapper dependency tree.
+ Add DEFS to make.tmpl.
+ Use dm_is_dm_major instead of local copy.
+ Allow mapped devices to be used as PVs.
+ Move set_selinux_context into libdevmapper.
+ Fix automatic text metadata buffer expansion (using macro).
+ Cache formatted text metadata buffer between metadata area writes.
+ Add pe_start field to pvs.
+ Add 'LVM-' prefix to uuids.
+ Split lv_segment_area from lv_segment to permit extension.
+ Replacement deactivation code using libdevmapper dependency tree.
+ Simplify dev_manager_info().
+ Attempt to load missing targets using modprobe.
+ Add -a to lvscan.
+ Move mknodes into libdevmapper.
+ Move bitset, hash, pool and dbg_malloc into libdevmapper.
+
+Version 2.01.15 - 16th October 2005
+===================================
+ Refuse to run pvcreate/pvremove on devices we can't open exclusively.
+ Use ORPHAN lock definition throughout.
+ Validate chunksize in lvcreate.
+ Reduce chunksize limit to 512k.
+ Fix chunksize field in reports.
+ Don't hide snapshots from default 'lvs' output.
+ Add is_dm_major() for use in duplicate device detection in lvmcache_add().
+ Really switch device number in lvmcache when it says it is doing so.
+ Option for bitset memory allocation using malloc as well as pool.
+ Don't assume exactly two mirrors when parsing mirror status.
+ Suppress fsync() error message on filesystems that don't support it.
+ Fix yes_no_prompt() error handling.
+ Add lvm.conf comment warning against multiple filter lines.
+ Tidy lvmconf.sh.
+ Add format1 dev_write debug messages.
+ Add clustered VG attribute to report.
+ Move lvconvert parameters into struct lvconvert_params.
+ Add clustered VG flag to LV lock requests.
+ Change LV locking macros to take lv instead of lvid.
+ Prepend 'cluster' activation parameter to mirror log when appropriate.
+ Pass exclusive flag to lv_activate and on to target activation code.
+ Prevent snapshot creation in a clustered VG for now.
+ Factor out adjusted_mirror_region_size() and generate_log_name_format().
+ Move compose_log_line() into mirror directory.
+ Factor out _get_library_path().
+ Don't kill idling clvmd threads.
+ clvmd no longer takes out locks for non-clustered LVs.
+ Recognise ATA over Ethernet (aoe) devices.
+
+Version 2.01.14 - 4th August 2005
+=================================
+ Fix lvconvert PV parameter in help string.
+ Prevent snapshots getting activated in a clustered VG.
+ Separate out _build_dev_string.
+ Move zero_lv to toollib.
+ Fix pool format handler to work with pv segment code.
+
+Version 2.01.13 - 13th July 2005
+================================
+ Fix pvmove segment splitting.
+ Abstract vg_validate.
+ Only make one attempt at contiguous allocation.
+ Fix lvm1 format metadata read.
+ Fix lvm1 format non-mirror lvcreate.
+
+Version 2.01.12 - 14th June 2005
+================================
+ Various allocation-related pvmove fixes.
+ Log an error if clvmd can't resolve a host name got from CCS.
+ Fix potential spin loop in clvmd.
+
+Version 2.01.11 - 13th June 2005
+================================
+ Added lvmconf.sh.
+ Use matchpathcon mode parameter.
+ Don't defer closing dead FDs in clvmd.
+ Remove hard-coded 64k text metadata writing restriction.
+ Make VG name restrictions consistent.
+ Introduce lvconvert. So far only removes mirror images.
+ Allow mirror images to be resized.
+ Allow mirror images to have more than one segment.
+ Centralise restrictions on LV names.
+ Always insert an intermediate layer for mirrors.
+ Suppress hidden LVs from reports unless --all is given.
+ Use square brackets for hidden LVs in reports.
+ Allow the creation of mirrors with contiguous extents.
+ Always perform sanity checks against metadata before committing it to disk.
+ Split lv_extend into two steps: choosing extents + allocation to LV(s).
+ Add mirror log region size to metadata.
+ Use list_iterate_items throughout and add list*back macros.
+ Introduce seg_ macros to access areas.
+ Add segtype_is_ macros.
+ Support tiny metadata areas for pool conversions.
+ Mirror activation handles disk log as well as core.
+ Activation code recognises mirror log dependency.
+ Add mirror_log and regionsize fields to report.
+ Fix non-orphan pvchange -u.
+ Fix vgmerge to handle duplicate LVIDs.
+ Move archiver code from tools into library.
+ vgscan/change/display/vgs automatically create metadata backups if needed.
+ Merge cloned allocation functions.
+ Fix contiguous allocation policy with linear.
+ Cope with missing format1 PVs again.
+ Remove lists of free PV segments.
+ Simplify pv_maps code and remove slow bitset algorithm.
+ Red-Hat-ify the clvmd rhel4 initscript.
+ %Zu->%zu
+ Fix loopfiles alias alloc & mem debugging.
+ Un-inline dbg_strdup.
+ lv_reduce tidying.
+ Remove some unnecessary parameters.
+ Introduce seg_is macros.
+
+Version 2.01.10 - 3rd May 2005
+==============================
+ Don't create backup and archive dirs till needed.
+ Reinstate full PV size when removing from VG.
+ Support loopfiles for testing.
+ Tidy lv_segment interface.
+ pv_segment support.
+ vgchange --physicalextentsize
+ Internal snapshot restructuring.
+ Remove unused internal non-persistent snapshot option.
+ Allow offline extension of snapshot volumes.
+ Move from 2-step to 3-step on-disk metadata commit.
+ Scan ramdisks too and allow non-O_DIRECT fallback.
+ Annotate, tidy and extend list.h.
+ Alignment tidying.
+ Make clvmd work around some "bugs" in gulm's node state notifications.
+ Tidy clvmd's SIGHUP handler
+
+Version 2.01.09 - 4th April 2005
+================================
+ Add --ignorelockingfailure to vgmknodes.
+ clvmd: Don't allow user operations to start until the lvm thread is fully up.
+ clvmd-gulm: set KEEPALIVE on sockets.
+
+Version 2.01.08 - 22nd March 2005
+=================================
+ Add clustered attribute so vgchange can identify clustered VGs w/o locking.
+ Improve detection of external changes affecting internal cache.
+ Add 'already in device cache' debug message.
+ Add -a to pvdisplay -C.
+ Avoid rmdir opendir error messsages when dir was already removed.
+ Tighten signal handlers.
+ Avoid some compiler warnings.
+ Additional rename failure error message.
+ read/write may be macros.
+ clvmd: don't take out lvm thread lock at startup, it only protects jobs list.
+
+Version 2.01.07 - 8th March 2005
+================================
+ Cope with new devices appearing by rescanning /dev if a uuid can't be found.
+ Remove DESTDIR from LVM_SHARED_PATH.
+ clvmd fixes: make FDs close-on-exec
+ gulm unlocks VG & orphan locks at startup in case they are stale
+ gulm now unlocks VG & orphan locks if client dies.
+
+Version 2.01.06 - 1st March 2005
+================================
+ Suppress 'open failed' error messages during scanning.
+ Option to suppress warnings of file descriptors left open.
+ Fix default value of metadatacopies in documentation (2->1).
+ Fix clvmd-gulm locking.
+ ./configure --enable-debug now enables debugging code in clvmd.
+ Fix clvmd-gulm node up/down code so it actually works.
+ clvmd-gulm now releases locks when shut down.
+
+Version 2.01.05 - 18th February 2005
+====================================
+ Static binary invokes dynamic binary if appropriate.
+ Make clvmd config check a little more tolerant.
+ gulm clvmd can now cope with >1 message arriving in a TCP message.
+
+Version 2.01.04 - 9th February 2005
+===================================
+ Add fixed offset to imported pool minor numbers.
+ Update binary pathnames in clvmd_init_rhel4.
+ lvm2cmd.so should skip the check for open fds.
+ Remove unused -f from pvmove.
+ Gulm clvmd doesn't report "connection refused" errors.
+ clvmd does a basic config file sanity check at startup.
+ Fix potential thread shutdown race in clvmd.
+
+Version 2.01.03 - 1st February 2005
+===================================
+ More 64-bit display/report fixes.
+ More informative startup mesg if can't create /etc/lvm.
+ Fix snapshot device size bug (since 2.01.01).
+ clvmd announces startup and cluster connection in syslog.
+ Gulm clvmd doesn't hang trying to talk to a rebooted node.
+ Gulm clvmd doesn't print cman error on startup.
+
+Version 2.01.02 - 21st January 2005
+===================================
+ Update clvmd_init_rhel4: use lvm.static and don't load dlm.
+ Fix some size_t printing.
+ Fix 64 bit xlate consts.
+ Split out pool sptype_names to avoid unused const.
+ Always fail if random id generation fails.
+ Recognise gnbd devices.
+ Fix clvmd startup bug introduced in cman/gulm amalgamation.
+ Improve reporting of node-specific locking errors.
+
+Version 2.01.01 - 19th January 2005
+===================================
+ Fix clvmd lv_info_by_lvid open_count.
+ Store snapshot and origin sizes separately.
+ Update vgcreate man page.
+
+Version 2.01.00 - 17th January 2005
+===================================
+ Fix vgscan metadata auto-correction.
+ Only ask libdevmapper for open_count when we need it.
+ Adjust RHEL4 clvmd init script priority.
+ Enable building of CMAN & GULM versions of clvmd into a single binary
+
+Version 2.00.33 - 7th January 2005
+==================================
+ pvcreate wipes first 4 sectors unless given --zero n.
+ gulm clvmd now uses new ccsd key names.
+ gulm clvmd now doesn't ignore the first node in cluster.conf
+ Improve clvmd failure message if it's already running.
+ Allow user to kill clvmd during initialisation.
+ Fix off-by-one error in cluster_locking that could cause read hangs.
+
+Version 2.00.32 - 22nd December 2004
+====================================
+ Drop static/dl restriction for now.
+ Fix an error fprintf.
+ Fix vgdisplay -s. Breaks (undocumented) lvs/pvs/vgs -s instead for now.
+ Fix device reference counting on re-opens.
+ Ignore sysfs symlinks when DT_UNKNOWN.
+ Add clvmd init script for RHEL4.
+ Skip devices that are too small to be PVs.
+ Fix pvchange -x segfault with lvm2-format orphan.
+ Cope with empty msdos partition tables.
+ Add CONTRIBUTORS file.
+
+Version 2.00.31 - 12th December 2004
+====================================
+ Reopen RO file descriptors RW if necessary.
+
+Version 2.00.30 - 10th December 2004
+====================================
+ Additional device-handling debug messages.
+ Additional verbosity level -vvvv includes line numbers and backtraces.
+ Verbose messages now go to stderr not stdout.
+ Close any stray file descriptors before starting.
+ Refine partitionable checks for certain device types.
+ Allow devices/types to override built-ins.
+ Fix lvreduce man page .i->.I
+ Fix vgsplit man page title.
+ Fix clvmd man makefile.
+ Extend dev_open logging.
+ Make clvmd_fix_conf.sh UNDOable.
+
+Version 2.00.29 - 27th November 2004
+====================================
+ xlate compilation fix.
+
+Version 2.00.28 - 27th November 2004
+====================================
+ Fix partition table & md signature detection.
+ Minor configure/makefile tidy.
+ Export version.h from tools for clvmd.
+
+Version 2.00.27 - 24th November 2004
+====================================
+ Trap large memory allocation requests.
+ Fix to partition table detection code.
+ Improve filter debug mesgs.
+ Make clvmd_fix_conf.sh UNDOable
+
+Version 2.00.26 - 23rd November 2004
+====================================
+ Improve pool debugging stats.
+ Detect partition table signature.
+ pvcreate wipes md superblocks. (With --uuid or --restorefile it prompts.)
+ Separate out md superblock detection code.
+ Prevent snapshot origin resizing.
+ Improve a vgremove error message.
+ Update some man pages.
+ Allow y/n with -ae args (exclusive activation).
+ Fixes to lvcreate vgname parsing.
+ Fix dm_name string size calculation.
+ Improve clvmd error reporting during startup.
+ Make clvmd cope with large gaps in node numbers IDs.
+ Make clvmd initialisation cope better with debugging output.
+ Tidy clvmd socket callbacks so all work happens outside main loop.
+ clvmd -V now displays lvm version too.
+ Add optional gulm build for clvmd
+
+Version 2.00.25 - 29th September 2004
+=====================================
+ Fix return code from rm_link for vgmknodes.
+ Make clvmd LV hash table thread-safe.
+ Fix clvmd locking so it will lock out multiple users on the same node.
+ Fix clvmd VG locking to it can cope with multiple VG locks.
+ Remove spurious trailing dot in lvreduce man page.
+ Fix vgremove locking.
+
+Version 2.00.24 - 16th September 2004
+=====================================
+ Fix pool_empty so it really does empty the memory pool.
+ Rename old segtypes files to segtype.
+ Some fixes to memory debugging code.
+ Exclude internal commands formats & segtypes from install.
+
+Version 2.00.23 - 15th September 2004
+=====================================
+ Export dm name build & split functions.
+ Use O_NOATIME on devices if available.
+ Write log message when each segtype/format gets initialised.
+ New commands 'segtypes' and 'formats'.
+ Suppress pvmove abort message in test mode.
+ Improve pvcreate/remove device not found error message.
+ Allow pvmove to move data within the same PV.
+ Describe how pvmove works on man page.
+ Test for incompatible format/segtype combinations in lv_extend.
+ Fix lvchange example on man page.
+
+Version 2.00.22 - 3rd September 2004
+====================================
+ Fix /dev/vgname perms.
+ Restructure xlate.h.
+ Add clvmd man page.
+
+Version 2.00.21 - 19th August 2004
+==================================
+ Update cnxman-socket.h from cman.
+ Recognise iseries/vd devices.
+ Use 'make install_cluster' to install cluster extensions only.
+ Cope with DT_UNKNOWN in sysfs.
+ Fix extents_moved metadata size comment.
+ Remove duplicate line in pvremove help text.
+ Support variable mirror region size.
+ Support PE ranges in pvmove source PV.
+ Fixes to as-yet-unused LV segment splitting code.
+ Change alloc_areas to pe_ranges and allow suppression of availability checks.
+ Add dev_size column to pvs.
+ Add report columns for in-kernel device number.
+
+Version 2.00.20 - 3 July 2004
+=============================
+ More autoconf fixes.
+ Fix device number handling for 2.6 kernels.
+
+Version 2.00.19 - 29 June 2004
+==============================
+ Reduce severity of setlocale failure message.
+ Recognise argv[0] "initrd-lvm" (pld-linux).
+ Make -O2 configurable.
+ Added --disable-selinux to configure script.
+ LD_FLAGS->LDFLAGS & LD_DEPS->LDDEPS in configure script.
+ Add init_debug to clvmd.
+
+Version 2.00.18 - 24 June 2004
+==============================
+ Fix vgchange activation.
+ Add cluster support.
+
+Version 2.00.17 - 20 June 2004
+==============================
+ configure --enable-fsadm to try out fsadm. fsadm is not tested yet.
+ Display all filtered devices, not just PVs, with pvs -a.
+ Fix sync_dir() when no / in filename
+ vgcfgbackup -f accepts template with %s for VG name.
+ Extend hash functions to handle non-null-terminated data.
+ Add local activation support.
+ Tidy relative paths in makefile includes.
+ fsadm support for fsck and resizing - needs testing.
+ Add read-only GFS pool support.
+ Add lvm2create_initrd script from http://poochiereds.net/svn/lvm2/
+ Fix rounding of large diplayed sizes.
+ Suppress decimal point when using units of sectors/bytes.
+ Additional kernel target checks before pvmove & snapshot creation.
+ Add i2o_block.
+
+Version 2.00.16 - 24 May 2004
+=============================
+ Set area_count within alloc_lv_segment.
+ Remove error labels from lvresize.
+ Fix a pvs error path.
+ xxchange -ae for exclusive activation.
+ Don't return non-zero status if there aren't any volume groups.
+ Add --alloc argument to tools.
+ Rename allocation policies to contiguous, normal, anywhere, inherit.
+ nextfree becomes normal; anywhere isn't implemented yet.
+ LV inherits allocation policy from VG. Defaults: LV - inherit; VG - normal
+ Additional status character added to vgs to indicate allocation policy.
+ Add reset_fn to external_locking.
+ Ensure presence of virtual targets before attempting activating.
+ Attempt to fix resizing of snapshot origins.
+ Restructure lvresize, bringing it closer to lvcreate.
+ A quick sanity check on vg_disk struct when read in. More checks needed.
+ Only include visible LVs in active/open counts.
+ Add virtual segment types, zero and error. A large sparse device can be
+constructed as a writeable snapshot of a large zero segment.
+ Add --type to lvcreate/resize.
+ Push lv_create & alloc policy up to tool level.
+ Fix pvdisplay return code.
+ Detect invalid LV names in arg lists.
+ Reporting uses line-at-a-time output.
+ lvm2 format sets unlimited_vols format flag.
+ Internal-only metadata flag support.
+ Basic checking for presence of device-mapper targets.
+ Separate out polldaemon.
+ Revise internal locking semantics.
+ Move find_pv_by_name to library.
+ Rename move->copy.
+ Add devices to segments report.
+ Begin separating out segment code. There's a lot of change here.
+ Compress any (obsolete) long LVM1 pvids encountered.
+ Support for tagged config files.
+ Don't abort operations if selinux present but disabled.
+ Fix typo in configure which left HAVE_LIBDL unset.
+
+Version 2.00.15 - 19 Apr 2004
+=============================
+ configure --with-owner= --with-group= to avoid -o and -g args to 'install'
+
+Version 2.00.14 - 16 Apr 2004
+=============================
+ Use 64-bit file functions by default.
+
+Version 2.00.13 - 16 Apr 2004
+=============================
+ Set devices/md_component_detection = 1 to ignore devices containing md
+ superblocks. [Luca Berra]
+ Ignore error setting selinux file context if fs doesn't support it.
+
+Version 2.00.12 - 14 Apr 2004
+=============================
+ Install a default lvm.conf into /etc/lvm if there isn't one already.
+ Allow different installation dir for lvm.static (configure --staticdir=)
+ Fix inverted selinux error check.
+ Recognise power2 in /proc/devices.
+ Fix counting in lvs_in_vg_opened. [It ignored devices open more than once.]
+
+Version 2.00.11 - 8 Apr 2004
+============================
+ Set fallback_to_lvm1 in lvm.conf (or configure --enable-lvm1_fallback)
+ to run lvm1 binaries if running a 2.4 kernel without device-mapper.
+
+Version 2.00.10 - 7 Apr 2004
+============================
+ More fixes for static build.
+ Add basic selinux support.
+ Fix sysfs detection.
+
+Version 2.00.09 - 31 Mar 2004
+=============================
+ Update copyright notices for Red Hat.
+ Fix vgmknodes to remove dud /dev/mapper entries. (libdevmapper update reqd).
+ Add LVM1-style colon output to vgdisplay.
+ lvchange --refresh to reload active LVs.
+ Add string display to memory leak dump.
+ Add locking flags & memlock option.
+ Add list_versions to library.
+ Ignore open hidden LVs when checking if deactivation is OK.
+ Suppress move percentage when device inactive.
+ Add lv_info_by_lvid.
+ Various tidy-ups to the build process.
+ Rebaseline internal verbose level.
+ Add --nolocking option for read operations if locking is failing.
+ Add option to compile into a library.
+ When compiled without libdevmapper, only print warning message once.
+ Fix lvreduce PV extent calculations.
+ Fix DESTDIR to work with configure path overrides.
+ Always use / as config file separator & rename internal config file variables.
+ Add support for tagging PV/VG/LVs and hosts.
+ Fix rare bug in recognition of long cmdline argument forms.
+ Add basic internationalisation infrastructure.
+ Don't recurse symlinked dirs such as /dev/fd on 2.6 kernels.
+ Update autoconf files.
+ Add sysfs block device filtering for 2.6 kernels.
+ Update refs for move to sources.redhat.com.
+
+Friday 14th November 2003
+=========================
+Some bug fixes & minor enhancements, including:
+ Backwards compatibility with LVM1 metadata improved.
+ Missing man pages written.
+ Tool error codes made more consistent.
+ vgmknodes written.
+ O_DIRECT can be turned off if it doesn't work in your kernel.
+ dumpconfig to display the active configuration file
+
+You need to update libdevmapper before using 'vgmknodes' or 'vgscan --mknodes'.
+If your root filesystem is on an LV, you should run one of those two
+commands to fix up the special files in /dev in your real root filesystem
+after finishing with your initrd. Also, remember you can use
+'vgchange --ignorelockingfailure' on your initrd if the tool fails because
+it can't write a lock file to a read-only filesystem.
+
+Wednesday 30th April 2003
+=========================
+A pvmove implementation is now available for the new metadata format.
+
+When running a command that allocates space (e.g. lvcreate), you can now
+restrict not only which disk(s) may be used but also the Physical Extents
+on those disks. e.g. lvcreate -L 10 vg1 /dev/hda6:1000-2000:3000-4000
+
+
+Monday 18th November 2002
+========================
+
+The new format of LVM metadata is ready for you to test!
+ We expect it to be more efficient and more robust than the original format.
+ It's more compact and supports transactional changes and replication.
+ Should things go wrong on a system, it's human-readable (and editable).
+
+Please report any problems you find to the mailing list,
+linux-lvm@sistina.com. The software has NOT yet been thoroughly
+tested and so quite possibly there'll still be some bugs in it.
+Be aware of the disclaimer in the COPYING file.
+
+While testing, we recommend turning logging on in the configuration file
+to provide us with diagnostic information:
+ log {
+ file="/tmp/lvm2.log"
+ level=7
+ activation=1
+ }
+
+You should schedule regular backups of your configuration file and
+metadata backups and archives (normally kept under /etc/lvm).
+
+Please read docs/example.conf and "man lvm.conf" to find out more about
+the configuration file.
+
+To convert an existing volume group called vg1 to the new format using
+the default settings, use "vgconvert -M2 vg1". See "man vgconvert".
+
+-M (or --metadatatype in its long form) is a new flag to indicate which
+format of metadata the command should use for anything it creates.
+Currently, the valid types are "lvm1" and "lvm2" and they can be
+abbreviated to "1" and "2" respectively. The default value for this
+flag can be changed in the global section in the config file.
+
+Backwards-compatible support for the original LVM1 metadata format is
+maintained, but it can be moved into a shared library or removed
+completely with configure's --with-lvm1 option.
+
+Under LVM2, the basic unit of metadata is the volume group. Different
+volume groups can use different formats of metadata - vg1 could use
+the original LVM1 format while vg2 used the new format - but you can't
+mix formats within a volume group. So to add a PV to an LVM2-format
+volume group you must run "pvcreate -M2" on it, followed by "vgextend".
+
+With LVM2-format metadata, lvextend will let you specify striping
+parameters. So an LV could consist of two or more "segments" - the
+first segment could have 3 stripes while the second segment has just 2.
+
+LVM2 maintains a backup of the current metadata for each volume group
+in /etc/lvm/backup, and puts copies of previous versions in
+/etc/lvm/archive. "vgcfgbackup" and "vgcfgrestore" can be used to
+create and restore from these files. If you fully understand what
+you're doing, metadata can be changed by editing a copy of a current
+backup file and using vgcfgrestore to reload it.
+
+Please read the pvcreate man page for more information on the new
+format for metadata.
+
+All tools that can change things have a --test flag which can be used
+to check the effect of a set of cmdline args without really making the
+changes.
+
+
+What's not finished?
+====================
+The internal cache. If you turn on debugging output you'll see lots of
+repeated messages, many of which will eventually get optimised out.
+
+--test sometimes causes a command to fail (e.g. vgconvert --test) even
+though the real command would work: again, fixing this is waiting for
+the work on the cache.
+
+Several of the tools do not yet contain the logic to handle full
+recovery: combinations of pvcreate and vgcfgrestore may sometimes be
+needed to restore metadata if a tool gets interrupted or crashes or
+finds something unexpected. This applies particularly to tools that
+work on more than one volume group at once (e.g. vgsplit).
+
+Display output. Some metadata information cannot yet be displayed.
+
+Recovery tools to salvage "lost" metadata directly from the disks:
+but we hope the new format will mean such tools are hardly ever needed!
--- /dev/null
+Version 1.02.60 - 20th December 2010
+====================================
+ Check for unlink failure in remove_lockfile() in dmeventd.
+ Use dm_free for dm_malloc-ed areas in _clog_ctr/_clog_dtr in cmirrord.
+ Use char* arithmetic in _process_all() & _targets() in dmsetup.
+ Change dm_regex_create() API to accept const char * const *patterns.
+ Add new dm_prepare_selinux_context fn to libdevmapper and use it throughout.
+ Detect existence of new SELinux selabel interface during configure.
+
+Version 1.02.59 - 6th December 2010
+===================================
+ Add backtraces to _process_mapper_dir and _create_and_load_v4 error paths.
+ Remove superfluous checks for NULL before calling dm_free.
+
+Version 1.02.58 - 22nd November 2010
+====================================
+ Fix _output_field crash from field_id free with DEBUG_MEM. (1.02.57)
+
+Version 1.02.57 - 8th November 2010
+===================================
+ Fix regex optimiser not to ignore RHS of OR nodes in _find_leftmost_common.
+ Add dmeventd -R to restart dmeventd without losing monitoring state. (1.02.56)
+ Fix memory leak of field_id in _output_field function.
+ Allocate buffer for reporting functions dynamically to support long outputs.
+
+Version 1.02.56 - 25th October 2010
+===================================
+ Return const pointer from dm_basename() in libdevmapper.
+ Implement dmeventd -R to restart without state loss.
+ Add dm_zalloc and use it and dm_pool_zalloc throughout.
+ Add --setuuid to dmsetup rename.
+ Add dm_task_set_newuuid to set uuid of mapped device post-creation.
+
+Version 1.02.55 - 24th September 2010
+=====================================
+ Fix the way regions are marked complete to avoid slow --nosync cmirror I/O.
+ Add DM_REPORT_FIELD_TYPE_ID_LEN to libdevmapper.h.
+
+Version 1.02.54 - 18th August 2010
+==================================
+ Fix dm-mod autoloading logic to not assume control node is set correctly.
+ Add dmeventd/executable to lvm.conf to test alternative dmeventd.
+ Export dm_event_handler_set_dmeventd_path to override built-in dmeventd path.
+ Generate libdevmapper-event exported symbols.
+ Remove superfluous NULL pointer tests before dm_free from dmeventd.
+ Assume dm-mod autoloading support is in kernel 2.6.36 and higher, not 2.6.35.
+ Fix udev rules to support udev database content generated by older rules.
+ Reinstate detection of inappropriate uevent with DISK_RO set and suppress it.
+ Fix regex ttree off-by-one error.
+ Add --enable-valgrind-pool to configure.
+ Fix segfault in regex matcher with characters of ordinal value > 127.
+ Fix 'void*' arithmetic warnings in dbg_malloc.c and libdm-iface.c.
+ Wait for node creation before displaying debug info in dmsetup.
+ Fix return status 0 for "dmsetup info -c -o help".
+ Add check for kernel semaphore support and disable udev_sync if not available.
+
+Version 1.02.53 - 28th July 2010
+================================
+ Revert failed table load preparation after "create, load and resume".
+ Switch dmeventd to use dm_create_lockfile and drop duplicate code.
+ Add dm_create_lockfile to libdm to handle pidfiles for all daemons.
+ Replace lookup with next in struct dfa_state & calculate states on demand.
+ Improve the regex matcher, reducing the number of charset nodes used.
+ Add dm_regex_fingerprint to facilitate regex testing.
+ Skip ffs(0) in _test_word in bitset functions.
+ Use "nowatch" udev rule for inappropriate devices.
+
+Version 1.02.52 - 6th July 2010
+===============================
+ Fix dmlosetup snprintf %llu compiler warning.
+ Add parentheses to some libdevmapper.h macro arguments.
+ Add printf format attributes to dm_{sn,as}printf and fix a caller.
+ Move dmeventd man page from install_lvm2 to install_device-mapper. (1.02.50)
+
+Version 1.02.51 - 30th June 2010
+================================
+ Generate libdevmapper exported symbols from header file.
+
+Version 1.02.50 - 23rd June 2010
+================================
+ Fix INTERNAL_ERROR typo in ioctl iface unknown task message.
+ Fix udev rules to handle spurious events properly.
+ Use C99 [] not [0] in dm_ulog_request struct to avoid abort when fortified.
+ Allow use of devmapper header file in C++ mode (extern "C" and __typeof__).
+ Add dmeventd man page.
+
+Version 1.02.49 - 4th June 2010
+===============================
+ Support autoloading of dm-mod module for kernels from 2.6.35.
+ Document 'clear' in dmsetup man page.
+ Fix semctl parameter (union) to avoid misaligned parameter on some arches.
+ Add dm_tree_node_set_presuspend_node() to presuspend child when deactivating.
+ Initial support for replicator target.
+
+Version 1.02.48 - 17th May 2010
+================================
+ Use -d to control level of messages sent to syslog by dmeventd.
+ Change -d to -f to run dmeventd in foreground.
+ Do not print encryption key in message debug output (cryptsetup luksResume).
+ Fix dmeventd static build library dependencies.
+ Fix udev flags on remove in create_and_load error path.
+
+Version 1.02.47 - 30th April 2010
+=================================
+ Add support for new IMPORT{db} udev rule.
+ Add DM_UDEV_PRIMARY_SOURCE_FLAG udev flag to recognize proper DM events.
+ Also include udev libs in libdevmapper.pc when udev_sync is enabled.
+ Cache bitset locations to speed up _calc_states.
+ Add a regex optimisation pass for shared prefixes and suffixes.
+ Add dm_bit_and and dm_bitset_equal to libdevmapper.
+ Simplify dm_bitset_create.
+ Speed up dm_bit_get_next with ffs().
+
+Version 1.02.46 - 14th April 2010
+=================================
+ Change dm_tree_deactivate_children to fail if device is open.
+ Wipe memory buffers for dm-ioctl parameters before releasing.
+ Strictly require libudev if udev_sync is used.
+ Add support for ioctl's DM_UEVENT_GENERATED_FLAG.
+
+Version 1.02.45 - 9th March 2010
+================================
+ Add --showkeys parameter description to dmsetup man page.
+ Add --help option as synonym for help command.
+
+Version 1.02.44 - 15th February 2010
+====================================
+ Add DM_UDEV_DISABLE_LIBRARY_FALLBACK udev flag to rely on udev only.
+ Export dm_udev_create_cookie function to create new cookies on demand.
+ Add --udevcookie, udevcreatecookie and udevreleasecookie to dmsetup.
+ Set udev state automatically instead of using DM_UDEV_DISABLE_CHECKING.
+
+Version 1.02.43 - 21st January 2010
+===================================
+ Remove bitset, hash and pool headers superceded by libdevmapper.h.
+ Fix off-by-one error causing bad cluster mirror table construction.
+
+Version 1.02.42 - 14th January 2010
+===================================
+ Add support for the "snapshot-merge" kernel target (2.6.33-rc1).
+ Introduce a third activation_priority level in dm_tree_activate_children.
+
+Version 1.02.41 - 12th January 2010
+===================================
+ If DM_UDEV_DISABLE_CHECKING is set in environment, disable udev warnings.
+ Add dm_tree_add_dev_with_udev_flags to provide wider support for udev flags.
+ Add --noudevrules option for dmsetup to disable /dev node management by udev.
+ Fix 'dmsetup info -c -o all' to show all fields.
+ Return errors if dm_tree_*_children functions fail.
+ Fix coredump and memory leak for 'dmsetup help -c'.
+ Disable udev rules for change events with DISK_RO set.
+
+Version 1.02.40 - 19th November 2009
+====================================
+ Fix install_device-mapper Makefile target to not build dmeventd plugins.
+ Support udev flags even when udev_sync is disabled or not compiled in.
+ Remove 'last_rule' from udev rules: honour DM_UDEV_DISABLE_OTHER_RULES_FLAG.
+ Add dmsetup --inactive support.
+ Add dm_task_query_inactive_table to libdevmapper for kernel driver >= 4.16.
+ Fix hash lookup segfault when keys compared are different lengths.
+
+Version 1.02.39 - 26th October 2009
+===================================
+ Remove strict default permissions for DM devices from 95-dm-notify.rules.
+ Add dmsetup udevflags command to decode udev flags in given cookie value.
+ Support udev flags in libdevmapper incl. dm_tree_add_new_dev_with_udev_flags.
+ Make libdm ABI consistent when built with/without selinux support.
+
+Version 1.02.38 - 25th September 2009
+=====================================
+ Export DM_DEV_DIR_UMASK, the default umask for /dev directories created.
+ Handle any path supplied to dm_task_set_name by looking up in /dev/mapper.
+ Add several examples to 12-dm-permissions.rules.
+ Add splitname and --yes to dmsetup man page.
+ Fix _mirror_emit_segment_line return code.
+ Fix dmeventd _temporary_log_fn parameters. (2.02.50)
+
+Version 1.02.37 - 15th September 2009
+=====================================
+ Add dmsetup manpage entries for udevcomplete_all and udevcookies.
+ Check udev is running when processing cookies and retain state internally.
+ Add y|--yes option to dmsetup for default 'yes' answer to prompts.
+ Fix tools Makefile to process dmsetup sources separately.
+ Restore umask when device node creation fails.
+ Check kernel vsn to use 'block_on_error' or 'handle_errors' in mirror table.
+ Add dm-log-userspace.h to tree for cmirrord builds.
+
+Version 1.02.36 - 6th August 2009
+=================================
+ Add udevcookies, udevcomplete, udevcomplete_all and --noudevwait to dmsetup.
+ Add libdevmapper functions to support synchronisation with udev.
+
+Version 1.02.35 - 28th July 2009
+================================
+ Add LOG_LINE_WITH_ERRNO macro.
+ Use log_error macro consistently throughout in place of log_err.
+
+Version 1.02.34 - 15th July 2009
+================================
+ Use _exit() not exit() after forking to avoid flushing libc buffers twice.
+ Rename plog macro to LOG_LINE & add LOG_MESG variant for dm_dump_memory_debug.
+ Change plog to use dm_log_with_errno unless deprecated dm_log_init was used.
+ Add dm_log_with_errno and dm_log_with_errno_init, deprecating the old fns.
+ Fix whitespace in linear target line to fix identical table line detection.
+ Add device number to more log messages during activation.
+
+Version 1.02.33 - 30th June 2009
+================================
+ Don't fallback to default major number: use dm_task_set_major_minor. (1.02.31)
+ Do not fork daemon when dmeventd cannot be found.
+ Add crypt target handling to libdevmapper tree nodes.
+ Add splitname command to dmsetup.
+ Add subsystem, vg_name, lv_name, lv_layer fields to dmsetup reports.
+ Make mempool optional in dm_split_lvm_name().
+
+Version 1.02.32 - 21st May 2009
+===============================
+ Only generate libdevmapper.a when configured to link statically.
+ Export dm_tree_node_size_changed() from libdevmapper.
+ Propagate the table size_changed property up the dm device tree.
+ Detect failure to free memory pools when releasing the library.
+ Fix segfault when getopt processes dmsetup -U, -G and -M options.
+
+Version 1.02.31 - 3rd March 2009
+================================
+ If kernel supports only one dm major number, use in place of any supplied.
+
+Version 1.02.30 - 26th January 2009
+====================================
+ Add "all" field to reports expanding to all fields of report type.
+ Enforce device name length and character limitations in libdm.
+ Replace _dm_snprintf with EMIT_PARAMS macro for creating target lines.
+
+Version 1.02.29 - 10th November 2008
+====================================
+ Merge device-mapper into the LVM2 tree.
+ Split out dm-logging.h from log.h.
+ Use lvm-types.h.
+ Add usrsbindir to configure.
+
+Version 1.02.28 - 18th September 2008
+=====================================
+ Only resume devices in dm_tree_preload_children if size changes.
+ Extend deptree buffers so the largest possible device numbers fit.
+ Generate versioned libdevmapper-event.so.
+ Underline longer report help text headings.
+
+Version 1.02.27 - 25th June 2008
+================================
+ Align struct memblock in dbg_malloc for sparc.
+ Add --unquoted and --rows to dmsetup.
+ Avoid compiler warning about cast in dmsetup.c's OFFSET_OF macro.
+ Fix inverted no_flush debug message.
+ Remove --enable-jobs from configure. (Set at runtime instead.)
+ Bring configure.in and list.h into line with the lvm2 versions.
+
+Version 1.02.26 - 6th June 2008
+===============================
+ Initialise params buffer to empty string in _emit_segment.
+ Skip add_dev_node when ioctls disabled.
+ Make dm_hash_iter safe against deletion.
+ Accept a NULL pointer to dm_free silently.
+ Add tables_loaded, readonly and suspended columns to reports.
+ Add --nameprefixes to dmsetup.
+ Add field name prefix option to reporting functions.
+ Calculate string size within dm_pool_grow_object.
+
+Version 1.02.25 - 10th April 2008
+=================================
+ Remove redundant if-before-free tests.
+ Use log_warn for reporting field help text instead of log_print.
+ Change cluster mirror log type name (s/clustered_/clustered-/)
+
+Version 1.02.24 - 20th December 2007
+====================================
+ Fix deptree to pass new name to _resume_node after a rename.
+ Suppress other node operations if node is deleted.
+ Add node operation stack debug messages.
+ Report error when empty device name passed to readahead functions.
+ Fix minimum readahead debug message.
+
+Version 1.02.23 - 5th December 2007
+===================================
+ Update dm-ioctl.h after removal of compat code.
+ Add readahead support to libdevmapper and dmsetup.
+ Fix double free in a libdevmapper-event error path.
+ Fix configure --with-dmeventd-path substitution.
+ Allow a DM_DEV_DIR environment variable to override /dev in dmsetup.
+ Create a libdevmapper.so.$LIB_VERSION symlink within the build tree.
+ Avoid static link failure with some SELinux libraries that require libpthread.
+ Remove obsolete dmfs code from tree and update INSTALL.
+
+Version 1.02.22 - 21st August 2007
+==================================
+ Fix inconsistent licence notices: executables are GPLv2; libraries LGPLv2.1.
+ Update to use autoconf 2.61, while still supporting 2.57.
+ Avoid repeated dm_task free on some dm_event_get_registered_device errors.
+ Introduce log_sys_* macros from LVM2.
+ Export dm_fclose and dm_create_dir; remove libdm-file.h.
+ Don't log EROFS mkdir failures in _create_dir_recursive (for LVM2).
+ Add fclose wrapper dm_fclose that catches write failures (using ferror).
+
+Version 1.02.21 - 13th July 2007
+================================
+ Introduce _LOG_STDERR to send log_warn() messages to stderr not stdout.
+ Fix dmsetup -o devno string termination. (1.02.20)
+
+Version 1.02.20 - 15th June 2007
+================================
+ Fix default dmsetup report buffering and add --unbuffered.
+ Add tree-based and dependency fields to dmsetup reports.
+
+Version 1.02.19 - 27th April 2007
+=================================
+ Standardise protective include file #defines.
+ Add regex functions to library.
+ Avoid trailing separator in reports when there are hidden sort fields.
+ Fix segfault in 'dmsetup status' without --showkeys against crypt target.
+ Deal with some more compiler warnings.
+ Introduce _add_field() and _is_same_field() to libdm-report.c.
+ Fix some libdevmapper-event and dmeventd memory leaks.
+ Remove unnecessary memset() return value checks.
+ Fix a few leaks in reporting error paths. [1.02.15+]
+
+Version 1.02.18 - 13th February 2007
+====================================
+ Improve dmeventd messaging protocol: drain pipe and tag messages.
+
+Version 1.02.17 - 29th January 2007
+===================================
+ Add recent reporting options to dmsetup man page.
+ Revise some report fields names.
+ Add dmsetup 'help' command and update usage text.
+ Use fixed-size fields in report interface and reorder.
+
+Version 1.02.16 - 25th January 2007
+===================================
+ Add some missing close() and fclose() return value checks.
+ Migrate dmsetup column-based output over to new libdevmapper report framework.
+ Add descriptions to reporting field definitions.
+ Add a dso-private variable to dmeventd dso interface.
+ Add dm_event_handler_[gs]et_timeout functions.
+ Streamline dm_report_field_* interface.
+ Add cmdline debug & version options to dmeventd.
+ Add DM_LIB_VERSION definition to configure.h.
+ Suppress 'Unrecognised field' error if report field is 'help'.
+ Add --separator and --sort to dmsetup (unused).
+ Make alignment flag optional when specifying report fields.
+
+Version 1.02.15 - 17th January 2007
+===================================
+ Add basic reporting functions to libdevmapper.
+ Fix a malloc error path in dmsetup message.
+ More libdevmapper-event interface changes and fixes.
+ Rename dm_saprintf() to dm_asprintf().
+ Report error if NULL pointer is supplied to dm_strdup_aux().
+ Reinstate dm_event_get_registered_device.
+
+Version 1.02.14 - 11th January 2007
+===================================
+ Add dm_saprintf().
+ Use CFLAGS when linking so mixed sparc builds can supply -m64.
+ Add dm_tree_use_no_flush_suspend().
+ Lots of dmevent changes including revised interface.
+ Export dm_basename().
+ Cope with a trailing space when comparing tables prior to possible reload.
+ Fix dmeventd to cope if monitored device disappears.
+
+Version 1.02.13 - 28 Nov 2006
+=============================
+ Update dmsetup man page (setgeometry & message).
+ Fix dmsetup free after getline with debug.
+ Suppress encryption key in 'dmsetup table' output unless --showkeys supplied.
+
+Version 1.02.12 - 13 Oct 2006
+=============================
+ Avoid deptree attempting to suspend a device that's already suspended.
+
+Version 1.02.11 - 12 Oct 2006
+==============================
+ Add suspend noflush support.
+ Add basic dmsetup loop support.
+ Switch dmsetup to use dm_malloc and dm_free.
+
+Version 1.02.10 - 19 Sep 2006
+=============================
+ Add dm_snprintf(), dm_split_words() and dm_split_lvm_name() to libdevmapper.
+ Reorder mm bounds_check code to reduce window for a dmeventd race.
+
+Version 1.02.09 - 15 Aug 2006
+=============================
+ Add --table argument to dmsetup for a one-line table.
+ Abort if errors are found during cmdline option processing.
+ Add lockfs indicator to debug output.
+
+Version 1.02.08 - 17 July 2006
+==============================
+ Append full patch to check in emails.
+ Avoid duplicate dmeventd subdir with 'make distclean'.
+ Update dmsetup man page.
+ Add --force to dmsetup remove* to load error target.
+ dmsetup remove_all also performs mknodes.
+ Don't suppress identical table reloads if permission changes.
+ Fix corelog segment line.
+ Suppress some compiler warnings.
+
+Version 1.02.07 - 11 May 2006
+=============================
+ Add DM_CORELOG flag to dm_tree_node_add_mirror_target().
+ Avoid a dmeventd compiler warning.
+
+Version 1.02.06 - 10 May 2006
+=============================
+ Move DEFS into configure.h.
+ Fix leaks in error paths found by coverity.
+ Remove dmsetup line buffer limitation.
+
+Version 1.02.05 - 19 Apr 2006
+=============================
+ Separate install_include target in makefiles.
+ Separate out DEFS from CFLAGS.
+ Support pkg-config.
+ Check for libsepol.
+
+Version 1.02.04 - 14 Apr 2006
+=============================
+ Bring dmsetup man page up-to-date.
+ Use name-based device refs if kernel doesn't support device number refs.
+ Fix memory leak (struct dm_ioctl) when struct dm_task is reused.
+ If _create_and_load_v4 fails part way through, revert the creation.
+ dmeventd thread/fifo fixes.
+ Add file & line to dm_strdup_aux().
+ Add setgeometry.
+
+Version 1.02.03 - 7 Feb 2006
+============================
+ Add exported functions to set uid, gid and mode.
+ Rename _log to dm_log and export.
+ Add dm_tree_skip_lockfs.
+ Fix dm_strdup debug definition.
+ Fix hash function to avoid using a negative array offset.
+ Don't inline _find in hash.c and tidy signed/unsigned etc.
+ Fix libdevmapper.h #endif.
+ Fix dmsetup version driver version.
+ Add sync, nosync and block_on_error mirror log parameters.
+ Add hweight32.
+ Fix dmeventd build.
+
+Version 1.02.02 - 2 Dec 2005
+============================
+ dmeventd added.
+ Export dm_task_update_nodes.
+ Use names instead of numbers in messages when ioctls fail.
+
+Version 1.02.01 - 23 Nov 2005
+=============================
+ Resume snapshot-origins last.
+ Drop leading zeros from dm_format_dev.
+ Suppress attempt to reload identical table.
+ Additional LVM- prefix matching for transitional period.
+
+Version 1.02.00 - 10 Nov 2005
+=============================
+ Added activation functions to library.
+ Added return macros.
+ Also suppress error if device doesn't exist with DM_DEVICE_STATUS.
+ Export dm_set_selinux_context().
+ Add dm_driver_version().
+ Added dependency tree functions to library.
+ Added hash, bitset, pool, dbg_malloc to library.
+ Added ls --tree to dmsetup.
+ Added dmsetup --nolockfs support for suspend/reload.
+
+Version 1.01.05 - 26 Sep 2005
+=============================
+ Resync list.h with LVM2.
+ Remember increased buffer size and use for subsequent calls.
+ On 'buffer full' condition, double buffer size and repeat ioctl.
+ Fix termination of getopt_long() option array.
+ Report 'buffer full' condition with v4 ioctl as well as with v1.
+
+Version 1.01.04 - 2 Aug 2005
+============================
+ Fix dmsetup ls -j and status --target with empty table.
+
+Version 1.01.03 - 13 Jun 2005
+=============================
+ Use matchpathcon mode parameter.
+ Fix configure script to re-enable selinux.
+
+Version 1.01.02 - 17 May 2005
+=============================
+ Call dm_lib_exit() and dm_lib_release() automatically now.
+ Add --target <target_type> filter to dmsetup table/status/ls.
+ Add --exec <command> to dmsetup ls.
+ Fix dmsetup getopt_long usage.
+
+Version 1.01.01 - 29 Mar 2005
+=============================
+ Update dmsetup man page.
+ Drop-in devmap_name replacement.
+ Add option to compile without ioctl for testing.
+ Fix DM_LIB_VERSION sed.
+
+Version 1.01.00 - 17 Jan 2005
+=============================
+ Add dm_task_no_open_count() to skip getting open_count.
+
+Version 1.00.21 - 7 Jan 2005
+============================
+ Fix /proc/devices parsing.
+
+Version 1.00.20 - 6 Jan 2005
+============================
+ Attempt to fix /dev/mapper/control transparently if it's wrong.
+ Configuration-time option for setting uid/gid/mode for /dev/mapper nodes.
+ Update kernel patches for 2.4.27/2.4.28-pre-4 (includes minor fixes).
+ Add --noheadings columns option for colon-separated dmsetup output.
+ Support device referencing by uuid or major/minor.
+ Warn if kernel data didn't fit in buffer.
+ Fix a printf.
+
+Version 1.00.19 - 3 July 2004
+=============================
+ More autoconf fixes.
+ Fix a dmsetup newline.
+ Fix device number handling for 2.6 kernels.
+
+Version 1.00.18 - 20 Jun 2004
+=============================
+ Fix a uuid free in libdm-iface.
+ Fix a targets string size calc in driver.
+ Add -c to dmsetup for column-based output.
+ Add target message-passing ioctl.
+
+Version 1.00.17 - 17 Apr 2004
+=============================
+ configure --with-owner= --with-group= to avoid -o and -g args to 'install'
+ Fix library selinux linking.
+
+Version 1.00.16 - 16 Apr 2004
+=============================
+ Ignore error setting selinux file context if fs doesn't support it.
+
+Version 1.00.15 - 7 Apr 2004
+============================
+ Fix status overflow check in kernel patches.
+
+Version 1.00.14 - 6 Apr 2004
+============================
+ Fix static selinux build.
+
+Version 1.00.13 - 6 Apr 2004
+============================
+ Add some basic selinux support.
+
+Version 1.00.12 - 6 Apr 2004
+============================
+ Fix dmsetup.static install.
+
+Version 1.00.11 - 5 Apr 2004
+============================
+ configure --enable-static_link does static build in addition to dynamic.
+ Moved Makefile library targets definition into template.
+
+Version 1.00.10 - 2 Apr 2004
+============================
+ Fix DESTDIR handling.
+ Static build installs to dmsetup.static.
+ Basic support for internationalisation.
+ Minor Makefile tidy-ups/fixes.
+
+Version 1.00.09 - 31 Mar 2004
+=============================
+ Update copyright notices to Red Hat.
+ Move full mknodes functionality from dmsetup into libdevmapper.
+ Avoid sscanf %as for uClibc compatibility.
+ Cope if DM_LIST_VERSIONS is not defined.
+ Add DM_LIST_VERSIONS functionality to kernel patches.
+ Generate new kernel patches for 2.4.26-rc1.
+
+Version 1.00.08 - 27 Feb 2004
+=============================
+ Added 'dmsetup targets'.
+ Added event_nr support to 'dmsetup wait'.
+ Updated dmsetup man page.
+ Allow logging function to be reset to use internal one.
+ Bring log macros in line with LVM2 ones.
+ Added 'make install_static_lib' which installs libdevmapper.a.
+ Made configure/makefiles closer to LVM2 versions.
+ Fixed DESTDIR for make install/install_static_lib.
+ Updated README/INSTALL to reflect move to sources.redhat.com.
+ Updated autoconf files to 2003-06-17.
--- /dev/null
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+# Free Software Foundation, Inc.
+
+timestamp='2008-01-23'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ exit ;;
+ *:SolidBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit ;;
+ arm:riscos:*:*|arm:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c &&
+ dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`$dummy $dummyarg` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[456])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ eval $set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:FreeBSD:*:*)
+ case ${UNAME_MACHINE} in
+ pc98)
+ echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ amd64)
+ echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ *)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ esac
+ exit ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit ;;
+ *:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit ;;
+ i*:windows32*:*)
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
+ exit ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit ;;
+ *:Interix*:[3456]*)
+ case ${UNAME_MACHINE} in
+ x86)
+ echo i586-pc-interix${UNAME_RELEASE}
+ exit ;;
+ EM64T | authenticamd)
+ echo x86_64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ IA64)
+ echo ia64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ esac ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-unknown-cygwin
+ exit ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit ;;
+ arm*:Linux:*:*)
+ eval $set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ else
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit ;;
+ crisv32:Linux:*:*)
+ echo crisv32-axis-linux-gnu
+ exit ;;
+ frv:Linux:*:*)
+ echo frv-unknown-linux-gnu
+ exit ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+ /^CPU/{
+ s: ::g
+ p
+ }'`"
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+ /^CPU/{
+ s: ::g
+ p
+ }'`"
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ or32:Linux:*:*)
+ echo or32-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ vax:Linux:*:*)
+ echo ${UNAME_MACHINE}-dec-linux-gnu
+ exit ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit ;;
+ xtensa*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+ exit ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
+ /^LIBC/{
+ s: ::g
+ p
+ }'`"
+ test x"${LIBC}" != x && {
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+ exit
+ }
+ test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo ${UNAME_MACHINE}-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-7:SUPER-UX:*:*)
+ echo sx7-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8:SUPER-UX:*:*)
+ echo sx8-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8R:SUPER-UX:*:*)
+ echo sx8r-nec-superux${UNAME_RELEASE}
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+ case $UNAME_PROCESSOR in
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NSE-?:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+ exit ;;
+ i*86:rdos:*:*)
+ echo ${UNAME_MACHINE}-pc-rdos
+ exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ c34*)
+ echo c34-convex-bsd
+ exit ;;
+ c38*)
+ echo c38-convex-bsd
+ exit ;;
+ c4*)
+ echo c4-convex-bsd
+ exit ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+and
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
--- /dev/null
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+# Free Software Foundation, Inc.
+
+timestamp='2008-01-16'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
+ uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
+ storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis | -knuth | -cray)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco6)
+ os=-sco5v6
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
+ | bfin \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | fido | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | m32c | m32r | m32rle | m68000 | m68k | m88k \
+ | maxq | mb | microblaze | mcore | mep \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64vr | mips64vrel \
+ | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | mt \
+ | msp430 \
+ | nios | nios2 \
+ | ns16k | ns32k \
+ | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | score \
+ | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+ | spu | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+ ms1)
+ basic_machine=mt-unknown
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* | avr32-* \
+ | bfin-* | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | clipper-* | craynv-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | m32c-* | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mips64vr5900-* | mips64vr5900el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | mmix-* \
+ | mt-* \
+ | msp430-* \
+ | nios-* | nios2-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+ | sparclite-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tron-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
+ | xstormy16-* | xtensa*-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ # Recognize the basic CPU types without company name, with glob match.
+ xtensa*)
+ basic_machine=$basic_machine-unknown
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ abacus)
+ basic_machine=abacus-unknown
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=-linux
+ ;;
+ blackfin-*)
+ basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ craynv)
+ basic_machine=craynv-cray
+ os=-unicosmp
+ ;;
+ cr16)
+ basic_machine=cr16-unknown
+ os=-elf
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ crisv32 | crisv32-* | etraxfs*)
+ basic_machine=crisv32-axis
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ crx)
+ basic_machine=crx-unknown
+ os=-elf
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=-msdosdjgpp
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=-linux
+ ;;
+ m68knommu-*)
+ basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ os=-mingw32ce
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ ms1-*)
+ basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ openrisc | openrisc-*)
+ basic_machine=or32-unknown
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=-linux
+ ;;
+ parisc-*)
+ basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pc98)
+ basic_machine=i386-pc
+ ;;
+ pc98-*)
+ basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rdos)
+ basic_machine=i386-pc
+ os=-rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sde)
+ basic_machine=mipsisa32-sde
+ os=-elf
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sh5el)
+ basic_machine=sh5le-unknown
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tic55x | c55x*)
+ basic_machine=tic55x-unknown
+ os=-coff
+ ;;
+ tic6x | c6x*)
+ basic_machine=tic6x-unknown
+ os=-coff
+ ;;
+ tile*)
+ basic_machine=tile-unknown
+ os=-linux-gnu
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=-mingw32
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ mmix)
+ basic_machine=mmix-knuth
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+ | -openbsd* | -solidbsd* \
+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
+ | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -kaos*)
+ os=-kaos
+ ;;
+ -zvmoe)
+ os=-zvmoe
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ score-*)
+ os=-elf
+ ;;
+ spu-*)
+ os=-elf
+ ;;
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mep-*)
+ os=-elf
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-haiku)
+ os=-haiku
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-knuth)
+ os=-mmixware
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
--- /dev/null
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2006-10-14.15
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+nl='
+'
+IFS=" "" $nl"
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+if test -z "$doit"; then
+ doit_exec=exec
+else
+ doit_exec=$doit
+fi
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+posix_glob=
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chmodcmd=$chmodprog
+chowncmd=
+chgrpcmd=
+stripcmd=
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=
+dst=
+dir_arg=
+dstarg=
+no_target_directory=
+
+usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+ or: $0 [OPTION]... SRCFILES... DIRECTORY
+ or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+ or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+-c (ignored)
+-d create directories instead of installing files.
+-g GROUP $chgrpprog installed files to GROUP.
+-m MODE $chmodprog installed files to MODE.
+-o USER $chownprog installed files to USER.
+-s $stripprog installed files.
+-t DIRECTORY install into DIRECTORY.
+-T report an error if DSTFILE is a directory.
+--help display this help and exit.
+--version display version info and exit.
+
+Environment variables override the default commands:
+ CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+ case $1 in
+ -c) shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ --help) echo "$usage"; exit $?;;
+
+ -m) mode=$2
+ shift
+ shift
+ case $mode in
+ *' '* | *' '* | *'
+'* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd=$stripprog
+ shift
+ continue;;
+
+ -t) dstarg=$2
+ shift
+ shift
+ continue;;
+
+ -T) no_target_directory=true
+ shift
+ continue;;
+
+ --version) echo "$0 $scriptversion"; exit $?;;
+
+ --) shift
+ break;;
+
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
+
+ *) break;;
+ esac
+done
+
+if test $# -ne 0 && test -z "$dir_arg$dstarg"; then
+ # When -d is used, all remaining arguments are directories to create.
+ # When -t is used, the destination is already specified.
+ # Otherwise, the last argument is the destination. Remove it from $@.
+ for arg
+ do
+ if test -n "$dstarg"; then
+ # $@ is not empty: it contains at least $arg.
+ set fnord "$@" "$dstarg"
+ shift # fnord
+ fi
+ shift # arg
+ dstarg=$arg
+ done
+fi
+
+if test $# -eq 0; then
+ if test -z "$dir_arg"; then
+ echo "$0: no input file specified." >&2
+ exit 1
+ fi
+ # It's OK to call `install-sh -d' without argument.
+ # This can happen when creating conditional directories.
+ exit 0
+fi
+
+if test -z "$dir_arg"; then
+ trap '(exit $?); exit' 1 2 13 15
+
+ # Set umask so as not to create temps with too-generous modes.
+ # However, 'strip' requires both read and write access to temps.
+ case $mode in
+ # Optimize common cases.
+ *644) cp_umask=133;;
+ *755) cp_umask=22;;
+
+ *[0-7])
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw='% 200'
+ fi
+ cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+ *)
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw=,u+rw
+ fi
+ cp_umask=$mode$u_plus_rw;;
+ esac
+fi
+
+for src
+do
+ # Protect names starting with `-'.
+ case $src in
+ -*) src=./$src ;;
+ esac
+
+ if test -n "$dir_arg"; then
+ dst=$src
+ dstdir=$dst
+ test -d "$dstdir"
+ dstdir_status=$?
+ else
+
+ # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+ # might cause directories to be created, which would be especially bad
+ # if $src (and thus $dsttmp) contains '*'.
+ if test ! -f "$src" && test ! -d "$src"; then
+ echo "$0: $src does not exist." >&2
+ exit 1
+ fi
+
+ if test -z "$dstarg"; then
+ echo "$0: no destination specified." >&2
+ exit 1
+ fi
+
+ dst=$dstarg
+ # Protect names starting with `-'.
+ case $dst in
+ -*) dst=./$dst ;;
+ esac
+
+ # If destination is a directory, append the input filename; won't work
+ # if double slashes aren't ignored.
+ if test -d "$dst"; then
+ if test -n "$no_target_directory"; then
+ echo "$0: $dstarg: Is a directory" >&2
+ exit 1
+ fi
+ dstdir=$dst
+ dst=$dstdir/`basename "$src"`
+ dstdir_status=0
+ else
+ # Prefer dirname, but fall back on a substitute if dirname fails.
+ dstdir=`
+ (dirname "$dst") 2>/dev/null ||
+ expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$dst" : 'X\(//\)[^/]' \| \
+ X"$dst" : 'X\(//\)$' \| \
+ X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
+ echo X"$dst" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'
+ `
+
+ test -d "$dstdir"
+ dstdir_status=$?
+ fi
+ fi
+
+ obsolete_mkdir_used=false
+
+ if test $dstdir_status != 0; then
+ case $posix_mkdir in
+ '')
+ # Create intermediate dirs using mode 755 as modified by the umask.
+ # This is like FreeBSD 'install' as of 1997-10-28.
+ umask=`umask`
+ case $stripcmd.$umask in
+ # Optimize common cases.
+ *[2367][2367]) mkdir_umask=$umask;;
+ .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+ *[0-7])
+ mkdir_umask=`expr $umask + 22 \
+ - $umask % 100 % 40 + $umask % 20 \
+ - $umask % 10 % 4 + $umask % 2
+ `;;
+ *) mkdir_umask=$umask,go-w;;
+ esac
+
+ # With -d, create the new directory with the user-specified mode.
+ # Otherwise, rely on $mkdir_umask.
+ if test -n "$dir_arg"; then
+ mkdir_mode=-m$mode
+ else
+ mkdir_mode=
+ fi
+
+ posix_mkdir=false
+ case $umask in
+ *[123567][0-7][0-7])
+ # POSIX mkdir -p sets u+wx bits regardless of umask, which
+ # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+ ;;
+ *)
+ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+ if (umask $mkdir_umask &&
+ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+ then
+ if test -z "$dir_arg" || {
+ # Check for POSIX incompatibilities with -m.
+ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+ # other-writeable bit of parent directory when it shouldn't.
+ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+ ls_ld_tmpdir=`ls -ld "$tmpdir"`
+ case $ls_ld_tmpdir in
+ d????-?r-*) different_mode=700;;
+ d????-?--*) different_mode=755;;
+ *) false;;
+ esac &&
+ $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+ ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+ }
+ }
+ then posix_mkdir=:
+ fi
+ rmdir "$tmpdir/d" "$tmpdir"
+ else
+ # Remove any dirs left behind by ancient mkdir implementations.
+ rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+ fi
+ trap '' 0;;
+ esac;;
+ esac
+
+ if
+ $posix_mkdir && (
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+ )
+ then :
+ else
+
+ # The umask is ridiculous, or mkdir does not conform to POSIX,
+ # or it failed possibly due to a race condition. Create the
+ # directory the slow way, step by step, checking for races as we go.
+
+ case $dstdir in
+ /*) prefix=/ ;;
+ -*) prefix=./ ;;
+ *) prefix= ;;
+ esac
+
+ case $posix_glob in
+ '')
+ if (set -f) 2>/dev/null; then
+ posix_glob=true
+ else
+ posix_glob=false
+ fi ;;
+ esac
+
+ oIFS=$IFS
+ IFS=/
+ $posix_glob && set -f
+ set fnord $dstdir
+ shift
+ $posix_glob && set +f
+ IFS=$oIFS
+
+ prefixes=
+
+ for d
+ do
+ test -z "$d" && continue
+
+ prefix=$prefix$d
+ if test -d "$prefix"; then
+ prefixes=
+ else
+ if $posix_mkdir; then
+ (umask=$mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+ # Don't fail if two instances are running concurrently.
+ test -d "$prefix" || exit 1
+ else
+ case $prefix in
+ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) qprefix=$prefix;;
+ esac
+ prefixes="$prefixes '$qprefix'"
+ fi
+ fi
+ prefix=$prefix/
+ done
+
+ if test -n "$prefixes"; then
+ # Don't fail if two instances are running concurrently.
+ (umask $mkdir_umask &&
+ eval "\$doit_exec \$mkdirprog $prefixes") ||
+ test -d "$dstdir" || exit 1
+ obsolete_mkdir_used=true
+ fi
+ fi
+ fi
+
+ if test -n "$dir_arg"; then
+ { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+ { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+ else
+
+ # Make a couple of temp file names in the proper directory.
+ dsttmp=$dstdir/_inst.$$_
+ rmtmp=$dstdir/_rm.$$_
+
+ # Trap to clean up those temp files at exit.
+ trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+ # Copy the file name to the temp name.
+ (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+ # and set any options; do chmod last to preserve setuid bits.
+ #
+ # If any of these fail, we abort the whole thing. If we want to
+ # ignore errors from any of these, just make sure not to ignore
+ # errors from the above "$doit $cpprog $src $dsttmp" command.
+ #
+ { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
+ && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
+ && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
+ && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+ # Now rename the file to the real destination.
+ { $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null \
+ || {
+ # The rename failed, perhaps because mv can't rename something else
+ # to itself, or perhaps because mv is so ancient that it does not
+ # support -f.
+
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ if test -f "$dst"; then
+ $doit $rmcmd -f "$dst" 2>/dev/null \
+ || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null \
+ && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }; }\
+ || {
+ echo "$0: cannot unlink or rename $dst" >&2
+ (exit 1); exit 1
+ }
+ else
+ :
+ fi
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dst"
+ }
+ } || exit 1
+
+ trap '' 0
+ fi
+done
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
--- /dev/null
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.63.
+#
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+if test "x$CONFIG_SHELL" = x; then
+ if (eval ":") 2>/dev/null; then
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+
+ if test $as_have_required = yes && (eval ":
+(as_func_return () {
+ (exit \$1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0) || { (exit 1); exit 1; }
+
+(
+ as_lineno_1=\$LINENO
+ as_lineno_2=\$LINENO
+ test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&
+ test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }
+") 2> /dev/null; then
+ :
+else
+ as_candidate_shells=
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ case $as_dir in
+ /*)
+ for as_base in sh bash ksh sh5; do
+ as_candidate_shells="$as_candidate_shells $as_dir/$as_base"
+ done;;
+ esac
+done
+IFS=$as_save_IFS
+
+
+ for as_shell in $as_candidate_shells $SHELL; do
+ # Try only shells that exist, to save several forks.
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { ("$as_shell") 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+_ASEOF
+}; then
+ CONFIG_SHELL=$as_shell
+ as_have_required=yes
+ if { "$as_shell" 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+(as_func_return () {
+ (exit $1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = "$1" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test $exitcode = 0) || { (exit 1); exit 1; }
+
+(
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }
+
+_ASEOF
+}; then
+ break
+fi
+
+fi
+
+ done
+
+ if test "x$CONFIG_SHELL" != x; then
+ for as_var in BASH_ENV ENV
+ do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+ done
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+fi
+
+
+ if test $as_have_required = no; then
+ echo This script requires a shell more modern than all the
+ echo shells that I found on your system. Please install a
+ echo modern shell, or manually run the script under such a
+ echo shell if you do have one.
+ { (exit 1); exit 1; }
+fi
+
+
+fi
+
+fi
+
+
+
+(eval "as_func_return () {
+ (exit \$1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0") || {
+ echo No shell found that supports shell functions.
+ echo Please tell bug-autoconf@gnu.org about your system,
+ echo including any error possibly output before this message.
+ echo This can help us improve future autoconf versions.
+ echo Configuration will now proceed without shell functions.
+}
+
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line after each line using $LINENO; the second 'sed'
+ # does the real work. The second script uses 'N' to pair each
+ # line-number line with the line containing $LINENO, and appends
+ # trailing '-' during substitution so that $LINENO is not a special
+ # case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # scripts with optimization help from Paolo Bonzini. Blame Lee
+ # E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+ case `echo 'x\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ *) ECHO_C='\c';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+exec 7<&0 </dev/null 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="lib/device/dev-cache.h"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_default_prefix=/usr
+ac_subst_vars='LTLIBOBJS
+usrsbindir
+usrlibdir
+udevdir
+udev_prefix
+tmpdir
+kernelvsn
+missingkernel
+kerneldir
+interface
+WRITE_INSTALL
+UDEV_SYNC
+UDEV_RULES
+UDEV_PC
+UDEV_LIBS
+TESTING
+STATIC_LINK
+STATICDIR
+SNAPSHOTS
+SELINUX_PC
+SELINUX_LIBS
+READLINE_LIBS
+PTHREAD_LIBS
+POOL
+PKGCONFIG
+REPLICATORS
+OCF
+MIRRORS
+LVM_RELEASE_DATE
+LVM_RELEASE
+LVM_PATCHLEVEL
+LVM_MINOR
+LVM_MAJOR
+LVM_LIBAPI
+LVM_VERSION
+LVM1_FALLBACK
+LVM1
+LOCALEDIR
+LIB_SUFFIX
+LDDEPS
+JOBS
+INTL_PACKAGE
+INTL
+HAVE_REALTIME
+HAVE_LIBDL
+FSADM
+DM_LIB_PATCHLEVEL
+DM_LIB_VERSION
+DM_IOCTLS
+DM_DEVICE_UID
+DM_DEVICE_MODE
+DM_DEVICE_GID
+DM_COMPAT
+DMEVENTD_PATH
+DMEVENTD
+DL_LIBS
+DEVMAPPER
+DEFAULT_RUN_DIR
+DEFAULT_LOCK_DIR
+DEFAULT_DATA_ALIGNMENT
+DEFAULT_CACHE_SUBDIR
+DEFAULT_BACKUP_SUBDIR
+DEFAULT_ARCHIVE_SUBDIR
+DEFAULT_SYS_DIR
+DEBUG
+COPTIMISE_FLAG
+CONFDIR
+CMDLIB
+CLVMD_CMANAGERS
+CLVMD
+CLUSTER
+CLDWHOLEARCHIVE
+CLDNOWHOLEARCHIVE
+CLDFLAGS
+BUILD_DMEVENTD
+BUILD_CMIRRORD
+APPLIB
+MODPROBE_CMD
+MSGFMT
+LVM2CMD_LIB
+LVM2APP_LIB
+VALGRIND
+RUBY19
+GENPNG
+GENHTML
+LCOV
+SACKPT_LIBS
+SACKPT_CFLAGS
+DLM_LIBS
+DLM_CFLAGS
+CPG_LIBS
+CPG_CFLAGS
+CONFDB_LIBS
+CONFDB_CFLAGS
+SALCK_LIBS
+SALCK_CFLAGS
+QUORUM_LIBS
+QUORUM_CFLAGS
+COROSYNC_LIBS
+COROSYNC_CFLAGS
+CMAN_LIBS
+CMAN_CFLAGS
+GULM_LIBS
+GULM_CFLAGS
+CCS_LIBS
+CCS_CFLAGS
+PKGCONFIGINIT_LIBS
+PKGCONFIGINIT_CFLAGS
+PKG_CONFIG
+POW_LIB
+LIBOBJS
+ALLOCA
+CSCOPE_CMD
+CFLOW_CMD
+RANLIB
+MKDIR_P
+SET_MAKE
+LN_S
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+EGREP
+GREP
+CPP
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+AWK
+SED
+target_os
+target_vendor
+target_cpu
+target
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_static_link
+with_user
+with_group
+with_device_uid
+with_device_gid
+with_device_mode
+enable_lvm1_fallback
+with_lvm1
+with_pool
+with_cluster
+with_snapshots
+with_mirrors
+with_replicators
+enable_readline
+enable_realtime
+enable_ocf
+with_clvmd
+with_clvmd_pidfile
+enable_cmirrord
+with_cmirrord_pidfile
+enable_debug
+with_optimisation
+enable_profiling
+enable_testing
+enable_valgrind_pool
+enable_devmapper
+enable_udev_sync
+enable_udev_rules
+enable_compat
+enable_units_compat
+enable_ioctl
+enable_o_direct
+enable_applib
+enable_cmdlib
+enable_pkgconfig
+enable_write_install
+enable_fsadm
+enable_dmeventd
+enable_selinux
+enable_nls
+with_localedir
+with_confdir
+with_staticdir
+with_usrlibdir
+with_usrsbindir
+with_udev_prefix
+with_udevdir
+with_dmeventd_pidfile
+with_dmeventd_path
+with_default_run_dir
+with_default_system_dir
+with_default_archive_subdir
+with_default_backup_subdir
+with_default_cache_subdir
+with_default_locking_dir
+with_default_data_alignment
+with_interface
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP
+PKG_CONFIG
+PKGCONFIGINIT_CFLAGS
+PKGCONFIGINIT_LIBS
+CCS_CFLAGS
+CCS_LIBS
+GULM_CFLAGS
+GULM_LIBS
+CMAN_CFLAGS
+CMAN_LIBS
+COROSYNC_CFLAGS
+COROSYNC_LIBS
+QUORUM_CFLAGS
+QUORUM_LIBS
+SALCK_CFLAGS
+SALCK_LIBS
+CONFDB_CFLAGS
+CONFDB_LIBS
+CPG_CFLAGS
+CPG_LIBS
+DLM_CFLAGS
+DLM_LIBS
+SACKPT_CFLAGS
+SACKPT_LIBS'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2
+ { (exit 1); exit 1; }; }
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2
+ { (exit 1); exit 1; }; }
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2
+ { (exit 1); exit 1; }; }
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2
+ { (exit 1); exit 1; }; }
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { $as_echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { $as_echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { $as_echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2
+ { (exit 1); exit 1; }; } ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ { $as_echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; }
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ { $as_echo "$as_me: error: working directory cannot be determined" >&2
+ { (exit 1); exit 1; }; }
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ { $as_echo "$as_me: error: pwd does not report name of working directory" >&2
+ { (exit 1); exit 1; }; }
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ { $as_echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || { $as_echo "$as_me: error: $ac_msg" >&2
+ { (exit 1); exit 1; }; }
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+ --target=TARGET configure for building compilers for TARGET [HOST]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-static_link use this to link the tools to their libraries
+ statically (default is dynamic linking
+ --enable-lvm1_fallback use this to fall back and use LVM1 binaries if
+ device-mapper is missing from the kernel
+ --disable-readline disable readline support
+ --enable-realtime enable realtime clock support
+ --enable-ocf enable Open Cluster Framework (OCF) compliant
+ resource agents
+ --enable-cmirrord enable the cluster mirror log daemon
+ --enable-debug enable debugging
+ --enable-profiling gather gcov profiling data
+ --enable-testing enable testing targets in the makefile
+ --enable-valgrind-pool enable valgrind awareness of pools
+ --disable-devmapper disable LVM2 device-mapper interaction
+ --enable-udev_sync enable synchronisation with udev processing
+ --enable-udev_rules install rule files needed for udev synchronisation
+ --enable-compat enable support for old device-mapper versions
+ --enable-units-compat enable output compatibility with old versions that
+ that do not use KiB-style unit suffixes
+ --disable-driver disable calls to device-mapper in the kernel
+ --disable-o_direct disable O_DIRECT
+ --enable-applib build application library
+ --enable-cmdlib build shared command library
+ --enable-pkgconfig install pkgconfig support
+ --enable-write_install install user writable files
+ --disable-fsadm disable fsadm
+ --enable-dmeventd enable the device-mapper event daemon
+ --disable-selinux disable selinux support
+ --enable-nls enable Native Language Support
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-user=USER set the owner of installed files [USER=]
+ --with-group=GROUP set the group owner of installed files [GROUP=]
+ --with-device-uid=UID set the owner used for new device nodes [UID=0]
+ --with-device-gid=GID set the group used for new device nodes [GID=0]
+ --with-device-mode=MODE set the mode used for new device nodes [MODE=0600]
+ --with-lvm1=TYPE LVM1 metadata support: internal/shared/none
+ [TYPE=internal]
+ --with-pool=TYPE GFS pool read-only support: internal/shared/none
+ [TYPE=internal]
+ --with-cluster=TYPE cluster LVM locking support: internal/shared/none
+ [TYPE=internal]
+ --with-snapshots=TYPE snapshot support: internal/shared/none
+ [TYPE=internal]
+ --with-mirrors=TYPE mirror support: internal/shared/none
+ [TYPE=internal]
+ --with-replicators=TYPE replicator support: internal/shared/none
+ [TYPE=none]
+ --with-clvmd=TYPE build cluster LVM Daemon
+ The following cluster manager combinations are valid:
+ * cman,gulm (RHEL4 or equivalent)
+ * cman (RHEL5 or equivalent)
+ * cman,corosync,openais (or selection of them)
+ * singlenode (localhost only)
+ * all (autodetect)
+ * none (disable build)
+ [TYPE=none]
+ --with-clvmd-pidfile=PATH
+ clvmd pidfile [/var/run/clvmd.pid]
+ --with-cmirrord-pidfile=PATH
+ cmirrord pidfile [/var/run/cmirrord.pid]
+ --with-optimisation=OPT C optimisation flag [OPT=-O2]
+ --with-localedir=DIR translation files in DIR [PREFIX/share/locale]
+ --with-confdir=DIR configuration files in DIR [/etc]
+ --with-staticdir=DIR static binaries in DIR [EPREFIX/sbin]
+ --with-usrlibdir=DIR usrlib in DIR [PREFIX/lib]
+ --with-usrsbindir=DIR usrsbin executables in DIR [PREFIX/sbin]
+ --with-udev-prefix=UPREFIX
+ install udev rule files in UPREFIX [EPREFIX]
+ --with-udevdir=DIR udev rules in DIR [UPREFIX/lib/udev/rules.d]
+ --with-dmeventd-pidfile=PATH
+ dmeventd pidfile [/var/run/dmeventd.pid]
+ --with-dmeventd-path=PATH
+ dmeventd path [EPREFIX/sbin/dmeventd]
+ --with-default-run-dir=DIR Default run directory [/var/run/lvm]
+ --with-default-system-dir=DIR
+ default LVM system directory [/etc/lvm]
+ --with-default-archive-subdir=SUBDIR
+ default metadata archive subdir [archive]
+ --with-default-backup-subdir=SUBDIR
+ default metadata backup subdir [backup]
+ --with-default-cache-subdir=SUBDIR
+ default metadata cache subdir [cache]
+ --with-default-locking-dir=DIR
+ default locking directory [/var/lock/lvm]
+ --with-default-data-alignment=NUM
+ set the default data alignment in MiB [1]
+ --with-interface=IFACE choose kernel interface (ioctl) [ioctl]
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+ PKG_CONFIG path to pkg-config utility
+ PKGCONFIGINIT_CFLAGS
+ C compiler flags for PKGCONFIGINIT, overriding pkg-config
+ PKGCONFIGINIT_LIBS
+ linker flags for PKGCONFIGINIT, overriding pkg-config
+ CCS_CFLAGS C compiler flags for CCS, overriding pkg-config
+ CCS_LIBS linker flags for CCS, overriding pkg-config
+ GULM_CFLAGS C compiler flags for GULM, overriding pkg-config
+ GULM_LIBS linker flags for GULM, overriding pkg-config
+ CMAN_CFLAGS C compiler flags for CMAN, overriding pkg-config
+ CMAN_LIBS linker flags for CMAN, overriding pkg-config
+ COROSYNC_CFLAGS
+ C compiler flags for COROSYNC, overriding pkg-config
+ COROSYNC_LIBS
+ linker flags for COROSYNC, overriding pkg-config
+ QUORUM_CFLAGS
+ C compiler flags for QUORUM, overriding pkg-config
+ QUORUM_LIBS linker flags for QUORUM, overriding pkg-config
+ SALCK_CFLAGS
+ C compiler flags for SALCK, overriding pkg-config
+ SALCK_LIBS linker flags for SALCK, overriding pkg-config
+ CONFDB_CFLAGS
+ C compiler flags for CONFDB, overriding pkg-config
+ CONFDB_LIBS linker flags for CONFDB, overriding pkg-config
+ CPG_CFLAGS C compiler flags for CPG, overriding pkg-config
+ CPG_LIBS linker flags for CPG, overriding pkg-config
+ DLM_CFLAGS C compiler flags for DLM, overriding pkg-config
+ DLM_LIBS linker flags for DLM, overriding pkg-config
+ SACKPT_CFLAGS
+ C compiler flags for SACKPT, overriding pkg-config
+ SACKPT_LIBS linker flags for SACKPT, overriding pkg-config
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+configure
+generated by GNU Autoconf 2.63
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.63. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args '$ac_arg'"
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) $as_unset $ac_var ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------------- ##
+## File substitutions. ##
+## ------------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ ac_site_file1=$CONFIG_SITE
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test -r "$ac_site_file"; then
+ { $as_echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { $as_echo "$as_me:$LINENO: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:$LINENO: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:$LINENO: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:$LINENO: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:$LINENO: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+$as_echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_config_headers="$ac_config_headers lib/misc/configure.h"
+
+
+################################################################################
+ac_aux_dir=
+for ac_dir in autoconf "$srcdir"/autoconf; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { { $as_echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in autoconf \"$srcdir\"/autoconf" >&5
+$as_echo "$as_me: error: cannot find install-sh or install.sh in autoconf \"$srcdir\"/autoconf" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+
+################################################################################
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ { { $as_echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5
+$as_echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;}
+ { (exit 1); exit 1; }; }
+
+{ $as_echo "$as_me:$LINENO: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if test "${ac_cv_build+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ { { $as_echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+$as_echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+ { (exit 1); exit 1; }; }
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5
+$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;}
+ { (exit 1); exit 1; }; }
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical build" >&5
+$as_echo "$as_me: error: invalid value of canonical build" >&2;}
+ { (exit 1); exit 1; }; };;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:$LINENO: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if test "${ac_cv_host+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5
+$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical host" >&5
+$as_echo "$as_me: error: invalid value of canonical host" >&2;}
+ { (exit 1); exit 1; }; };;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:$LINENO: checking target system type" >&5
+$as_echo_n "checking target system type... " >&6; }
+if test "${ac_cv_target+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$target_alias" = x; then
+ ac_cv_target=$ac_cv_host
+else
+ ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` ||
+ { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $target_alias failed" >&5
+$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $target_alias failed" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_target" >&5
+$as_echo "$ac_cv_target" >&6; }
+case $ac_cv_target in
+*-*-*) ;;
+*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical target" >&5
+$as_echo "$as_me: error: invalid value of canonical target" >&2;}
+ { (exit 1); exit 1; }; };;
+esac
+target=$ac_cv_target
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_target
+shift
+target_cpu=$1
+target_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+target_os=$*
+IFS=$ac_save_IFS
+case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac
+
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+test -n "$target_alias" &&
+ test "$program_prefix$program_suffix$program_transform_name" = \
+ NONENONEs,x,x, &&
+ program_prefix=${target_alias}-
+
+case "$host_os" in
+ linux*)
+ CFLAGS="$CFLAGS"
+ COPTIMISE_FLAG="-O2"
+ CLDFLAGS="$CLDFLAGS -Wl,--version-script,.export.sym"
+ CLDWHOLEARCHIVE="-Wl,-whole-archive"
+ CLDNOWHOLEARCHIVE="-Wl,-no-whole-archive"
+ LDDEPS="$LDDEPS .export.sym"
+ LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+ LIB_SUFFIX=so
+ DEVMAPPER=yes
+ ODIRECT=yes
+ DM_IOCTLS=yes
+ SELINUX=yes
+ CLUSTER=internal
+ FSADM=yes
+ ;;
+ darwin*)
+ CFLAGS="$CFLAGS -no-cpp-precomp -fno-common"
+ COPTIMISE_FLAG="-O2"
+ CLDFLAGS="$CLDFLAGS"
+ CLDWHOLEARCHIVE="-all_load"
+ CLDNOWHOLEARCHIVE=
+ LIB_SUFFIX=dylib
+ DEVMAPPER=yes
+ ODIRECT=no
+ DM_IOCTLS=no
+ SELINUX=no
+ CLUSTER=none
+ FSADM=no
+ ;;
+esac
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if test "${ac_cv_path_SED+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+ $as_unset ac_script || ac_script=
+ if test -z "$SED"; then
+ ac_path_SED_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue
+# Check for GNU ac_path_SED and select it if it is found.
+ # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+ ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo '' >> "conftest.nl"
+ "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_SED_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_SED="$ac_path_SED"
+ ac_path_SED_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_SED_found && break 3
+ done
+ done
+done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_SED"; then
+ { { $as_echo "$as_me:$LINENO: error: no acceptable sed could be found in \$PATH" >&5
+$as_echo "$as_me: error: no acceptable sed could be found in \$PATH" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+else
+ ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+ rm -f conftest.sed
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_AWK+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:$LINENO: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:$LINENO: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:$LINENO: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+{ (ac_try="$ac_compiler --version >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compiler --version >&5") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -v >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compiler -v >&5") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -V >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compiler -V >&5") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { (ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+if test -z "$ac_file"; then
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+ if { ac_try='./$ac_file'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+{ $as_echo "$as_me:$LINENO: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+{ $as_echo "$as_me:$LINENO: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+{ $as_echo "$as_me:$LINENO: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if test "${ac_cv_objext+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_compiler_gnu=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_compiler_gnu=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ CFLAGS=""
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_c89=$ac_arg
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:$LINENO: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:$LINENO: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test "${ac_cv_prog_CPP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:$LINENO: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ :
+else
+ { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if test "${ac_cv_path_GREP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ { { $as_echo "$as_me:$LINENO: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+$as_echo "$as_me: error: no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:$LINENO: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if test "${ac_cv_path_EGREP+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ { { $as_echo "$as_me:$LINENO: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+$as_echo "$as_me: error: no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+if test $ac_cv_c_compiler_gnu = yes; then
+ { $as_echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5
+$as_echo_n "checking whether $CC needs -traditional... " >&6; }
+if test "${ac_cv_prog_gcc_traditional+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_pattern="Autoconf.*'x'"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sgtty.h>
+Autoconf TIOCGETP
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "$ac_pattern" >/dev/null 2>&1; then
+ ac_cv_prog_gcc_traditional=yes
+else
+ ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+ if test $ac_cv_prog_gcc_traditional = no; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <termio.h>
+Autoconf TCGETA
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "$ac_pattern" >/dev/null 2>&1; then
+ ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5
+$as_echo "$ac_cv_prog_gcc_traditional" >&6; }
+ if test $ac_cv_prog_gcc_traditional = yes; then
+ CC="$CC -traditional"
+ fi
+fi
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+ ./ | .// | /cC/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:$LINENO: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+ if test "${ac_cv_path_mkdir+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in mkdir gmkdir; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue
+ case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+ 'mkdir (GNU coreutils) '* | \
+ 'mkdir (coreutils) '* | \
+ 'mkdir (fileutils) '4.1*)
+ ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+ break 3;;
+ esac
+ done
+ done
+done
+IFS=$as_save_IFS
+
+fi
+
+ if test "${ac_cv_path_mkdir+set}" = set; then
+ MKDIR_P="$ac_cv_path_mkdir -p"
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for MKDIR_P within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ test -d ./--version && rmdir ./--version
+ MKDIR_P="$ac_install_sh -d"
+ fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:$LINENO: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+# Extract the first word of "cflow", so it can be a program name with args.
+set dummy cflow; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_CFLOW_CMD+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $CFLOW_CMD in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_CFLOW_CMD="$CFLOW_CMD" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_CFLOW_CMD="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+CFLOW_CMD=$ac_cv_path_CFLOW_CMD
+if test -n "$CFLOW_CMD"; then
+ { $as_echo "$as_me:$LINENO: result: $CFLOW_CMD" >&5
+$as_echo "$CFLOW_CMD" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "cscope", so it can be a program name with args.
+set dummy cscope; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_CSCOPE_CMD+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $CSCOPE_CMD in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_CSCOPE_CMD="$CSCOPE_CMD" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_CSCOPE_CMD="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+CSCOPE_CMD=$ac_cv_path_CSCOPE_CMD
+if test -n "$CSCOPE_CMD"; then
+ { $as_echo "$as_me:$LINENO: result: $CSCOPE_CMD" >&5
+$as_echo "$CSCOPE_CMD" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+################################################################################
+
+
+
+
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+ as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_hdr that defines DIR" >&5
+$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main ()
+{
+if ((DIR *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ eval "$as_ac_Header=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
+_ACEOF
+
+ac_header_dirent=$ac_hdr; break
+fi
+
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+ { $as_echo "$as_me:$LINENO: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if test "${ac_cv_search_opendir+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' dir; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_search_opendir=$ac_res
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_opendir+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_opendir+set}" = set; then
+ :
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+else
+ { $as_echo "$as_me:$LINENO: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if test "${ac_cv_search_opendir+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' x; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_search_opendir=$ac_res
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_opendir+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_opendir+set}" = set; then
+ :
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_stdc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_stdc=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then
+ :
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ eval "$as_ac_Header=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+{ $as_echo "$as_me:$LINENO: checking whether sys/types.h defines makedev" >&5
+$as_echo_n "checking whether sys/types.h defines makedev... " >&6; }
+if test "${ac_cv_header_sys_types_h_makedev+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+int
+main ()
+{
+return makedev(0, 0);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_header_sys_types_h_makedev=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_sys_types_h_makedev=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_types_h_makedev" >&5
+$as_echo "$ac_cv_header_sys_types_h_makedev" >&6; }
+
+if test $ac_cv_header_sys_types_h_makedev = no; then
+if test "${ac_cv_header_sys_mkdev_h+set}" = set; then
+ { $as_echo "$as_me:$LINENO: checking for sys/mkdev.h" >&5
+$as_echo_n "checking for sys/mkdev.h... " >&6; }
+if test "${ac_cv_header_sys_mkdev_h+set}" = set; then
+ $as_echo_n "(cached) " >&6
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_mkdev_h" >&5
+$as_echo "$ac_cv_header_sys_mkdev_h" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking sys/mkdev.h usability" >&5
+$as_echo_n "checking sys/mkdev.h usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <sys/mkdev.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking sys/mkdev.h presence" >&5
+$as_echo_n "checking sys/mkdev.h presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/mkdev.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: sys/mkdev.h: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: sys/mkdev.h: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for sys/mkdev.h" >&5
+$as_echo_n "checking for sys/mkdev.h... " >&6; }
+if test "${ac_cv_header_sys_mkdev_h+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_header_sys_mkdev_h=$ac_header_preproc
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_mkdev_h" >&5
+$as_echo "$ac_cv_header_sys_mkdev_h" >&6; }
+
+fi
+if test "x$ac_cv_header_sys_mkdev_h" = x""yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define MAJOR_IN_MKDEV 1
+_ACEOF
+
+fi
+
+
+
+ if test $ac_cv_header_sys_mkdev_h = no; then
+ if test "${ac_cv_header_sys_sysmacros_h+set}" = set; then
+ { $as_echo "$as_me:$LINENO: checking for sys/sysmacros.h" >&5
+$as_echo_n "checking for sys/sysmacros.h... " >&6; }
+if test "${ac_cv_header_sys_sysmacros_h+set}" = set; then
+ $as_echo_n "(cached) " >&6
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_sysmacros_h" >&5
+$as_echo "$ac_cv_header_sys_sysmacros_h" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking sys/sysmacros.h usability" >&5
+$as_echo_n "checking sys/sysmacros.h usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <sys/sysmacros.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking sys/sysmacros.h presence" >&5
+$as_echo_n "checking sys/sysmacros.h presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/sysmacros.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: sys/sysmacros.h: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: sys/sysmacros.h: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for sys/sysmacros.h" >&5
+$as_echo_n "checking for sys/sysmacros.h... " >&6; }
+if test "${ac_cv_header_sys_sysmacros_h+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_header_sys_sysmacros_h=$ac_header_preproc
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_sysmacros_h" >&5
+$as_echo "$ac_cv_header_sys_sysmacros_h" >&6; }
+
+fi
+if test "x$ac_cv_header_sys_sysmacros_h" = x""yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define MAJOR_IN_SYSMACROS 1
+_ACEOF
+
+fi
+
+
+ fi
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_stdc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_stdc=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then
+ :
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5
+$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
+if test "${ac_cv_header_sys_wait_h+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+int
+main ()
+{
+ int s;
+ wait (&s);
+ s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_sys_wait_h=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_sys_wait_h=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5
+$as_echo "$ac_cv_header_sys_wait_h" >&6; }
+if test $ac_cv_header_sys_wait_h = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SYS_WAIT_H 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5
+$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; }
+if test "${ac_cv_header_time+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+main ()
+{
+if ((struct tm *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_time=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_time=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5
+$as_echo "$ac_cv_header_time" >&6; }
+if test $ac_cv_header_time = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define TIME_WITH_SYS_TIME 1
+_ACEOF
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+for ac_header in locale.h stddef.h syslog.h sys/file.h sys/time.h assert.h \
+ langinfo.h libgen.h signal.h sys/mman.h sys/resource.h sys/utsname.h \
+ sys/wait.h time.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+done
+
+
+case "$host_os" in
+ linux*)
+
+
+
+for ac_header in asm/byteorder.h linux/fs.h malloc.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+done
+ ;;
+ darwin*)
+
+
+for ac_header in machine/endian.h sys/disk.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+done
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+for ac_header in ctype.h dirent.h errno.h fcntl.h getopt.h inttypes.h limits.h \
+ stdarg.h stdio.h stdlib.h string.h sys/ioctl.h sys/param.h sys/stat.h \
+ sys/types.h unistd.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+done
+
+
+
+for ac_header in termios.h sys/statvfs.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5
+$as_echo_n "checking for an ANSI C-conforming const... " >&6; }
+if test "${ac_cv_c_const+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+/* FIXME: Include the comments suggested by Paul. */
+#ifndef __cplusplus
+ /* Ultrix mips cc rejects this. */
+ typedef int charset[2];
+ const charset cs;
+ /* SunOS 4.1.1 cc rejects this. */
+ char const *const *pcpcc;
+ char **ppc;
+ /* NEC SVR4.0.2 mips cc rejects this. */
+ struct point {int x, y;};
+ static struct point const zero = {0,0};
+ /* AIX XL C 1.02.0.0 rejects this.
+ It does not let you subtract one const X* pointer from another in
+ an arm of an if-expression whose if-part is not a constant
+ expression */
+ const char *g = "string";
+ pcpcc = &g + (g ? g-g : 0);
+ /* HPUX 7.0 cc rejects these. */
+ ++pcpcc;
+ ppc = (char**) pcpcc;
+ pcpcc = (char const *const *) ppc;
+ { /* SCO 3.2v4 cc rejects this. */
+ char *t;
+ char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+ *t++ = 0;
+ if (s) return 0;
+ }
+ { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
+ int x[] = {25, 17};
+ const int *foo = &x[0];
+ ++foo;
+ }
+ { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+ typedef const int *iptr;
+ iptr p = 0;
+ ++p;
+ }
+ { /* AIX XL C 1.02.0.0 rejects this saying
+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+ struct s { int j; const int *ap[3]; };
+ struct s *b; b->j = 5;
+ }
+ { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+ const int foo = 10;
+ if (!foo) return 0;
+ }
+ return !cs[0] && !zero.x;
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_c_const=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_c_const=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5
+$as_echo "$ac_cv_c_const" >&6; }
+if test $ac_cv_c_const = no; then
+
+cat >>confdefs.h <<\_ACEOF
+#define const /**/
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for inline" >&5
+$as_echo_n "checking for inline... " >&6; }
+if test "${ac_cv_c_inline+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_inline=no
+for ac_kw in inline __inline__ __inline; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifndef __cplusplus
+typedef int foo_t;
+static $ac_kw foo_t static_foo () {return 0; }
+$ac_kw foo_t foo () {return 0; }
+#endif
+
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_c_inline=$ac_kw
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_inline" != no && break
+done
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5
+$as_echo "$ac_cv_c_inline" >&6; }
+
+
+case $ac_cv_c_inline in
+ inline | yes) ;;
+ *)
+ case $ac_cv_c_inline in
+ no) ac_val=;;
+ *) ac_val=$ac_cv_c_inline;;
+ esac
+ cat >>confdefs.h <<_ACEOF
+#ifndef __cplusplus
+#define inline $ac_val
+#endif
+_ACEOF
+ ;;
+esac
+
+{ $as_echo "$as_me:$LINENO: checking for struct stat.st_rdev" >&5
+$as_echo_n "checking for struct stat.st_rdev... " >&6; }
+if test "${ac_cv_member_struct_stat_st_rdev+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static struct stat ac_aggr;
+if (ac_aggr.st_rdev)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_member_struct_stat_st_rdev=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static struct stat ac_aggr;
+if (sizeof ac_aggr.st_rdev)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_member_struct_stat_st_rdev=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_member_struct_stat_st_rdev=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_member_struct_stat_st_rdev" >&5
+$as_echo "$ac_cv_member_struct_stat_st_rdev" >&6; }
+if test "x$ac_cv_member_struct_stat_st_rdev" = x""yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_RDEV 1
+_ACEOF
+
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for off_t" >&5
+$as_echo_n "checking for off_t... " >&6; }
+if test "${ac_cv_type_off_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_type_off_t=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof (off_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof ((off_t)))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_off_t=yes
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_off_t" >&5
+$as_echo "$ac_cv_type_off_t" >&6; }
+if test "x$ac_cv_type_off_t" = x""yes; then
+ :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define off_t long int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for pid_t" >&5
+$as_echo_n "checking for pid_t... " >&6; }
+if test "${ac_cv_type_pid_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_type_pid_t=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof (pid_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof ((pid_t)))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_pid_t=yes
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5
+$as_echo "$ac_cv_type_pid_t" >&6; }
+if test "x$ac_cv_type_pid_t" = x""yes; then
+ :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define pid_t int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking return type of signal handlers" >&5
+$as_echo_n "checking return type of signal handlers... " >&6; }
+if test "${ac_cv_type_signal+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <signal.h>
+
+int
+main ()
+{
+return *(signal (0, 0)) (0) == 1;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type_signal=int
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_signal=void
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5
+$as_echo "$ac_cv_type_signal" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define RETSIGTYPE $ac_cv_type_signal
+_ACEOF
+
+
+{ $as_echo "$as_me:$LINENO: checking for size_t" >&5
+$as_echo_n "checking for size_t... " >&6; }
+if test "${ac_cv_type_size_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_type_size_t=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof (size_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof ((size_t)))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_size_t=yes
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5
+$as_echo "$ac_cv_type_size_t" >&6; }
+if test "x$ac_cv_type_size_t" = x""yes; then
+ :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define size_t unsigned int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for mode_t" >&5
+$as_echo_n "checking for mode_t... " >&6; }
+if test "${ac_cv_type_mode_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_type_mode_t=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof (mode_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof ((mode_t)))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_mode_t=yes
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_mode_t" >&5
+$as_echo "$ac_cv_type_mode_t" >&6; }
+if test "x$ac_cv_type_mode_t" = x""yes; then
+ :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define mode_t int
+_ACEOF
+
+fi
+
+
+ { $as_echo "$as_me:$LINENO: checking for int8_t" >&5
+$as_echo_n "checking for int8_t... " >&6; }
+if test "${ac_cv_c_int8_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_int8_t=no
+ for ac_type in 'int8_t' 'int' 'long int' \
+ 'long long int' 'short int' 'signed char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (8 - 2)) - 1) * 2 + 1))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (8 - 2)) - 1) * 2 + 1)
+ < ($ac_type) (((($ac_type) 1 << (8 - 2)) - 1) * 2 + 2))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ case $ac_type in
+ int8_t) ac_cv_c_int8_t=yes ;;
+ *) ac_cv_c_int8_t=$ac_type ;;
+esac
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_int8_t" != no && break
+ done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_int8_t" >&5
+$as_echo "$ac_cv_c_int8_t" >&6; }
+ case $ac_cv_c_int8_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<_ACEOF
+#define int8_t $ac_cv_c_int8_t
+_ACEOF
+;;
+ esac
+
+
+ { $as_echo "$as_me:$LINENO: checking for int16_t" >&5
+$as_echo_n "checking for int16_t... " >&6; }
+if test "${ac_cv_c_int16_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_int16_t=no
+ for ac_type in 'int16_t' 'int' 'long int' \
+ 'long long int' 'short int' 'signed char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (16 - 2)) - 1) * 2 + 1))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (16 - 2)) - 1) * 2 + 1)
+ < ($ac_type) (((($ac_type) 1 << (16 - 2)) - 1) * 2 + 2))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ case $ac_type in
+ int16_t) ac_cv_c_int16_t=yes ;;
+ *) ac_cv_c_int16_t=$ac_type ;;
+esac
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_int16_t" != no && break
+ done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_int16_t" >&5
+$as_echo "$ac_cv_c_int16_t" >&6; }
+ case $ac_cv_c_int16_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<_ACEOF
+#define int16_t $ac_cv_c_int16_t
+_ACEOF
+;;
+ esac
+
+
+ { $as_echo "$as_me:$LINENO: checking for int32_t" >&5
+$as_echo_n "checking for int32_t... " >&6; }
+if test "${ac_cv_c_int32_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_int32_t=no
+ for ac_type in 'int32_t' 'int' 'long int' \
+ 'long long int' 'short int' 'signed char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (32 - 2)) - 1) * 2 + 1))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (32 - 2)) - 1) * 2 + 1)
+ < ($ac_type) (((($ac_type) 1 << (32 - 2)) - 1) * 2 + 2))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ case $ac_type in
+ int32_t) ac_cv_c_int32_t=yes ;;
+ *) ac_cv_c_int32_t=$ac_type ;;
+esac
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_int32_t" != no && break
+ done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_int32_t" >&5
+$as_echo "$ac_cv_c_int32_t" >&6; }
+ case $ac_cv_c_int32_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<_ACEOF
+#define int32_t $ac_cv_c_int32_t
+_ACEOF
+;;
+ esac
+
+
+ { $as_echo "$as_me:$LINENO: checking for int64_t" >&5
+$as_echo_n "checking for int64_t... " >&6; }
+if test "${ac_cv_c_int64_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_int64_t=no
+ for ac_type in 'int64_t' 'int' 'long int' \
+ 'long long int' 'short int' 'signed char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (64 - 2)) - 1) * 2 + 1))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (64 - 2)) - 1) * 2 + 1)
+ < ($ac_type) (((($ac_type) 1 << (64 - 2)) - 1) * 2 + 2))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ case $ac_type in
+ int64_t) ac_cv_c_int64_t=yes ;;
+ *) ac_cv_c_int64_t=$ac_type ;;
+esac
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_int64_t" != no && break
+ done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_int64_t" >&5
+$as_echo "$ac_cv_c_int64_t" >&6; }
+ case $ac_cv_c_int64_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<_ACEOF
+#define int64_t $ac_cv_c_int64_t
+_ACEOF
+;;
+ esac
+
+{ $as_echo "$as_me:$LINENO: checking for ssize_t" >&5
+$as_echo_n "checking for ssize_t... " >&6; }
+if test "${ac_cv_type_ssize_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_type_ssize_t=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof (ssize_t))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+if (sizeof ((ssize_t)))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_ssize_t=yes
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_ssize_t" >&5
+$as_echo "$ac_cv_type_ssize_t" >&6; }
+if test "x$ac_cv_type_ssize_t" = x""yes; then
+ :
+else
+
+cat >>confdefs.h <<_ACEOF
+#define ssize_t int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5
+$as_echo_n "checking for uid_t in sys/types.h... " >&6; }
+if test "${ac_cv_type_uid_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "uid_t" >/dev/null 2>&1; then
+ ac_cv_type_uid_t=yes
+else
+ ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_type_uid_t" >&5
+$as_echo "$ac_cv_type_uid_t" >&6; }
+if test $ac_cv_type_uid_t = no; then
+
+cat >>confdefs.h <<\_ACEOF
+#define uid_t int
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define gid_t int
+_ACEOF
+
+fi
+
+
+ { $as_echo "$as_me:$LINENO: checking for uint8_t" >&5
+$as_echo_n "checking for uint8_t... " >&6; }
+if test "${ac_cv_c_uint8_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_uint8_t=no
+ for ac_type in 'uint8_t' 'unsigned int' 'unsigned long int' \
+ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) -1 >> (8 - 1) == 1)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ case $ac_type in
+ uint8_t) ac_cv_c_uint8_t=yes ;;
+ *) ac_cv_c_uint8_t=$ac_type ;;
+esac
+
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_uint8_t" != no && break
+ done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_uint8_t" >&5
+$as_echo "$ac_cv_c_uint8_t" >&6; }
+ case $ac_cv_c_uint8_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<\_ACEOF
+#define _UINT8_T 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint8_t $ac_cv_c_uint8_t
+_ACEOF
+;;
+ esac
+
+
+ { $as_echo "$as_me:$LINENO: checking for uint16_t" >&5
+$as_echo_n "checking for uint16_t... " >&6; }
+if test "${ac_cv_c_uint16_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_uint16_t=no
+ for ac_type in 'uint16_t' 'unsigned int' 'unsigned long int' \
+ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) -1 >> (16 - 1) == 1)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ case $ac_type in
+ uint16_t) ac_cv_c_uint16_t=yes ;;
+ *) ac_cv_c_uint16_t=$ac_type ;;
+esac
+
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_uint16_t" != no && break
+ done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_uint16_t" >&5
+$as_echo "$ac_cv_c_uint16_t" >&6; }
+ case $ac_cv_c_uint16_t in #(
+ no|yes) ;; #(
+ *)
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint16_t $ac_cv_c_uint16_t
+_ACEOF
+;;
+ esac
+
+
+ { $as_echo "$as_me:$LINENO: checking for uint32_t" >&5
+$as_echo_n "checking for uint32_t... " >&6; }
+if test "${ac_cv_c_uint32_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_uint32_t=no
+ for ac_type in 'uint32_t' 'unsigned int' 'unsigned long int' \
+ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) -1 >> (32 - 1) == 1)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ case $ac_type in
+ uint32_t) ac_cv_c_uint32_t=yes ;;
+ *) ac_cv_c_uint32_t=$ac_type ;;
+esac
+
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_uint32_t" != no && break
+ done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_uint32_t" >&5
+$as_echo "$ac_cv_c_uint32_t" >&6; }
+ case $ac_cv_c_uint32_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<\_ACEOF
+#define _UINT32_T 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint32_t $ac_cv_c_uint32_t
+_ACEOF
+;;
+ esac
+
+
+ { $as_echo "$as_me:$LINENO: checking for uint64_t" >&5
+$as_echo_n "checking for uint64_t... " >&6; }
+if test "${ac_cv_c_uint64_t+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_uint64_t=no
+ for ac_type in 'uint64_t' 'unsigned int' 'unsigned long int' \
+ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) -1 >> (64 - 1) == 1)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ case $ac_type in
+ uint64_t) ac_cv_c_uint64_t=yes ;;
+ *) ac_cv_c_uint64_t=$ac_type ;;
+esac
+
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_uint64_t" != no && break
+ done
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_uint64_t" >&5
+$as_echo "$ac_cv_c_uint64_t" >&6; }
+ case $ac_cv_c_uint64_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<\_ACEOF
+#define _UINT64_T 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint64_t $ac_cv_c_uint64_t
+_ACEOF
+;;
+ esac
+
+{ $as_echo "$as_me:$LINENO: checking for struct stat.st_rdev" >&5
+$as_echo_n "checking for struct stat.st_rdev... " >&6; }
+if test "${ac_cv_member_struct_stat_st_rdev+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static struct stat ac_aggr;
+if (ac_aggr.st_rdev)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_member_struct_stat_st_rdev=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static struct stat ac_aggr;
+if (sizeof ac_aggr.st_rdev)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_member_struct_stat_st_rdev=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_member_struct_stat_st_rdev=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_member_struct_stat_st_rdev" >&5
+$as_echo "$ac_cv_member_struct_stat_st_rdev" >&6; }
+if test "x$ac_cv_member_struct_stat_st_rdev" = x""yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_RDEV 1
+_ACEOF
+
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5
+$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; }
+if test "${ac_cv_struct_tm+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <time.h>
+
+int
+main ()
+{
+struct tm tm;
+ int *p = &tm.tm_sec;
+ return !p;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_struct_tm=time.h
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_struct_tm=sys/time.h
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5
+$as_echo "$ac_cv_struct_tm" >&6; }
+if test $ac_cv_struct_tm = sys/time.h; then
+
+cat >>confdefs.h <<\_ACEOF
+#define TM_IN_SYS_TIME 1
+_ACEOF
+
+fi
+
+
+################################################################################
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+for ac_func in ftruncate gethostname getpagesize \
+ gettimeofday memset mkdir mkfifo rmdir munmap nl_langinfo setenv setlocale \
+ strcasecmp strchr strcspn strspn strdup strncasecmp strerror strrchr \
+ strstr strtol strtoul uname
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ eval "$as_ac_var=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+done
+
+
+for ac_func in siginterrupt
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ eval "$as_ac_var=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works
+# for constant arguments. Useless!
+{ $as_echo "$as_me:$LINENO: checking for working alloca.h" >&5
+$as_echo_n "checking for working alloca.h... " >&6; }
+if test "${ac_cv_working_alloca_h+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <alloca.h>
+int
+main ()
+{
+char *p = (char *) alloca (2 * sizeof (int));
+ if (p) return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_working_alloca_h=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_working_alloca_h=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_working_alloca_h" >&5
+$as_echo "$ac_cv_working_alloca_h" >&6; }
+if test $ac_cv_working_alloca_h = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ALLOCA_H 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for alloca" >&5
+$as_echo_n "checking for alloca... " >&6; }
+if test "${ac_cv_func_alloca_works+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __GNUC__
+# define alloca __builtin_alloca
+#else
+# ifdef _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+# else
+# ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+ #pragma alloca
+# else
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+# endif
+# endif
+# endif
+# endif
+#endif
+
+int
+main ()
+{
+char *p = (char *) alloca (1);
+ if (p) return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_func_alloca_works=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_func_alloca_works=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_alloca_works" >&5
+$as_echo "$ac_cv_func_alloca_works" >&6; }
+
+if test $ac_cv_func_alloca_works = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ALLOCA 1
+_ACEOF
+
+else
+ # The SVR3 libPW and SVR4 libucb both contain incompatible functions
+# that cause trouble. Some versions do not even contain alloca or
+# contain a buggy version. If you still want to use their alloca,
+# use ar to extract alloca.o from them instead of compiling alloca.c.
+
+ALLOCA=\${LIBOBJDIR}alloca.$ac_objext
+
+cat >>confdefs.h <<\_ACEOF
+#define C_ALLOCA 1
+_ACEOF
+
+
+{ $as_echo "$as_me:$LINENO: checking whether \`alloca.c' needs Cray hooks" >&5
+$as_echo_n "checking whether \`alloca.c' needs Cray hooks... " >&6; }
+if test "${ac_cv_os_cray+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#if defined CRAY && ! defined CRAY2
+webecray
+#else
+wenotbecray
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "webecray" >/dev/null 2>&1; then
+ ac_cv_os_cray=yes
+else
+ ac_cv_os_cray=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_os_cray" >&5
+$as_echo "$ac_cv_os_cray" >&6; }
+if test $ac_cv_os_cray = yes; then
+ for ac_func in _getb67 GETB67 getb67; do
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ eval "$as_ac_var=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define CRAY_STACKSEG_END $ac_func
+_ACEOF
+
+ break
+fi
+
+ done
+fi
+
+{ $as_echo "$as_me:$LINENO: checking stack direction for C alloca" >&5
+$as_echo_n "checking stack direction for C alloca... " >&6; }
+if test "${ac_cv_c_stack_direction+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_c_stack_direction=0
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+find_stack_direction ()
+{
+ static char *addr = 0;
+ auto char dummy;
+ if (addr == 0)
+ {
+ addr = &dummy;
+ return find_stack_direction ();
+ }
+ else
+ return (&dummy > addr) ? 1 : -1;
+}
+
+int
+main ()
+{
+ return find_stack_direction () < 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_c_stack_direction=1
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_stack_direction=-1
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_stack_direction" >&5
+$as_echo "$ac_cv_c_stack_direction" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define STACK_DIRECTION $ac_cv_c_stack_direction
+_ACEOF
+
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether closedir returns void" >&5
+$as_echo_n "checking whether closedir returns void... " >&6; }
+if test "${ac_cv_func_closedir_void+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_closedir_void=yes
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header_dirent>
+#ifndef __cplusplus
+int closedir ();
+#endif
+
+int
+main ()
+{
+return closedir (opendir (".")) != 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_closedir_void=no
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_closedir_void=yes
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_closedir_void" >&5
+$as_echo "$ac_cv_func_closedir_void" >&6; }
+if test $ac_cv_func_closedir_void = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define CLOSEDIR_VOID 1
+_ACEOF
+
+fi
+
+
+for ac_header in unistd.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:$LINENO: checking for working chown" >&5
+$as_echo_n "checking for working chown... " >&6; }
+if test "${ac_cv_func_chown_works+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_chown_works=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <fcntl.h>
+
+int
+main ()
+{
+ char *f = "conftest.chown";
+ struct stat before, after;
+
+ if (creat (f, 0600) < 0)
+ return 1;
+ if (stat (f, &before) < 0)
+ return 1;
+ if (chown (f, (uid_t) -1, (gid_t) -1) == -1)
+ return 1;
+ if (stat (f, &after) < 0)
+ return 1;
+ return ! (before.st_uid == after.st_uid && before.st_gid == after.st_gid);
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_chown_works=yes
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_chown_works=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+rm -f conftest.chown
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_chown_works" >&5
+$as_echo "$ac_cv_func_chown_works" >&6; }
+if test $ac_cv_func_chown_works = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_CHOWN 1
+_ACEOF
+
+fi
+
+
+for ac_header in vfork.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+for ac_func in fork vfork
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ eval "$as_ac_var=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+if test "x$ac_cv_func_fork" = xyes; then
+ { $as_echo "$as_me:$LINENO: checking for working fork" >&5
+$as_echo_n "checking for working fork... " >&6; }
+if test "${ac_cv_func_fork_works+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_fork_works=cross
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* By Ruediger Kuhlmann. */
+ return fork () < 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_fork_works=yes
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_fork_works=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5
+$as_echo "$ac_cv_func_fork_works" >&6; }
+
+else
+ ac_cv_func_fork_works=$ac_cv_func_fork
+fi
+if test "x$ac_cv_func_fork_works" = xcross; then
+ case $host in
+ *-*-amigaos* | *-*-msdosdjgpp*)
+ # Override, as these systems have only a dummy fork() stub
+ ac_cv_func_fork_works=no
+ ;;
+ *)
+ ac_cv_func_fork_works=yes
+ ;;
+ esac
+ { $as_echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;}
+fi
+ac_cv_func_vfork_works=$ac_cv_func_vfork
+if test "x$ac_cv_func_vfork" = xyes; then
+ { $as_echo "$as_me:$LINENO: checking for working vfork" >&5
+$as_echo_n "checking for working vfork... " >&6; }
+if test "${ac_cv_func_vfork_works+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_vfork_works=cross
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Thanks to Paul Eggert for this test. */
+$ac_includes_default
+#include <sys/wait.h>
+#ifdef HAVE_VFORK_H
+# include <vfork.h>
+#endif
+/* On some sparc systems, changes by the child to local and incoming
+ argument registers are propagated back to the parent. The compiler
+ is told about this with #include <vfork.h>, but some compilers
+ (e.g. gcc -O) don't grok <vfork.h>. Test for this by using a
+ static variable whose address is put into a register that is
+ clobbered by the vfork. */
+static void
+#ifdef __cplusplus
+sparc_address_test (int arg)
+# else
+sparc_address_test (arg) int arg;
+#endif
+{
+ static pid_t child;
+ if (!child) {
+ child = vfork ();
+ if (child < 0) {
+ perror ("vfork");
+ _exit(2);
+ }
+ if (!child) {
+ arg = getpid();
+ write(-1, "", 0);
+ _exit (arg);
+ }
+ }
+}
+
+int
+main ()
+{
+ pid_t parent = getpid ();
+ pid_t child;
+
+ sparc_address_test (0);
+
+ child = vfork ();
+
+ if (child == 0) {
+ /* Here is another test for sparc vfork register problems. This
+ test uses lots of local variables, at least as many local
+ variables as main has allocated so far including compiler
+ temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris
+ 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should
+ reuse the register of parent for one of the local variables,
+ since it will think that parent can't possibly be used any more
+ in this routine. Assigning to the local variable will thus
+ munge parent in the parent process. */
+ pid_t
+ p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(),
+ p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid();
+ /* Convince the compiler that p..p7 are live; otherwise, it might
+ use the same hardware register for all 8 local variables. */
+ if (p != p1 || p != p2 || p != p3 || p != p4
+ || p != p5 || p != p6 || p != p7)
+ _exit(1);
+
+ /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent
+ from child file descriptors. If the child closes a descriptor
+ before it execs or exits, this munges the parent's descriptor
+ as well. Test for this by closing stdout in the child. */
+ _exit(close(fileno(stdout)) != 0);
+ } else {
+ int status;
+ struct stat st;
+
+ while (wait(&status) != child)
+ ;
+ return (
+ /* Was there some problem with vforking? */
+ child < 0
+
+ /* Did the child fail? (This shouldn't happen.) */
+ || status
+
+ /* Did the vfork/compiler bug occur? */
+ || parent != getpid()
+
+ /* Did the file descriptor bug occur? */
+ || fstat(fileno(stdout), &st) != 0
+ );
+ }
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_vfork_works=yes
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_vfork_works=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5
+$as_echo "$ac_cv_func_vfork_works" >&6; }
+
+fi;
+if test "x$ac_cv_func_fork_works" = xcross; then
+ ac_cv_func_vfork_works=$ac_cv_func_vfork
+ { $as_echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;}
+fi
+
+if test "x$ac_cv_func_vfork_works" = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_WORKING_VFORK 1
+_ACEOF
+
+else
+
+cat >>confdefs.h <<\_ACEOF
+#define vfork fork
+_ACEOF
+
+fi
+if test "x$ac_cv_func_fork_works" = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_WORKING_FORK 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5
+$as_echo_n "checking whether lstat dereferences a symlink specified with a trailing slash... " >&6; }
+if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ rm -f conftest.sym conftest.file
+echo >conftest.file
+if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_lstat_dereferences_slashed_symlink=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+struct stat sbuf;
+ /* Linux will dereference the symlink and fail.
+ That is better in the sense that it means we will not
+ have to compile and use the lstat wrapper. */
+ return lstat ("conftest.sym/", &sbuf) == 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_lstat_dereferences_slashed_symlink=yes
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_lstat_dereferences_slashed_symlink=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+else
+ # If the `ln -s' command failed, then we probably don't even
+ # have an lstat function.
+ ac_cv_func_lstat_dereferences_slashed_symlink=no
+fi
+rm -f conftest.sym conftest.file
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5
+$as_echo "$ac_cv_func_lstat_dereferences_slashed_symlink" >&6; }
+
+test $ac_cv_func_lstat_dereferences_slashed_symlink = yes &&
+
+cat >>confdefs.h <<_ACEOF
+#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1
+_ACEOF
+
+
+if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then
+ case " $LIBOBJS " in
+ *" lstat.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS lstat.$ac_objext"
+ ;;
+esac
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether lstat accepts an empty string" >&5
+$as_echo_n "checking whether lstat accepts an empty string... " >&6; }
+if test "${ac_cv_func_lstat_empty_string_bug+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_lstat_empty_string_bug=yes
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+struct stat sbuf;
+ return lstat ("", &sbuf) == 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_lstat_empty_string_bug=no
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_lstat_empty_string_bug=yes
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_lstat_empty_string_bug" >&5
+$as_echo "$ac_cv_func_lstat_empty_string_bug" >&6; }
+if test $ac_cv_func_lstat_empty_string_bug = yes; then
+ case " $LIBOBJS " in
+ *" lstat.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS lstat.$ac_objext"
+ ;;
+esac
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LSTAT_EMPTY_STRING_BUG 1
+_ACEOF
+
+fi
+
+
+for ac_header in stdlib.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5
+$as_echo_n "checking for GNU libc compatible malloc... " >&6; }
+if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_malloc_0_nonnull=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#if defined STDC_HEADERS || defined HAVE_STDLIB_H
+# include <stdlib.h>
+#else
+char *malloc ();
+#endif
+
+int
+main ()
+{
+return ! malloc (0);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_malloc_0_nonnull=yes
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_malloc_0_nonnull=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5
+$as_echo "$ac_cv_func_malloc_0_nonnull" >&6; }
+if test $ac_cv_func_malloc_0_nonnull = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_MALLOC 1
+_ACEOF
+
+else
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_MALLOC 0
+_ACEOF
+
+ case " $LIBOBJS " in
+ *" malloc.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS malloc.$ac_objext"
+ ;;
+esac
+
+
+cat >>confdefs.h <<\_ACEOF
+#define malloc rpl_malloc
+_ACEOF
+
+fi
+
+
+
+{ $as_echo "$as_me:$LINENO: checking for working memcmp" >&5
+$as_echo_n "checking for working memcmp... " >&6; }
+if test "${ac_cv_func_memcmp_working+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_memcmp_working=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* Some versions of memcmp are not 8-bit clean. */
+ char c0 = '\100', c1 = '\200', c2 = '\201';
+ if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0)
+ return 1;
+
+ /* The Next x86 OpenStep bug shows up only when comparing 16 bytes
+ or more and with at least one buffer not starting on a 4-byte boundary.
+ William Lewis provided this test program. */
+ {
+ char foo[21];
+ char bar[21];
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ char *a = foo + i;
+ char *b = bar + i;
+ strcpy (a, "--------01111111");
+ strcpy (b, "--------10000000");
+ if (memcmp (a, b, 16) >= 0)
+ return 1;
+ }
+ return 0;
+ }
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_memcmp_working=yes
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_memcmp_working=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5
+$as_echo "$ac_cv_func_memcmp_working" >&6; }
+test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in
+ *" memcmp.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS memcmp.$ac_objext"
+ ;;
+esac
+
+
+
+
+for ac_header in stdlib.h unistd.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_func in getpagesize
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ eval "$as_ac_var=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+{ $as_echo "$as_me:$LINENO: checking for working mmap" >&5
+$as_echo_n "checking for working mmap... " >&6; }
+if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_mmap_fixed_mapped=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+/* malloc might have been renamed as rpl_malloc. */
+#undef malloc
+
+/* Thanks to Mike Haertel and Jim Avera for this test.
+ Here is a matrix of mmap possibilities:
+ mmap private not fixed
+ mmap private fixed at somewhere currently unmapped
+ mmap private fixed at somewhere already mapped
+ mmap shared not fixed
+ mmap shared fixed at somewhere currently unmapped
+ mmap shared fixed at somewhere already mapped
+ For private mappings, we should verify that changes cannot be read()
+ back from the file, nor mmap's back from the file at a different
+ address. (There have been systems where private was not correctly
+ implemented like the infamous i386 svr4.0, and systems where the
+ VM page cache was not coherent with the file system buffer cache
+ like early versions of FreeBSD and possibly contemporary NetBSD.)
+ For shared mappings, we should conversely verify that changes get
+ propagated back to all the places they're supposed to be.
+
+ Grep wants private fixed already mapped.
+ The main things grep needs to know about mmap are:
+ * does it exist and is it safe to write into the mmap'd area
+ * how to use it (BSD variants) */
+
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#if !defined STDC_HEADERS && !defined HAVE_STDLIB_H
+char *malloc ();
+#endif
+
+/* This mess was copied from the GNU getpagesize.h. */
+#ifndef HAVE_GETPAGESIZE
+/* Assume that all systems that can run configure have sys/param.h. */
+# ifndef HAVE_SYS_PARAM_H
+# define HAVE_SYS_PARAM_H 1
+# endif
+
+# ifdef _SC_PAGESIZE
+# define getpagesize() sysconf(_SC_PAGESIZE)
+# else /* no _SC_PAGESIZE */
+# ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+# ifdef EXEC_PAGESIZE
+# define getpagesize() EXEC_PAGESIZE
+# else /* no EXEC_PAGESIZE */
+# ifdef NBPG
+# define getpagesize() NBPG * CLSIZE
+# ifndef CLSIZE
+# define CLSIZE 1
+# endif /* no CLSIZE */
+# else /* no NBPG */
+# ifdef NBPC
+# define getpagesize() NBPC
+# else /* no NBPC */
+# ifdef PAGESIZE
+# define getpagesize() PAGESIZE
+# endif /* PAGESIZE */
+# endif /* no NBPC */
+# endif /* no NBPG */
+# endif /* no EXEC_PAGESIZE */
+# else /* no HAVE_SYS_PARAM_H */
+# define getpagesize() 8192 /* punt totally */
+# endif /* no HAVE_SYS_PARAM_H */
+# endif /* no _SC_PAGESIZE */
+
+#endif /* no HAVE_GETPAGESIZE */
+
+int
+main ()
+{
+ char *data, *data2, *data3;
+ int i, pagesize;
+ int fd;
+
+ pagesize = getpagesize ();
+
+ /* First, make a file with some known garbage in it. */
+ data = (char *) malloc (pagesize);
+ if (!data)
+ return 1;
+ for (i = 0; i < pagesize; ++i)
+ *(data + i) = rand ();
+ umask (0);
+ fd = creat ("conftest.mmap", 0600);
+ if (fd < 0)
+ return 1;
+ if (write (fd, data, pagesize) != pagesize)
+ return 1;
+ close (fd);
+
+ /* Next, try to mmap the file at a fixed address which already has
+ something else allocated at it. If we can, also make sure that
+ we see the same garbage. */
+ fd = open ("conftest.mmap", O_RDWR);
+ if (fd < 0)
+ return 1;
+ data2 = (char *) malloc (2 * pagesize);
+ if (!data2)
+ return 1;
+ data2 += (pagesize - ((long int) data2 & (pagesize - 1))) & (pagesize - 1);
+ if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_FIXED, fd, 0L))
+ return 1;
+ for (i = 0; i < pagesize; ++i)
+ if (*(data + i) != *(data2 + i))
+ return 1;
+
+ /* Finally, make sure that changes to the mapped area do not
+ percolate back to the file as seen by read(). (This is a bug on
+ some variants of i386 svr4.0.) */
+ for (i = 0; i < pagesize; ++i)
+ *(data2 + i) = *(data2 + i) + 1;
+ data3 = (char *) malloc (pagesize);
+ if (!data3)
+ return 1;
+ if (read (fd, data3, pagesize) != pagesize)
+ return 1;
+ for (i = 0; i < pagesize; ++i)
+ if (*(data + i) != *(data3 + i))
+ return 1;
+ close (fd);
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_mmap_fixed_mapped=yes
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_mmap_fixed_mapped=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_mmap_fixed_mapped" >&5
+$as_echo "$ac_cv_func_mmap_fixed_mapped" >&6; }
+if test $ac_cv_func_mmap_fixed_mapped = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_MMAP 1
+_ACEOF
+
+fi
+rm -f conftest.mmap
+
+
+for ac_header in stdlib.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:$LINENO: checking for GNU libc compatible realloc" >&5
+$as_echo_n "checking for GNU libc compatible realloc... " >&6; }
+if test "${ac_cv_func_realloc_0_nonnull+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_realloc_0_nonnull=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#if defined STDC_HEADERS || defined HAVE_STDLIB_H
+# include <stdlib.h>
+#else
+char *realloc ();
+#endif
+
+int
+main ()
+{
+return ! realloc (0, 0);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_realloc_0_nonnull=yes
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_realloc_0_nonnull=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_realloc_0_nonnull" >&5
+$as_echo "$ac_cv_func_realloc_0_nonnull" >&6; }
+if test $ac_cv_func_realloc_0_nonnull = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_REALLOC 1
+_ACEOF
+
+else
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_REALLOC 0
+_ACEOF
+
+ case " $LIBOBJS " in
+ *" realloc.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS realloc.$ac_objext"
+ ;;
+esac
+
+
+cat >>confdefs.h <<\_ACEOF
+#define realloc rpl_realloc
+_ACEOF
+
+fi
+
+
+
+{ $as_echo "$as_me:$LINENO: checking whether stat accepts an empty string" >&5
+$as_echo_n "checking whether stat accepts an empty string... " >&6; }
+if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_stat_empty_string_bug=yes
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+struct stat sbuf;
+ return stat ("", &sbuf) == 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_stat_empty_string_bug=no
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_stat_empty_string_bug=yes
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_stat_empty_string_bug" >&5
+$as_echo "$ac_cv_func_stat_empty_string_bug" >&6; }
+if test $ac_cv_func_stat_empty_string_bug = yes; then
+ case " $LIBOBJS " in
+ *" stat.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS stat.$ac_objext"
+ ;;
+esac
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STAT_EMPTY_STRING_BUG 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking for working strtod" >&5
+$as_echo_n "checking for working strtod... " >&6; }
+if test "${ac_cv_func_strtod+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_strtod=no
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+$ac_includes_default
+#ifndef strtod
+double strtod ();
+#endif
+int
+main()
+{
+ {
+ /* Some versions of Linux strtod mis-parse strings with leading '+'. */
+ char *string = " +69";
+ char *term;
+ double value;
+ value = strtod (string, &term);
+ if (value != 69 || term != (string + 4))
+ return 1;
+ }
+
+ {
+ /* Under Solaris 2.4, strtod returns the wrong value for the
+ terminating character under some conditions. */
+ char *string = "NaN";
+ char *term;
+ strtod (string, &term);
+ if (term != string && *(term - 1) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_func_strtod=yes
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_func_strtod=no
+fi
+rm -rf conftest.dSYM
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_strtod" >&5
+$as_echo "$ac_cv_func_strtod" >&6; }
+if test $ac_cv_func_strtod = no; then
+ case " $LIBOBJS " in
+ *" strtod.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS strtod.$ac_objext"
+ ;;
+esac
+
+{ $as_echo "$as_me:$LINENO: checking for pow" >&5
+$as_echo_n "checking for pow... " >&6; }
+if test "${ac_cv_func_pow+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define pow to an innocuous variant, in case <limits.h> declares pow.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define pow innocuous_pow
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char pow (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef pow
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pow ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_pow || defined __stub___pow
+choke me
+#endif
+
+int
+main ()
+{
+return pow ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_func_pow=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_func_pow=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_pow" >&5
+$as_echo "$ac_cv_func_pow" >&6; }
+
+if test $ac_cv_func_pow = no; then
+ { $as_echo "$as_me:$LINENO: checking for pow in -lm" >&5
+$as_echo_n "checking for pow in -lm... " >&6; }
+if test "${ac_cv_lib_m_pow+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pow ();
+int
+main ()
+{
+return pow ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_m_pow=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_m_pow=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_m_pow" >&5
+$as_echo "$ac_cv_lib_m_pow" >&6; }
+if test "x$ac_cv_lib_m_pow" = x""yes; then
+ POW_LIB=-lm
+else
+ { $as_echo "$as_me:$LINENO: WARNING: cannot find library containing definition of pow" >&5
+$as_echo "$as_me: WARNING: cannot find library containing definition of pow" >&2;}
+fi
+
+fi
+
+fi
+
+
+for ac_func in vprintf
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ eval "$as_ac_var=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+{ $as_echo "$as_me:$LINENO: checking for _doprnt" >&5
+$as_echo_n "checking for _doprnt... " >&6; }
+if test "${ac_cv_func__doprnt+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define _doprnt to an innocuous variant, in case <limits.h> declares _doprnt.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define _doprnt innocuous__doprnt
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char _doprnt (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef _doprnt
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char _doprnt ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub__doprnt || defined __stub____doprnt
+choke me
+#endif
+
+int
+main ()
+{
+return _doprnt ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_func__doprnt=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_func__doprnt=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5
+$as_echo "$ac_cv_func__doprnt" >&6; }
+if test "x$ac_cv_func__doprnt" = x""yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_DOPRNT 1
+_ACEOF
+
+fi
+
+fi
+done
+
+
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to use static linking" >&5
+$as_echo_n "checking whether to use static linking... " >&6; }
+# Check whether --enable-static_link was given.
+if test "${enable_static_link+set}" = set; then
+ enableval=$enable_static_link; STATIC_LINK=$enableval
+else
+ STATIC_LINK=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $STATIC_LINK" >&5
+$as_echo "$STATIC_LINK" >&6; }
+
+################################################################################
+
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking file owner" >&5
+$as_echo_n "checking file owner... " >&6; }
+
+# Check whether --with-user was given.
+if test "${with_user+set}" = set; then
+ withval=$with_user; OWNER=$withval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $OWNER" >&5
+$as_echo "$OWNER" >&6; }
+
+if test x$OWNER != x; then
+ INSTALL="$INSTALL -o $OWNER"
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking group owner" >&5
+$as_echo_n "checking group owner... " >&6; }
+
+# Check whether --with-group was given.
+if test "${with_group+set}" = set; then
+ withval=$with_group; GROUP=$withval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $GROUP" >&5
+$as_echo "$GROUP" >&6; }
+
+if test x$GROUP != x; then
+ INSTALL="$INSTALL -g $GROUP"
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking device node uid" >&5
+$as_echo_n "checking device node uid... " >&6; }
+
+
+# Check whether --with-device-uid was given.
+if test "${with_device_uid+set}" = set; then
+ withval=$with_device_uid; DM_DEVICE_UID=$withval
+else
+ DM_DEVICE_UID=0
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $DM_DEVICE_UID" >&5
+$as_echo "$DM_DEVICE_UID" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking device node gid" >&5
+$as_echo_n "checking device node gid... " >&6; }
+
+
+# Check whether --with-device-gid was given.
+if test "${with_device_gid+set}" = set; then
+ withval=$with_device_gid; DM_DEVICE_GID=$withval
+else
+ DM_DEVICE_GID=0
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $DM_DEVICE_GID" >&5
+$as_echo "$DM_DEVICE_GID" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking device node mode" >&5
+$as_echo_n "checking device node mode... " >&6; }
+
+
+# Check whether --with-device-mode was given.
+if test "${with_device_mode+set}" = set; then
+ withval=$with_device_mode; DM_DEVICE_MODE=$withval
+else
+ DM_DEVICE_MODE=0600
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $DM_DEVICE_MODE" >&5
+$as_echo "$DM_DEVICE_MODE" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable lvm1 fallback" >&5
+$as_echo_n "checking whether to enable lvm1 fallback... " >&6; }
+# Check whether --enable-lvm1_fallback was given.
+if test "${enable_lvm1_fallback+set}" = set; then
+ enableval=$enable_lvm1_fallback; LVM1_FALLBACK=$enableval
+else
+ LVM1_FALLBACK=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $LVM1_FALLBACK" >&5
+$as_echo "$LVM1_FALLBACK" >&6; }
+
+if test x$LVM1_FALLBACK = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define LVM1_FALLBACK 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to include support for lvm1 metadata" >&5
+$as_echo_n "checking whether to include support for lvm1 metadata... " >&6; }
+
+# Check whether --with-lvm1 was given.
+if test "${with_lvm1+set}" = set; then
+ withval=$with_lvm1; LVM1=$withval
+else
+ LVM1=internal
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $LVM1" >&5
+$as_echo "$LVM1" >&6; }
+
+if [ "x$LVM1" != xnone -a "x$LVM1" != xinternal -a "x$LVM1" != xshared ];
+ then { { $as_echo "$as_me:$LINENO: error: --with-lvm1 parameter invalid
+" >&5
+$as_echo "$as_me: error: --with-lvm1 parameter invalid
+" >&2;}
+ { (exit 1); exit 1; }; }
+fi;
+
+if test x$LVM1 = xinternal; then
+
+cat >>confdefs.h <<\_ACEOF
+#define LVM1_INTERNAL 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to include support for GFS pool metadata" >&5
+$as_echo_n "checking whether to include support for GFS pool metadata... " >&6; }
+
+# Check whether --with-pool was given.
+if test "${with_pool+set}" = set; then
+ withval=$with_pool; POOL=$withval
+else
+ POOL=internal
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $POOL" >&5
+$as_echo "$POOL" >&6; }
+
+if [ "x$POOL" != xnone -a "x$POOL" != xinternal -a "x$POOL" != xshared ];
+ then { { $as_echo "$as_me:$LINENO: error: --with-pool parameter invalid
+" >&5
+$as_echo "$as_me: error: --with-pool parameter invalid
+" >&2;}
+ { (exit 1); exit 1; }; }
+fi;
+
+if test x$POOL = xinternal; then
+
+cat >>confdefs.h <<\_ACEOF
+#define POOL_INTERNAL 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to include support for cluster locking" >&5
+$as_echo_n "checking whether to include support for cluster locking... " >&6; }
+
+# Check whether --with-cluster was given.
+if test "${with_cluster+set}" = set; then
+ withval=$with_cluster; CLUSTER=$withval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $CLUSTER" >&5
+$as_echo "$CLUSTER" >&6; }
+
+if [ "x$CLUSTER" != xnone -a "x$CLUSTER" != xinternal -a "x$CLUSTER" != xshared ];
+ then { { $as_echo "$as_me:$LINENO: error: --with-cluster parameter invalid
+" >&5
+$as_echo "$as_me: error: --with-cluster parameter invalid
+" >&2;}
+ { (exit 1); exit 1; }; }
+fi;
+
+if test x$CLUSTER = xinternal; then
+
+cat >>confdefs.h <<\_ACEOF
+#define CLUSTER_LOCKING_INTERNAL 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to include snapshots" >&5
+$as_echo_n "checking whether to include snapshots... " >&6; }
+
+# Check whether --with-snapshots was given.
+if test "${with_snapshots+set}" = set; then
+ withval=$with_snapshots; SNAPSHOTS=$withval
+else
+ SNAPSHOTS=internal
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $SNAPSHOTS" >&5
+$as_echo "$SNAPSHOTS" >&6; }
+
+if [ "x$SNAPSHOTS" != xnone -a "x$SNAPSHOTS" != xinternal -a "x$SNAPSHOTS" != xshared ];
+ then { { $as_echo "$as_me:$LINENO: error: --with-snapshots parameter invalid
+" >&5
+$as_echo "$as_me: error: --with-snapshots parameter invalid
+" >&2;}
+ { (exit 1); exit 1; }; }
+fi;
+
+if test x$SNAPSHOTS = xinternal; then
+
+cat >>confdefs.h <<\_ACEOF
+#define SNAPSHOT_INTERNAL 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to include mirrors" >&5
+$as_echo_n "checking whether to include mirrors... " >&6; }
+
+# Check whether --with-mirrors was given.
+if test "${with_mirrors+set}" = set; then
+ withval=$with_mirrors; MIRRORS=$withval
+else
+ MIRRORS=internal
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $MIRRORS" >&5
+$as_echo "$MIRRORS" >&6; }
+
+if [ "x$MIRRORS" != xnone -a "x$MIRRORS" != xinternal -a "x$MIRRORS" != xshared ];
+ then { { $as_echo "$as_me:$LINENO: error: --with-mirrors parameter invalid
+" >&5
+$as_echo "$as_me: error: --with-mirrors parameter invalid
+" >&2;}
+ { (exit 1); exit 1; }; }
+fi;
+
+if test x$MIRRORS = xinternal; then
+
+cat >>confdefs.h <<\_ACEOF
+#define MIRRORED_INTERNAL 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to include replicators" >&5
+$as_echo_n "checking whether to include replicators... " >&6; }
+
+# Check whether --with-replicators was given.
+if test "${with_replicators+set}" = set; then
+ withval=$with_replicators; REPLICATORS=$withval
+else
+ REPLICATORS=none
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $REPLICATORS" >&5
+$as_echo "$REPLICATORS" >&6; }
+
+case "$REPLICATORS" in
+ none|shared) ;;
+ internal)
+cat >>confdefs.h <<\_ACEOF
+#define REPLICATOR_INTERNAL 1
+_ACEOF
+ ;;
+ *) { { $as_echo "$as_me:$LINENO: error: --with-replicators parameter invalid ($REPLICATORS)" >&5
+$as_echo "$as_me: error: --with-replicators parameter invalid ($REPLICATORS)" >&2;}
+ { (exit 1); exit 1; }; } ;;
+esac
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable readline" >&5
+$as_echo_n "checking whether to enable readline... " >&6; }
+# Check whether --enable-readline was given.
+if test "${enable_readline+set}" = set; then
+ enableval=$enable_readline; READLINE=$enableval
+else
+ READLINE=maybe
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $READLINE" >&5
+$as_echo "$READLINE" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable realtime support" >&5
+$as_echo_n "checking whether to enable realtime support... " >&6; }
+# Check whether --enable-realtime was given.
+if test "${enable_realtime+set}" = set; then
+ enableval=$enable_realtime; REALTIME=$enableval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $REALTIME" >&5
+$as_echo "$REALTIME" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable OCF resource agents" >&5
+$as_echo_n "checking whether to enable OCF resource agents... " >&6; }
+# Check whether --enable-ocf was given.
+if test "${enable_ocf+set}" = set; then
+ enableval=$enable_ocf; OCF=$enableval
+else
+ OCF=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $OCF" >&5
+$as_echo "$OCF" >&6; }
+
+################################################################################
+pkg_config_init() {
+
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_PKG_CONFIG+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $PKG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+ { $as_echo "$as_me:$LINENO: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_PKG_CONFIG"; then
+ ac_pt_PKG_CONFIG=$PKG_CONFIG
+ # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_ac_pt_PKG_CONFIG+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $ac_pt_PKG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
+if test -n "$ac_pt_PKG_CONFIG"; then
+ { $as_echo "$as_me:$LINENO: result: $ac_pt_PKG_CONFIG" >&5
+$as_echo "$ac_pt_PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_pt_PKG_CONFIG" = x; then
+ PKG_CONFIG=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ PKG_CONFIG=$ac_pt_PKG_CONFIG
+ fi
+else
+ PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
+fi
+
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=0.9.0
+ { $as_echo "$as_me:$LINENO: checking pkg-config is at least version $_pkg_min_version" >&5
+$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ PKG_CONFIG=""
+ fi
+
+fi
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for PKGCONFIGINIT" >&5
+$as_echo_n "checking for PKGCONFIGINIT... " >&6; }
+
+if test -n "$PKGCONFIGINIT_CFLAGS"; then
+ pkg_cv_PKGCONFIGINIT_CFLAGS="$PKGCONFIGINIT_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"pkgconfiginit\"") >&5
+ ($PKG_CONFIG --exists --print-errors "pkgconfiginit") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_PKGCONFIGINIT_CFLAGS=`$PKG_CONFIG --cflags "pkgconfiginit" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$PKGCONFIGINIT_LIBS"; then
+ pkg_cv_PKGCONFIGINIT_LIBS="$PKGCONFIGINIT_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"pkgconfiginit\"") >&5
+ ($PKG_CONFIG --exists --print-errors "pkgconfiginit") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_PKGCONFIGINIT_LIBS=`$PKG_CONFIG --libs "pkgconfiginit" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ PKGCONFIGINIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "pkgconfiginit" 2>&1`
+ else
+ PKGCONFIGINIT_PKG_ERRORS=`$PKG_CONFIG --print-errors "pkgconfiginit" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$PKGCONFIGINIT_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ { $as_echo "$as_me:$LINENO: result: pkg-config initialized" >&5
+$as_echo "pkg-config initialized" >&6; }
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:$LINENO: result: pkg-config initialized" >&5
+$as_echo "pkg-config initialized" >&6; }
+else
+ PKGCONFIGINIT_CFLAGS=$pkg_cv_PKGCONFIGINIT_CFLAGS
+ PKGCONFIGINIT_LIBS=$pkg_cv_PKGCONFIGINIT_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ :
+fi
+ PKGCONFIG_INIT=1
+}
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to build cluster LVM daemon" >&5
+$as_echo_n "checking whether to build cluster LVM daemon... " >&6; }
+
+# Check whether --with-clvmd was given.
+if test "${with_clvmd+set}" = set; then
+ withval=$with_clvmd; CLVMD=$withval
+else
+ CLVMD=none
+fi
+
+if test x$CLVMD = xyes; then
+ CLVMD=all
+fi
+{ $as_echo "$as_me:$LINENO: result: $CLVMD" >&5
+$as_echo "$CLVMD" >&6; }
+
+if test x$CLVMD != xnone && test x$CLUSTER = xnone; then
+ CLUSTER=internal
+fi
+
+if test x$CLVMD != xnone && test x$PKGCONFIG_INIT != x1; then
+ pkg_config_init
+fi
+
+CLVMD_CMANAGERS=""
+CLVMD_NEEDS_QDISKD=no
+
+if [ `expr x"$CLVMD" : '.*gulm.*'` != 0 ]; then
+ BUILDGULM=yes
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS lock_gulmd"
+ CLVMD_NEEDS_QDISKD=yes
+fi
+if [ `expr x"$CLVMD" : '.*cman.*'` != 0 ]; then
+ BUILDCMAN=yes
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS cman"
+ CLVMD_NEEDS_QDISKD=yes
+fi
+if [ `expr x"$CLVMD" : '.*corosync.*'` != 0 ]; then
+ BUILDCOROSYNC=yes
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS corosync"
+fi
+if [ `expr x"$CLVMD" : '.*openais.*'` != 0 ]; then
+ BUILDOPENAIS=yes
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS openais"
+fi
+if test x$CLVMD_NEEDS_QDISKD != xno; then
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS qdiskd"
+fi
+
+if test x$BUILDGULM = xyes; then
+ if test x$BUILDCOROSYNC = xyes || \
+ test x$BUILDOPENAIS = xyes; then
+ { { $as_echo "$as_me:$LINENO: error: requested clvmd configuration is not valid" >&5
+$as_echo "$as_me: error: requested clvmd configuration is not valid" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+fi
+
+soft_bailout() {
+ NOTFOUND=1
+}
+
+hard_bailout() {
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+}
+
+if test x$CLVMD = xall; then
+ bailout=soft_bailout
+ BUILDGULM=yes
+ BUILDCMAN=yes
+ BUILDCOROSYNC=yes
+ BUILDOPENAIS=yes
+else
+ bailout=hard_bailout
+fi
+
+check_lib_no_libs() {
+ lib_no_libs_arg1=$1
+ shift
+ lib_no_libs_arg2=$1
+ shift
+ lib_no_libs_args=$@
+
+as_ac_Lib=`$as_echo "ac_cv_lib_$lib_no_libs_arg1''_$lib_no_libs_arg2" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $lib_no_libs_arg2 in -l$lib_no_libs_arg1" >&5
+$as_echo_n "checking for $lib_no_libs_arg2 in -l$lib_no_libs_arg1... " >&6; }
+if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-l$lib_no_libs_arg1 $lib_no_libs_args $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $lib_no_libs_arg2 ();
+int
+main ()
+{
+return $lib_no_libs_arg2 ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ eval "$as_ac_Lib=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Lib=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+ac_res=`eval 'as_val=${'$as_ac_Lib'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_Lib'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_LIB$lib_no_libs_arg1" | $as_tr_cpp` 1
+_ACEOF
+
+ LIBS="-l$lib_no_libs_arg1 $LIBS"
+
+else
+ $bailout
+fi
+
+ LIBS=$ac_check_lib_save_LIBS
+}
+
+if test x$BUILDGULM = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for CCS" >&5
+$as_echo_n "checking for CCS... " >&6; }
+
+if test -n "$CCS_CFLAGS"; then
+ pkg_cv_CCS_CFLAGS="$CCS_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libccs\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libccs") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_CCS_CFLAGS=`$PKG_CONFIG --cflags "libccs" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$CCS_LIBS"; then
+ pkg_cv_CCS_LIBS="$CCS_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libccs\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libccs") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_CCS_LIBS=`$PKG_CONFIG --libs "libccs" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ CCS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libccs" 2>&1`
+ else
+ CCS_PKG_ERRORS=`$PKG_CONFIG --print-errors "libccs" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$CCS_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ NOTFOUND=0
+
+for ac_header in ccs.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ $bailout
+fi
+
+done
+
+ check_lib_no_libs ccs ccs_connect
+ if test $NOTFOUND = 0; then
+ { $as_echo "$as_me:$LINENO: result: no pkg for libccs, using -lccs" >&5
+$as_echo "no pkg for libccs, using -lccs" >&6; }
+ CCS_LIBS="-lccs"
+ HAVE_CCS=yes
+ fi
+elif test $pkg_failed = untried; then
+ NOTFOUND=0
+
+for ac_header in ccs.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ $bailout
+fi
+
+done
+
+ check_lib_no_libs ccs ccs_connect
+ if test $NOTFOUND = 0; then
+ { $as_echo "$as_me:$LINENO: result: no pkg for libccs, using -lccs" >&5
+$as_echo "no pkg for libccs, using -lccs" >&6; }
+ CCS_LIBS="-lccs"
+ HAVE_CCS=yes
+ fi
+else
+ CCS_CFLAGS=$pkg_cv_CCS_CFLAGS
+ CCS_LIBS=$pkg_cv_CCS_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ HAVE_CCS=yes
+fi
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for GULM" >&5
+$as_echo_n "checking for GULM... " >&6; }
+
+if test -n "$GULM_CFLAGS"; then
+ pkg_cv_GULM_CFLAGS="$GULM_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libgulm\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libgulm") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_GULM_CFLAGS=`$PKG_CONFIG --cflags "libgulm" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$GULM_LIBS"; then
+ pkg_cv_GULM_LIBS="$GULM_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libgulm\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libgulm") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_GULM_LIBS=`$PKG_CONFIG --libs "libgulm" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ GULM_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libgulm" 2>&1`
+ else
+ GULM_PKG_ERRORS=`$PKG_CONFIG --print-errors "libgulm" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$GULM_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ NOTFOUND=0
+
+for ac_header in libgulm.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ $bailout
+fi
+
+done
+
+ check_lib_no_libs gulm lg_core_login
+ if test $NOTFOUND = 0; then
+ { $as_echo "$as_me:$LINENO: result: no pkg for libgulm, using -lgulm" >&5
+$as_echo "no pkg for libgulm, using -lgulm" >&6; }
+ GULM_LIBS="-lgulm"
+ HAVE_GULM=yes
+ fi
+elif test $pkg_failed = untried; then
+ NOTFOUND=0
+
+for ac_header in libgulm.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ $bailout
+fi
+
+done
+
+ check_lib_no_libs gulm lg_core_login
+ if test $NOTFOUND = 0; then
+ { $as_echo "$as_me:$LINENO: result: no pkg for libgulm, using -lgulm" >&5
+$as_echo "no pkg for libgulm, using -lgulm" >&6; }
+ GULM_LIBS="-lgulm"
+ HAVE_GULM=yes
+ fi
+else
+ GULM_CFLAGS=$pkg_cv_GULM_CFLAGS
+ GULM_LIBS=$pkg_cv_GULM_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ HAVE_GULM=yes
+fi
+fi
+
+if test x$BUILDCMAN = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for CMAN" >&5
+$as_echo_n "checking for CMAN... " >&6; }
+
+if test -n "$CMAN_CFLAGS"; then
+ pkg_cv_CMAN_CFLAGS="$CMAN_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcman\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libcman") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_CMAN_CFLAGS=`$PKG_CONFIG --cflags "libcman" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$CMAN_LIBS"; then
+ pkg_cv_CMAN_LIBS="$CMAN_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcman\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libcman") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_CMAN_LIBS=`$PKG_CONFIG --libs "libcman" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ CMAN_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libcman" 2>&1`
+ else
+ CMAN_PKG_ERRORS=`$PKG_CONFIG --print-errors "libcman" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$CMAN_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ NOTFOUND=0
+
+for ac_header in libcman.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ $bailout
+fi
+
+done
+
+ check_lib_no_libs cman cman_init
+ if test $NOTFOUND = 0; then
+ { $as_echo "$as_me:$LINENO: result: no pkg for libcman, using -lcman" >&5
+$as_echo "no pkg for libcman, using -lcman" >&6; }
+ CMAN_LIBS="-lcman"
+ HAVE_CMAN=yes
+ fi
+elif test $pkg_failed = untried; then
+ NOTFOUND=0
+
+for ac_header in libcman.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ $bailout
+fi
+
+done
+
+ check_lib_no_libs cman cman_init
+ if test $NOTFOUND = 0; then
+ { $as_echo "$as_me:$LINENO: result: no pkg for libcman, using -lcman" >&5
+$as_echo "no pkg for libcman, using -lcman" >&6; }
+ CMAN_LIBS="-lcman"
+ HAVE_CMAN=yes
+ fi
+else
+ CMAN_CFLAGS=$pkg_cv_CMAN_CFLAGS
+ CMAN_LIBS=$pkg_cv_CMAN_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ HAVE_CMAN=yes
+fi
+ CHECKCONFDB=yes
+ CHECKDLM=yes
+fi
+
+if test x$BUILDCOROSYNC = xyes || \
+ test x$BUILDOPENAIS = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for COROSYNC" >&5
+$as_echo_n "checking for COROSYNC... " >&6; }
+
+if test -n "$COROSYNC_CFLAGS"; then
+ pkg_cv_COROSYNC_CFLAGS="$COROSYNC_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"corosync\"") >&5
+ ($PKG_CONFIG --exists --print-errors "corosync") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_COROSYNC_CFLAGS=`$PKG_CONFIG --cflags "corosync" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$COROSYNC_LIBS"; then
+ pkg_cv_COROSYNC_LIBS="$COROSYNC_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"corosync\"") >&5
+ ($PKG_CONFIG --exists --print-errors "corosync") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_COROSYNC_LIBS=`$PKG_CONFIG --libs "corosync" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ COROSYNC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "corosync" 2>&1`
+ else
+ COROSYNC_PKG_ERRORS=`$PKG_CONFIG --print-errors "corosync" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$COROSYNC_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ $bailout
+elif test $pkg_failed = untried; then
+ $bailout
+else
+ COROSYNC_CFLAGS=$pkg_cv_COROSYNC_CFLAGS
+ COROSYNC_LIBS=$pkg_cv_COROSYNC_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ HAVE_COROSYNC=yes
+fi
+ CHECKCONFDB=yes
+fi
+
+if test x$BUILDCOROSYNC = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for QUORUM" >&5
+$as_echo_n "checking for QUORUM... " >&6; }
+
+if test -n "$QUORUM_CFLAGS"; then
+ pkg_cv_QUORUM_CFLAGS="$QUORUM_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libquorum\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libquorum") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_QUORUM_CFLAGS=`$PKG_CONFIG --cflags "libquorum" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$QUORUM_LIBS"; then
+ pkg_cv_QUORUM_LIBS="$QUORUM_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libquorum\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libquorum") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_QUORUM_LIBS=`$PKG_CONFIG --libs "libquorum" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ QUORUM_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libquorum" 2>&1`
+ else
+ QUORUM_PKG_ERRORS=`$PKG_CONFIG --print-errors "libquorum" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$QUORUM_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ $bailout
+elif test $pkg_failed = untried; then
+ $bailout
+else
+ QUORUM_CFLAGS=$pkg_cv_QUORUM_CFLAGS
+ QUORUM_LIBS=$pkg_cv_QUORUM_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ HAVE_QUORUM=yes
+fi
+ CHECKCPG=yes
+ CHECKDLM=yes
+fi
+
+if test x$BUILDOPENAIS = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for SALCK" >&5
+$as_echo_n "checking for SALCK... " >&6; }
+
+if test -n "$SALCK_CFLAGS"; then
+ pkg_cv_SALCK_CFLAGS="$SALCK_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libSaLck\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libSaLck") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_SALCK_CFLAGS=`$PKG_CONFIG --cflags "libSaLck" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$SALCK_LIBS"; then
+ pkg_cv_SALCK_LIBS="$SALCK_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libSaLck\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libSaLck") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_SALCK_LIBS=`$PKG_CONFIG --libs "libSaLck" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ SALCK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libSaLck" 2>&1`
+ else
+ SALCK_PKG_ERRORS=`$PKG_CONFIG --print-errors "libSaLck" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$SALCK_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ $bailout
+elif test $pkg_failed = untried; then
+ $bailout
+else
+ SALCK_CFLAGS=$pkg_cv_SALCK_CFLAGS
+ SALCK_LIBS=$pkg_cv_SALCK_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ HAVE_SALCK=yes
+fi
+ CHECKCPG=yes
+fi
+
+
+
+if test x$CHECKCONFDB = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for CONFDB" >&5
+$as_echo_n "checking for CONFDB... " >&6; }
+
+if test -n "$CONFDB_CFLAGS"; then
+ pkg_cv_CONFDB_CFLAGS="$CONFDB_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libconfdb\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libconfdb") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_CONFDB_CFLAGS=`$PKG_CONFIG --cflags "libconfdb" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$CONFDB_LIBS"; then
+ pkg_cv_CONFDB_LIBS="$CONFDB_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libconfdb\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libconfdb") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_CONFDB_LIBS=`$PKG_CONFIG --libs "libconfdb" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ CONFDB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libconfdb" 2>&1`
+ else
+ CONFDB_PKG_ERRORS=`$PKG_CONFIG --print-errors "libconfdb" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$CONFDB_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ HAVE_CONFDB=no
+elif test $pkg_failed = untried; then
+ HAVE_CONFDB=no
+else
+ CONFDB_CFLAGS=$pkg_cv_CONFDB_CFLAGS
+ CONFDB_LIBS=$pkg_cv_CONFDB_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ HAVE_CONFDB=yes
+fi
+
+
+for ac_header in corosync/confdb.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+ HAVE_CONFDB_H=yes
+else
+ HAVE_CONFDB_H=no
+fi
+
+done
+
+
+ if test x$HAVE_CONFDB != xyes && \
+ test x$HAVE_CONFDB_H = xyes; then
+ check_lib_no_libs confdb confdb_initialize
+ { $as_echo "$as_me:$LINENO: result: no pkg for confdb, using -lconfdb" >&5
+$as_echo "no pkg for confdb, using -lconfdb" >&6; }
+ CONFDB_LIBS="-lconfdb"
+ HAVE_CONFDB=yes
+ fi
+
+ if test x$BUILDCOROSYNC = xyes && \
+ test x$HAVE_CONFDB != xyes &&
+ test x$CLVMD != xall; then
+ { { $as_echo "$as_me:$LINENO: error: bailing out... confdb library is required" >&5
+$as_echo "$as_me: error: bailing out... confdb library is required" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+fi
+
+if test x$CHECKCPG = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for CPG" >&5
+$as_echo_n "checking for CPG... " >&6; }
+
+if test -n "$CPG_CFLAGS"; then
+ pkg_cv_CPG_CFLAGS="$CPG_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcpg\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libcpg") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_CPG_CFLAGS=`$PKG_CONFIG --cflags "libcpg" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$CPG_LIBS"; then
+ pkg_cv_CPG_LIBS="$CPG_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcpg\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libcpg") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_CPG_LIBS=`$PKG_CONFIG --libs "libcpg" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ CPG_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libcpg" 2>&1`
+ else
+ CPG_PKG_ERRORS=`$PKG_CONFIG --print-errors "libcpg" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$CPG_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ $bailout
+elif test $pkg_failed = untried; then
+ $bailout
+else
+ CPG_CFLAGS=$pkg_cv_CPG_CFLAGS
+ CPG_LIBS=$pkg_cv_CPG_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ HAVE_CPG=yes
+fi
+fi
+
+if test x$CHECKDLM = xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for DLM" >&5
+$as_echo_n "checking for DLM... " >&6; }
+
+if test -n "$DLM_CFLAGS"; then
+ pkg_cv_DLM_CFLAGS="$DLM_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libdlm\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libdlm") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_DLM_CFLAGS=`$PKG_CONFIG --cflags "libdlm" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$DLM_LIBS"; then
+ pkg_cv_DLM_LIBS="$DLM_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libdlm\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libdlm") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_DLM_LIBS=`$PKG_CONFIG --libs "libdlm" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ DLM_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libdlm" 2>&1`
+ else
+ DLM_PKG_ERRORS=`$PKG_CONFIG --print-errors "libdlm" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$DLM_PKG_ERRORS" >&5
+
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+ NOTFOUND=0
+
+for ac_header in libdlm.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ $bailout
+fi
+
+done
+
+ check_lib_no_libs dlm dlm_lock -lpthread
+ if test $NOTFOUND = 0; then
+ { $as_echo "$as_me:$LINENO: result: no pkg for libdlm, using -ldlm" >&5
+$as_echo "no pkg for libdlm, using -ldlm" >&6; }
+ DLM_LIBS="-ldlm -lpthread"
+ HAVE_DLM=yes
+ fi
+elif test $pkg_failed = untried; then
+ NOTFOUND=0
+
+for ac_header in libdlm.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ $bailout
+fi
+
+done
+
+ check_lib_no_libs dlm dlm_lock -lpthread
+ if test $NOTFOUND = 0; then
+ { $as_echo "$as_me:$LINENO: result: no pkg for libdlm, using -ldlm" >&5
+$as_echo "no pkg for libdlm, using -ldlm" >&6; }
+ DLM_LIBS="-ldlm -lpthread"
+ HAVE_DLM=yes
+ fi
+else
+ DLM_CFLAGS=$pkg_cv_DLM_CFLAGS
+ DLM_LIBS=$pkg_cv_DLM_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ HAVE_DLM=yes
+fi
+fi
+
+if test x$CLVMD = xall; then
+ CLVMD=none
+ CLVMD_CMANAGERS=""
+ CLVMD_NEEDS_QDISKD=no
+ if test x$HAVE_CCS = xyes && \
+ test x$HAVE_GULM = xyes; then
+ { $as_echo "$as_me:$LINENO: result: Enabling clvmd gulm cluster manager" >&5
+$as_echo "Enabling clvmd gulm cluster manager" >&6; }
+ CLVMD="$CLVMD,gulm"
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS lock_gulmd"
+ CLVMD_NEEDS_QDISKD=yes
+ fi
+ if test x$HAVE_CMAN = xyes && \
+ test x$HAVE_DLM = xyes; then
+ { $as_echo "$as_me:$LINENO: result: Enabling clvmd cman cluster manager" >&5
+$as_echo "Enabling clvmd cman cluster manager" >&6; }
+ CLVMD="$CLVMD,cman"
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS cman"
+ CLVMD_NEEDS_QDISKD=yes
+ fi
+ if test x$HAVE_COROSYNC = xyes && \
+ test x$HAVE_QUORUM = xyes && \
+ test x$HAVE_CPG = xyes && \
+ test x$HAVE_DLM = xyes && \
+ test x$HAVE_CONFDB = xyes; then
+ { $as_echo "$as_me:$LINENO: result: Enabling clvmd corosync cluster manager" >&5
+$as_echo "Enabling clvmd corosync cluster manager" >&6; }
+ CLVMD="$CLVMD,corosync"
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS corosync"
+ fi
+ if test x$HAVE_COROSYNC = xyes && \
+ test x$HAVE_CPG = xyes && \
+ test x$HAVE_SALCK = xyes; then
+ { $as_echo "$as_me:$LINENO: result: Enabling clvmd openais cluster manager" >&5
+$as_echo "Enabling clvmd openais cluster manager" >&6; }
+ CLVMD="$CLVMD,openais"
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS openais"
+ fi
+ if test x$CLVMD_NEEDS_QDISKD != xno; then
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS qdiskd"
+ fi
+ if test x$CLVMD = xnone; then
+ { $as_echo "$as_me:$LINENO: result: Disabling clvmd build. No cluster manager detected." >&5
+$as_echo "Disabling clvmd build. No cluster manager detected." >&6; }
+ fi
+fi
+
+################################################################################
+if test "x$CLVMD" != xnone; then
+
+# Check whether --with-clvmd-pidfile was given.
+if test "${with_clvmd_pidfile+set}" = set; then
+ withval=$with_clvmd_pidfile; CLVMD_PIDFILE=$withval
+else
+ CLVMD_PIDFILE="/var/run/clvmd.pid"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define CLVMD_PIDFILE "$CLVMD_PIDFILE"
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to build cluster mirror log daemon" >&5
+$as_echo_n "checking whether to build cluster mirror log daemon... " >&6; }
+# Check whether --enable-cmirrord was given.
+if test "${enable_cmirrord+set}" = set; then
+ enableval=$enable_cmirrord; CMIRRORD=$enableval
+else
+ CMIRRORD=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $CMIRRORD" >&5
+$as_echo "$CMIRRORD" >&6; }
+
+BUILD_CMIRRORD=$CMIRRORD
+
+################################################################################
+if test "x$BUILD_CMIRRORD" = xyes; then
+
+# Check whether --with-cmirrord-pidfile was given.
+if test "${with_cmirrord_pidfile+set}" = set; then
+ withval=$with_cmirrord_pidfile; CMIRRORD_PIDFILE=$withval
+else
+ CMIRRORD_PIDFILE="/var/run/cmirrord.pid"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define CMIRRORD_PIDFILE "$CMIRRORD_PIDFILE"
+_ACEOF
+
+fi
+
+################################################################################
+if [ "x$BUILD_CMIRRORD" = xyes ]; then
+ if test x$PKGCONFIG_INIT != x1; then
+ pkg_config_init
+ fi
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for SACKPT" >&5
+$as_echo_n "checking for SACKPT... " >&6; }
+
+if test -n "$SACKPT_CFLAGS"; then
+ pkg_cv_SACKPT_CFLAGS="$SACKPT_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libSaCkpt\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libSaCkpt") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_SACKPT_CFLAGS=`$PKG_CONFIG --cflags "libSaCkpt" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$SACKPT_LIBS"; then
+ pkg_cv_SACKPT_LIBS="$SACKPT_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libSaCkpt\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libSaCkpt") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_SACKPT_LIBS=`$PKG_CONFIG --libs "libSaCkpt" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ SACKPT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libSaCkpt" 2>&1`
+ else
+ SACKPT_PKG_ERRORS=`$PKG_CONFIG --print-errors "libSaCkpt" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$SACKPT_PKG_ERRORS" >&5
+
+ { { $as_echo "$as_me:$LINENO: error: Package requirements (libSaCkpt) were not met:
+
+$SACKPT_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables SACKPT_CFLAGS
+and SACKPT_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+" >&5
+$as_echo "$as_me: error: Package requirements (libSaCkpt) were not met:
+
+$SACKPT_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables SACKPT_CFLAGS
+and SACKPT_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+" >&2;}
+ { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+ { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables SACKPT_CFLAGS
+and SACKPT_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables SACKPT_CFLAGS
+and SACKPT_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+else
+ SACKPT_CFLAGS=$pkg_cv_SACKPT_CFLAGS
+ SACKPT_LIBS=$pkg_cv_SACKPT_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ :
+fi
+ if test x$HAVE_CPG != xyes; then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for CPG" >&5
+$as_echo_n "checking for CPG... " >&6; }
+
+if test -n "$CPG_CFLAGS"; then
+ pkg_cv_CPG_CFLAGS="$CPG_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcpg\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libcpg") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_CPG_CFLAGS=`$PKG_CONFIG --cflags "libcpg" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$CPG_LIBS"; then
+ pkg_cv_CPG_LIBS="$CPG_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcpg\"") >&5
+ ($PKG_CONFIG --exists --print-errors "libcpg") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ pkg_cv_CPG_LIBS=`$PKG_CONFIG --libs "libcpg" 2>/dev/null`
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ CPG_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libcpg" 2>&1`
+ else
+ CPG_PKG_ERRORS=`$PKG_CONFIG --print-errors "libcpg" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$CPG_PKG_ERRORS" >&5
+
+ { { $as_echo "$as_me:$LINENO: error: Package requirements (libcpg) were not met:
+
+$CPG_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables CPG_CFLAGS
+and CPG_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+" >&5
+$as_echo "$as_me: error: Package requirements (libcpg) were not met:
+
+$CPG_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables CPG_CFLAGS
+and CPG_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+" >&2;}
+ { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+ { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { $as_echo "$as_me:$LINENO: error: The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables CPG_CFLAGS
+and CPG_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details." >&5
+$as_echo "$as_me: error: The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables CPG_CFLAGS
+and CPG_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }; }
+else
+ CPG_CFLAGS=$pkg_cv_CPG_CFLAGS
+ CPG_LIBS=$pkg_cv_CPG_LIBS
+ { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+ :
+fi
+ fi
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable debugging" >&5
+$as_echo_n "checking whether to enable debugging... " >&6; }
+# Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then
+ enableval=$enable_debug; DEBUG=$enableval
+else
+ DEBUG=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $DEBUG" >&5
+$as_echo "$DEBUG" >&6; }
+
+if test x$DEBUG = xyes; then
+ COPTIMISE_FLAG=
+else
+ CSCOPE_CMD=
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking for C optimisation flag" >&5
+$as_echo_n "checking for C optimisation flag... " >&6; }
+
+# Check whether --with-optimisation was given.
+if test "${with_optimisation+set}" = set; then
+ withval=$with_optimisation; COPTIMISE_FLAG=$withval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $COPTIMISE_FLAG" >&5
+$as_echo "$COPTIMISE_FLAG" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to gather gcov profiling data" >&5
+$as_echo_n "checking whether to gather gcov profiling data... " >&6; }
+# Check whether --enable-profiling was given.
+if test "${enable_profiling+set}" = set; then
+ enableval=$enable_profiling; PROFILING=$enableval
+else
+ PROFILING=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $PROFILING" >&5
+$as_echo "$PROFILING" >&6; }
+
+if test "x$PROFILING" = xyes; then
+ COPTIMISE_FLAG="$COPTIMISE_FLAG -fprofile-arcs -ftest-coverage"
+ # Extract the first word of "lcov", so it can be a program name with args.
+set dummy lcov; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_LCOV+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $LCOV in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_LCOV="$LCOV" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_LCOV="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+LCOV=$ac_cv_path_LCOV
+if test -n "$LCOV"; then
+ { $as_echo "$as_me:$LINENO: result: $LCOV" >&5
+$as_echo "$LCOV" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ # Extract the first word of "genhtml", so it can be a program name with args.
+set dummy genhtml; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_GENHTML+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $GENHTML in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_GENHTML="$GENHTML" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_GENHTML="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+GENHTML=$ac_cv_path_GENHTML
+if test -n "$GENHTML"; then
+ { $as_echo "$as_me:$LINENO: result: $GENHTML" >&5
+$as_echo "$GENHTML" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$LCOV" -o -z "$GENHTML"; then
+ { { $as_echo "$as_me:$LINENO: error: lcov and genhtml are required for profiling" >&5
+$as_echo "$as_me: error: lcov and genhtml are required for profiling" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ # Extract the first word of "genpng", so it can be a program name with args.
+set dummy genpng; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_GENPNG+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $GENPNG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_GENPNG="$GENPNG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_GENPNG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+GENPNG=$ac_cv_path_GENPNG
+if test -n "$GENPNG"; then
+ { $as_echo "$as_me:$LINENO: result: $GENPNG" >&5
+$as_echo "$GENPNG" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -n "$GENPNG"; then
+ { $as_echo "$as_me:$LINENO: checking whether $GENPNG has all required modules" >&5
+$as_echo_n "checking whether $GENPNG has all required modules... " >&6; }
+ if $GENPNG --help > /dev/null 2>&1 ; then
+ { $as_echo "$as_me:$LINENO: result: ok" >&5
+$as_echo "ok" >&6; }
+ GENHTML="$GENHTML --frames"
+ else
+ { $as_echo "$as_me:$LINENO: result: not supported" >&5
+$as_echo "not supported" >&6; }
+ { $as_echo "$as_me:$LINENO: WARNING: GD.pm perl module is not installed" >&5
+$as_echo "$as_me: WARNING: GD.pm perl module is not installed" >&2;}
+ GENPNG=
+ fi
+ fi
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable unit testing" >&5
+$as_echo_n "checking whether to enable unit testing... " >&6; }
+# Check whether --enable-testing was given.
+if test "${enable_testing+set}" = set; then
+ enableval=$enable_testing; TESTING=$enableval
+else
+ TESTING=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $TESTING" >&5
+$as_echo "$TESTING" >&6; }
+
+if test "$TESTING" = yes; then
+ # Extract the first word of "ruby1.9", so it can be a program name with args.
+set dummy ruby1.9; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_RUBY19+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $RUBY19 in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_RUBY19="$RUBY19" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_RUBY19="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+RUBY19=$ac_cv_path_RUBY19
+if test -n "$RUBY19"; then
+ { $as_echo "$as_me:$LINENO: result: $RUBY19" >&5
+$as_echo "$RUBY19" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ # Extract the first word of "valgrind", so it can be a program name with args.
+set dummy valgrind; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_VALGRIND+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $VALGRIND in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_VALGRIND="$VALGRIND" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_VALGRIND="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+VALGRIND=$ac_cv_path_VALGRIND
+if test -n "$VALGRIND"; then
+ { $as_echo "$as_me:$LINENO: result: $VALGRIND" >&5
+$as_echo "$VALGRIND" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$RUBY19" -o -z "$VALGRIND"; then
+ { { $as_echo "$as_me:$LINENO: error: ruby1.9 and valgrind are required for testing" >&5
+$as_echo "$as_me: error: ruby1.9 and valgrind are required for testing" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable valgrind awareness of pools" >&5
+$as_echo_n "checking whether to enable valgrind awareness of pools... " >&6; }
+# Check whether --enable-valgrind_pool was given.
+if test "${enable_valgrind_pool+set}" = set; then
+ enableval=$enable_valgrind_pool; VALGRIND_POOL=$enableval
+else
+ VALGRIND_POOL=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $VALGRIND_POOL" >&5
+$as_echo "$VALGRIND_POOL" >&6; }
+
+if test "$VALGRIND_POOL" = yes; then
+
+for ac_header in valgrind/memcheck.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+done
+
+
+cat >>confdefs.h <<\_ACEOF
+#define VALGRIND_POOL 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to use device-mapper" >&5
+$as_echo_n "checking whether to use device-mapper... " >&6; }
+# Check whether --enable-devmapper was given.
+if test "${enable_devmapper+set}" = set; then
+ enableval=$enable_devmapper; DEVMAPPER=$enableval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $DEVMAPPER" >&5
+$as_echo "$DEVMAPPER" >&6; }
+
+if test x$DEVMAPPER = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define DEVMAPPER_SUPPORT 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable synchronisation with udev processing" >&5
+$as_echo_n "checking whether to enable synchronisation with udev processing... " >&6; }
+# Check whether --enable-udev_sync was given.
+if test "${enable_udev_sync+set}" = set; then
+ enableval=$enable_udev_sync; UDEV_SYNC=$enableval
+else
+ UDEV_SYNC=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $UDEV_SYNC" >&5
+$as_echo "$UDEV_SYNC" >&6; }
+
+if test x$UDEV_SYNC = xyes; then
+ { $as_echo "$as_me:$LINENO: checking for udev_queue_get_udev_is_active in -ludev" >&5
+$as_echo_n "checking for udev_queue_get_udev_is_active in -ludev... " >&6; }
+if test "${ac_cv_lib_udev_udev_queue_get_udev_is_active+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ludev $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char udev_queue_get_udev_is_active ();
+int
+main ()
+{
+return udev_queue_get_udev_is_active ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_udev_udev_queue_get_udev_is_active=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_udev_udev_queue_get_udev_is_active=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_udev_udev_queue_get_udev_is_active" >&5
+$as_echo "$ac_cv_lib_udev_udev_queue_get_udev_is_active" >&6; }
+if test "x$ac_cv_lib_udev_udev_queue_get_udev_is_active" = x""yes; then
+ UDEV_PC="libudev"; UDEV_LIBS="-ludev"
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out... libudev library is required" >&5
+$as_echo "$as_me: error: bailing out... libudev library is required" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+cat >>confdefs.h <<\_ACEOF
+#define UDEV_SYNC_SUPPORT 1
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:$LINENO: checking whether to enable installation of udev rules required for synchronisation" >&5
+$as_echo_n "checking whether to enable installation of udev rules required for synchronisation... " >&6; }
+# Check whether --enable-udev_rules was given.
+if test "${enable_udev_rules+set}" = set; then
+ enableval=$enable_udev_rules; UDEV_RULES=$enableval
+else
+ UDEV_RULES=$UDEV_SYNC
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $UDEV_RULES" >&5
+$as_echo "$UDEV_RULES" >&6; }
+
+################################################################################
+# Check whether --enable-compat was given.
+if test "${enable_compat+set}" = set; then
+ enableval=$enable_compat; DM_COMPAT=$enableval
+else
+ DM_COMPAT=no
+fi
+
+
+################################################################################
+# Check whether --enable-units-compat was given.
+if test "${enable_units_compat+set}" = set; then
+ enableval=$enable_units_compat; UNITS_COMPAT=$enableval
+else
+ UNITS_COMPAT=no
+fi
+
+
+if test x$UNITS_COMPAT = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define DEFAULT_SI_UNIT_CONSISTENCY 0
+_ACEOF
+
+fi
+
+################################################################################
+# Check whether --enable-ioctl was given.
+if test "${enable_ioctl+set}" = set; then
+ enableval=$enable_ioctl; DM_IOCTLS=$enableval
+fi
+
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable O_DIRECT" >&5
+$as_echo_n "checking whether to enable O_DIRECT... " >&6; }
+# Check whether --enable-o_direct was given.
+if test "${enable_o_direct+set}" = set; then
+ enableval=$enable_o_direct; ODIRECT=$enableval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $ODIRECT" >&5
+$as_echo "$ODIRECT" >&6; }
+
+if test x$ODIRECT = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define O_DIRECT_SUPPORT 1
+_ACEOF
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to build liblvm2app.so application library" >&5
+$as_echo_n "checking whether to build liblvm2app.so application library... " >&6; }
+# Check whether --enable-applib was given.
+if test "${enable_applib+set}" = set; then
+ enableval=$enable_applib; APPLIB=$enableval
+else
+ APPLIB=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $APPLIB" >&5
+$as_echo "$APPLIB" >&6; }
+
+test x$APPLIB = xyes \
+ && LVM2APP_LIB=-llvm2app \
+ || LVM2APP_LIB=
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to compile liblvm2cmd.so" >&5
+$as_echo_n "checking whether to compile liblvm2cmd.so... " >&6; }
+# Check whether --enable-cmdlib was given.
+if test "${enable_cmdlib+set}" = set; then
+ enableval=$enable_cmdlib; CMDLIB=$enableval
+else
+ CMDLIB=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $CMDLIB" >&5
+$as_echo "$CMDLIB" >&6; }
+
+test x$CMDLIB = xyes \
+ && LVM2CMD_LIB=-llvm2cmd \
+ || LVM2CMD_LIB=
+
+################################################################################
+# Check whether --enable-pkgconfig was given.
+if test "${enable_pkgconfig+set}" = set; then
+ enableval=$enable_pkgconfig; PKGCONFIG=$enableval
+else
+ PKGCONFIG=no
+fi
+
+
+################################################################################
+# Check whether --enable-write_install was given.
+if test "${enable_write_install+set}" = set; then
+ enableval=$enable_write_install; WRITE_INSTALL=$enableval
+else
+ WRITE_INSTALL=no
+fi
+
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to install fsadm" >&5
+$as_echo_n "checking whether to install fsadm... " >&6; }
+# Check whether --enable-fsadm was given.
+if test "${enable_fsadm+set}" = set; then
+ enableval=$enable_fsadm; FSADM=$enableval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $FSADM" >&5
+$as_echo "$FSADM" >&6; }
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to use dmeventd" >&5
+$as_echo_n "checking whether to use dmeventd... " >&6; }
+# Check whether --enable-dmeventd was given.
+if test "${enable_dmeventd+set}" = set; then
+ enableval=$enable_dmeventd; DMEVENTD=$enableval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $DMEVENTD" >&5
+$as_echo "$DMEVENTD" >&6; }
+
+BUILD_DMEVENTD=$DMEVENTD
+
+if test x$DMEVENTD = xyes; then
+ if test x$MIRRORS != xinternal; then
+ { { $as_echo "$as_me:$LINENO: error: --enable-dmeventd currently requires --with-mirrors=internal
+ " >&5
+$as_echo "$as_me: error: --enable-dmeventd currently requires --with-mirrors=internal
+ " >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ if test x$CMDLIB = xno; then
+ { { $as_echo "$as_me:$LINENO: error: --enable-dmeventd requires --enable-cmdlib to be used as well
+ " >&5
+$as_echo "$as_me: error: --enable-dmeventd requires --enable-cmdlib to be used as well
+ " >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+fi
+
+if test x$DMEVENTD = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define DMEVENTD 1
+_ACEOF
+
+fi
+
+################################################################################
+
+{ $as_echo "$as_me:$LINENO: checking for getline in -lc" >&5
+$as_echo_n "checking for getline in -lc... " >&6; }
+if test "${ac_cv_lib_c_getline+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lc $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char getline ();
+int
+main ()
+{
+return getline ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_c_getline=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_c_getline=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_c_getline" >&5
+$as_echo "$ac_cv_lib_c_getline" >&6; }
+if test "x$ac_cv_lib_c_getline" = x""yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GETLINE 1
+_ACEOF
+
+fi
+
+
+################################################################################
+
+{ $as_echo "$as_me:$LINENO: checking for canonicalize_file_name in -lc" >&5
+$as_echo_n "checking for canonicalize_file_name in -lc... " >&6; }
+if test "${ac_cv_lib_c_canonicalize_file_name+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lc $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char canonicalize_file_name ();
+int
+main ()
+{
+return canonicalize_file_name ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_c_canonicalize_file_name=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_c_canonicalize_file_name=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_c_canonicalize_file_name" >&5
+$as_echo "$ac_cv_lib_c_canonicalize_file_name" >&6; }
+if test "x$ac_cv_lib_c_canonicalize_file_name" = x""yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_CANONICALIZE_FILE_NAME 1
+_ACEOF
+
+fi
+
+
+################################################################################
+if [ "x$exec_prefix" = xNONE -a "x$prefix" = xNONE ];
+ then exec_prefix="";
+fi;
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_dl_dlopen=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_dl_dlopen=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = x""yes; then
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_LIBDL 1
+_ACEOF
+
+ DL_LIBS="-ldl"
+ HAVE_LIBDL=yes
+else
+
+ DL_LIBS=
+ HAVE_LIBDL=no
+fi
+
+
+################################################################################
+if [ \( "x$LVM1" = xshared -o "x$POOL" = xshared -o "x$CLUSTER" = xshared \
+ -o "x$SNAPSHOTS" = xshared -o "x$MIRRORS" = xshared \
+ \) -a "x$STATIC_LINK" = xyes ];
+ then { { $as_echo "$as_me:$LINENO: error: Features cannot be 'shared' when building statically
+" >&5
+$as_echo "$as_me: error: Features cannot be 'shared' when building statically
+" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+################################################################################
+if [ "$DMEVENTD" = yes -o "$CLVMD" != none ] ; then
+ { $as_echo "$as_me:$LINENO: checking for pthread_mutex_lock in -lpthread" >&5
+$as_echo_n "checking for pthread_mutex_lock in -lpthread... " >&6; }
+if test "${ac_cv_lib_pthread_pthread_mutex_lock+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpthread $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_mutex_lock ();
+int
+main ()
+{
+return pthread_mutex_lock ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_pthread_pthread_mutex_lock=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_pthread_pthread_mutex_lock=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_pthread_pthread_mutex_lock" >&5
+$as_echo "$ac_cv_lib_pthread_pthread_mutex_lock" >&6; }
+if test "x$ac_cv_lib_pthread_pthread_mutex_lock" = x""yes; then
+ PTHREAD_LIBS="-lpthread"
+else
+ hard_bailout
+fi
+
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable selinux support" >&5
+$as_echo_n "checking whether to enable selinux support... " >&6; }
+# Check whether --enable-selinux was given.
+if test "${enable_selinux+set}" = set; then
+ enableval=$enable_selinux; SELINUX=$enableval
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $SELINUX" >&5
+$as_echo "$SELINUX" >&6; }
+
+################################################################################
+if test x$SELINUX = xyes; then
+ { $as_echo "$as_me:$LINENO: checking for sepol_check_context in -lsepol" >&5
+$as_echo_n "checking for sepol_check_context in -lsepol... " >&6; }
+if test "${ac_cv_lib_sepol_sepol_check_context+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsepol $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char sepol_check_context ();
+int
+main ()
+{
+return sepol_check_context ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_sepol_sepol_check_context=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_sepol_sepol_check_context=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_sepol_sepol_check_context" >&5
+$as_echo "$ac_cv_lib_sepol_sepol_check_context" >&6; }
+if test "x$ac_cv_lib_sepol_sepol_check_context" = x""yes; then
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SEPOL 1
+_ACEOF
+
+ SELINUX_LIBS="-lsepol"
+fi
+
+
+ { $as_echo "$as_me:$LINENO: checking for is_selinux_enabled in -lselinux" >&5
+$as_echo_n "checking for is_selinux_enabled in -lselinux... " >&6; }
+if test "${ac_cv_lib_selinux_is_selinux_enabled+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lselinux $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char is_selinux_enabled ();
+int
+main ()
+{
+return is_selinux_enabled ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_selinux_is_selinux_enabled=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_selinux_is_selinux_enabled=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_is_selinux_enabled" >&5
+$as_echo "$ac_cv_lib_selinux_is_selinux_enabled" >&6; }
+if test "x$ac_cv_lib_selinux_is_selinux_enabled" = x""yes; then
+
+
+for ac_header in selinux/selinux.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ hard_bailout
+fi
+
+done
+
+
+for ac_header in selinux/label.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SELINUX 1
+_ACEOF
+
+ SELINUX_LIBS="-lselinux $SELINUX_LIBS"
+ SELINUX_PC="libselinux"
+ HAVE_SELINUX=yes
+else
+
+ { $as_echo "$as_me:$LINENO: WARNING: Disabling selinux" >&5
+$as_echo "$as_me: WARNING: Disabling selinux" >&2;}
+ SELINUX_LIBS=
+ SELINUX_PC=
+ HAVE_SELINUX=no
+fi
+
+fi
+
+################################################################################
+if test x$REALTIME = xyes; then
+ { $as_echo "$as_me:$LINENO: checking for clock_gettime in -lrt" >&5
+$as_echo_n "checking for clock_gettime in -lrt... " >&6; }
+if test "${ac_cv_lib_rt_clock_gettime+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lrt $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_rt_clock_gettime=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_rt_clock_gettime=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_rt_clock_gettime" >&5
+$as_echo "$ac_cv_lib_rt_clock_gettime" >&6; }
+if test "x$ac_cv_lib_rt_clock_gettime" = x""yes; then
+ HAVE_REALTIME=yes
+else
+ HAVE_REALTIME=no
+fi
+
+
+ if test x$HAVE_REALTIME = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_REALTIME 1
+_ACEOF
+
+ LIBS="-lrt $LIBS"
+ else
+ { $as_echo "$as_me:$LINENO: WARNING: Disabling realtime clock" >&5
+$as_echo "$as_me: WARNING: Disabling realtime clock" >&2;}
+ fi
+fi
+
+################################################################################
+
+for ac_header in getopt.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GETOPTLONG 1
+_ACEOF
+
+fi
+
+done
+
+
+################################################################################
+if test x$READLINE != xno; then
+ lvm_saved_libs=$LIBS
+ { $as_echo "$as_me:$LINENO: checking for library containing tgetent" >&5
+$as_echo_n "checking for library containing tgetent... " >&6; }
+if test "${ac_cv_search_tgetent+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char tgetent ();
+int
+main ()
+{
+return tgetent ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' tinfo ncurses curses termcap termlib; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_search_tgetent=$ac_res
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_tgetent+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_tgetent+set}" = set; then
+ :
+else
+ ac_cv_search_tgetent=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_tgetent" >&5
+$as_echo "$ac_cv_search_tgetent" >&6; }
+ac_res=$ac_cv_search_tgetent
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+ READLINE_LIBS=$ac_cv_search_tgetent
+else
+
+ if test "$READLINE" = yes; then
+ { { $as_echo "$as_me:$LINENO: error: termcap could not be found which is required for the
+--enable-readline option (which is enabled by default). Either disable readline
+support with --disable-readline or download and install termcap from:
+ ftp.gnu.org/gnu/termcap
+Note: if you are using precompiled packages you will also need the development
+ package as well (which may be called termcap-devel or something similar).
+Note: (n)curses also seems to work as a substitute for termcap. This was
+ not found either - but you could try installing that as well." >&5
+$as_echo "$as_me: error: termcap could not be found which is required for the
+--enable-readline option (which is enabled by default). Either disable readline
+support with --disable-readline or download and install termcap from:
+ ftp.gnu.org/gnu/termcap
+Note: if you are using precompiled packages you will also need the development
+ package as well (which may be called termcap-devel or something similar).
+Note: (n)curses also seems to work as a substitute for termcap. This was
+ not found either - but you could try installing that as well." >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+fi
+
+ { $as_echo "$as_me:$LINENO: checking for readline in -lreadline" >&5
+$as_echo_n "checking for readline in -lreadline... " >&6; }
+if test "${ac_cv_lib_readline_readline+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lreadline $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char readline ();
+int
+main ()
+{
+return readline ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_readline_readline=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_readline_readline=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_readline_readline" >&5
+$as_echo "$ac_cv_lib_readline_readline" >&6; }
+if test "x$ac_cv_lib_readline_readline" = x""yes; then
+
+
+cat >>confdefs.h <<\_ACEOF
+#define READLINE_SUPPORT 1
+_ACEOF
+
+ LIBS=$lvm_saved_libs
+ { $as_echo "$as_me:$LINENO: checking for rl_line_buffer in -lreadline" >&5
+$as_echo_n "checking for rl_line_buffer in -lreadline... " >&6; }
+if test "${ac_cv_lib_readline_rl_line_buffer+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lreadline $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char rl_line_buffer ();
+int
+main ()
+{
+return rl_line_buffer ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_readline_rl_line_buffer=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_readline_rl_line_buffer=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_readline_rl_line_buffer" >&5
+$as_echo "$ac_cv_lib_readline_rl_line_buffer" >&6; }
+if test "x$ac_cv_lib_readline_rl_line_buffer" = x""yes; then
+ READLINE_LIBS="-lreadline"
+else
+
+ { $as_echo "$as_me:$LINENO: result: linking -lreadline with $READLINE_LIBS needed" >&5
+$as_echo "linking -lreadline with $READLINE_LIBS needed" >&6; }
+ READLINE_LIBS="-lreadline $READLINE_LIBS"
+
+fi
+
+else
+
+ READLINE_LIBS=
+ if test "$READLINE" = yes; then
+ { { $as_echo "$as_me:$LINENO: error: GNU Readline could not be found which is required for the
+--enable-readline option (which is enabled by default). Either disable readline
+support with --disable-readline or download and install readline from:
+ ftp.gnu.org/gnu/readline
+Note: if you are using precompiled packages you will also need the development
+package as well (which may be called readline-devel or something similar)." >&5
+$as_echo "$as_me: error: GNU Readline could not be found which is required for the
+--enable-readline option (which is enabled by default). Either disable readline
+support with --disable-readline or download and install readline from:
+ ftp.gnu.org/gnu/readline
+Note: if you are using precompiled packages you will also need the development
+package as well (which may be called readline-devel or something similar)." >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+fi
+
+ LIBS="$READLINE_LIBS $lvm_saved_libs"
+
+for ac_func in rl_completion_matches
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ eval "$as_ac_var=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+ LIBS=$lvm_saved_libs
+fi
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking whether to enable internationalisation" >&5
+$as_echo_n "checking whether to enable internationalisation... " >&6; }
+# Check whether --enable-nls was given.
+if test "${enable_nls+set}" = set; then
+ enableval=$enable_nls; INTL=$enableval
+else
+ INTL=no
+fi
+
+{ $as_echo "$as_me:$LINENO: result: $INTL" >&5
+$as_echo "$INTL" >&6; }
+
+if test x$INTL = xyes; then
+# FIXME - Move this - can be device-mapper too
+ INTL_PACKAGE="lvm2"
+ # Extract the first word of "msgfmt", so it can be a program name with args.
+set dummy msgfmt; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_MSGFMT+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $MSGFMT in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MSGFMT="$MSGFMT" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_MSGFMT="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+MSGFMT=$ac_cv_path_MSGFMT
+if test -n "$MSGFMT"; then
+ { $as_echo "$as_me:$LINENO: result: $MSGFMT" >&5
+$as_echo "$MSGFMT" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if [ "x$MSGFMT" == x ];
+ then { { $as_echo "$as_me:$LINENO: error: msgfmt not found in path $PATH
+ " >&5
+$as_echo "$as_me: error: msgfmt not found in path $PATH
+ " >&2;}
+ { (exit 1); exit 1; }; }
+ fi;
+
+
+# Check whether --with-localedir was given.
+if test "${with_localedir+set}" = set; then
+ withval=$with_localedir; LOCALEDIR=$withval
+else
+ LOCALEDIR='${prefix}/share/locale'
+fi
+
+fi
+
+################################################################################
+
+# Check whether --with-confdir was given.
+if test "${with_confdir+set}" = set; then
+ withval=$with_confdir; CONFDIR=$withval
+else
+ CONFDIR="/etc"
+fi
+
+
+
+# Check whether --with-staticdir was given.
+if test "${with_staticdir+set}" = set; then
+ withval=$with_staticdir; STATICDIR=$withval
+else
+ STATICDIR='${exec_prefix}/sbin'
+fi
+
+
+
+# Check whether --with-usrlibdir was given.
+if test "${with_usrlibdir+set}" = set; then
+ withval=$with_usrlibdir; usrlibdir=$withval
+else
+ usrlibdir='${prefix}/lib'
+fi
+
+
+
+# Check whether --with-usrsbindir was given.
+if test "${with_usrsbindir+set}" = set; then
+ withval=$with_usrsbindir; usrsbindir=$withval
+else
+ usrsbindir='${prefix}/sbin'
+fi
+
+
+################################################################################
+
+# Check whether --with-udev_prefix was given.
+if test "${with_udev_prefix+set}" = set; then
+ withval=$with_udev_prefix; udev_prefix=$withval
+else
+ udev_prefix='${exec_prefix}'
+fi
+
+
+
+# Check whether --with-udevdir was given.
+if test "${with_udevdir+set}" = set; then
+ withval=$with_udevdir; udevdir=$withval
+else
+ udevdir='${udev_prefix}/lib/udev/rules.d'
+fi
+
+
+################################################################################
+if test x$READLINE = xyes; then
+
+
+for ac_header in readline/readline.h readline/history.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+done
+
+fi
+
+if test x$CLVMD != xnone; then
+
+
+
+
+
+
+
+
+
+
+for ac_header in mntent.h netdb.h netinet/in.h pthread.h search.h sys/mount.h sys/socket.h sys/uio.h sys/un.h utmpx.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+done
+
+
+
+
+
+
+for ac_func in dup2 getmntent memmove select socket
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ eval "$as_ac_var=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+done
+
+ # getmntent is in the standard C library on UNICOS, in -lsun on Irix 4,
+# -lseq on Dynix/PTX, -lgen on Unixware.
+{ $as_echo "$as_me:$LINENO: checking for library containing getmntent" >&5
+$as_echo_n "checking for library containing getmntent... " >&6; }
+if test "${ac_cv_search_getmntent+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char getmntent ();
+int
+main ()
+{
+return getmntent ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' sun seq gen; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_search_getmntent=$ac_res
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_getmntent+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_getmntent+set}" = set; then
+ :
+else
+ ac_cv_search_getmntent=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_search_getmntent" >&5
+$as_echo "$ac_cv_search_getmntent" >&6; }
+ac_res=$ac_cv_search_getmntent
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+ ac_cv_func_getmntent=yes
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GETMNTENT 1
+_ACEOF
+
+else
+ ac_cv_func_getmntent=no
+fi
+
+
+
+
+for ac_header in sys/select.h sys/socket.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:$LINENO: checking types of arguments for select" >&5
+$as_echo_n "checking types of arguments for select... " >&6; }
+if test "${ac_cv_func_select_args+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ for ac_arg234 in 'fd_set *' 'int *' 'void *'; do
+ for ac_arg1 in 'int' 'size_t' 'unsigned long int' 'unsigned int'; do
+ for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+int
+main ()
+{
+extern int select ($ac_arg1,
+ $ac_arg234, $ac_arg234, $ac_arg234,
+ $ac_arg5);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+ done
+done
+# Provide a safe default value.
+: ${ac_cv_func_select_args='int,int *,struct timeval *'}
+
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_select_args" >&5
+$as_echo "$ac_cv_func_select_args" >&6; }
+ac_save_IFS=$IFS; IFS=','
+set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'`
+IFS=$ac_save_IFS
+shift
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG1 $1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG234 ($2)
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG5 ($3)
+_ACEOF
+
+rm -f conftest*
+
+fi
+
+if test x$CLUSTER != xnone; then
+
+
+for ac_header in sys/socket.h sys/un.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+done
+
+
+for ac_func in socket
+do
+as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
+$as_echo_n "checking for $ac_func... " >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ eval "$as_ac_var=yes"
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+as_val=`eval 'as_val=${'$as_ac_var'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+done
+
+fi
+
+if test x$DMEVENTD = xyes; then
+
+for ac_header in arpa/inet.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+done
+
+fi
+
+if test x$HAVE_LIBDL = xyes; then
+
+for ac_header in dlfcn.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+done
+
+fi
+
+if test x$INTL = xyes; then
+
+for ac_header in libintl.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+done
+
+fi
+
+if test x$UDEV_SYNC = xyes; then
+
+
+for ac_header in sys/ipc.h sys/sem.h
+do
+as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5
+$as_echo_n "checking $ac_header usability... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5
+$as_echo_n "checking $ac_header presence... " >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+
+ ;;
+esac
+{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5
+$as_echo_n "checking for $ac_header... " >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ $as_echo_n "(cached) " >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ { $as_echo "$as_me:$LINENO: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+fi
+as_val=`eval 'as_val=${'$as_ac_Header'}
+ $as_echo "$as_val"'`
+ if test "x$as_val" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ { { $as_echo "$as_me:$LINENO: error: bailing out" >&5
+$as_echo "$as_me: error: bailing out" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+done
+
+fi
+
+################################################################################
+# Extract the first word of "modprobe", so it can be a program name with args.
+set dummy modprobe; ac_word=$2
+{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_MODPROBE_CMD+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ case $MODPROBE_CMD in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MODPROBE_CMD="$MODPROBE_CMD" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_MODPROBE_CMD="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+MODPROBE_CMD=$ac_cv_path_MODPROBE_CMD
+if test -n "$MODPROBE_CMD"; then
+ { $as_echo "$as_me:$LINENO: result: $MODPROBE_CMD" >&5
+$as_echo "$MODPROBE_CMD" >&6; }
+else
+ { $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+if test x$MODPROBE_CMD != x; then
+
+cat >>confdefs.h <<_ACEOF
+#define MODPROBE_CMD "$MODPROBE_CMD"
+_ACEOF
+
+fi
+
+
+lvm_exec_prefix=$exec_prefix
+test "$lvm_exec_prefix" = NONE && lvm_exec_prefix=$prefix
+test "$lvm_exec_prefix" = NONE && lvm_exec_prefix=$ac_default_prefix
+
+cat >>confdefs.h <<_ACEOF
+#define LVM_PATH "$lvm_exec_prefix/sbin/lvm"
+_ACEOF
+
+
+if test "$CLVMD" != none; then
+ clvmd_prefix=$ac_default_prefix
+ test "$prefix" != NONE && clvmd_prefix=$prefix
+
+cat >>confdefs.h <<_ACEOF
+#define CLVMD_PATH "$clvmd_prefix/sbin/clvmd"
+_ACEOF
+
+fi
+
+################################################################################
+if test "$BUILD_DMEVENTD" = yes; then
+
+# Check whether --with-dmeventd-pidfile was given.
+if test "${with_dmeventd_pidfile+set}" = set; then
+ withval=$with_dmeventd_pidfile; DMEVENTD_PIDFILE=$withval
+else
+ DMEVENTD_PIDFILE="/var/run/dmeventd.pid"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DMEVENTD_PIDFILE "$DMEVENTD_PIDFILE"
+_ACEOF
+
+fi
+
+if test "$BUILD_DMEVENTD" = yes; then
+
+# Check whether --with-dmeventd-path was given.
+if test "${with_dmeventd_path+set}" = set; then
+ withval=$with_dmeventd_path; DMEVENTD_PATH=$withval
+else
+ DMEVENTD_PATH="$lvm_exec_prefix/sbin/dmeventd"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DMEVENTD_PATH "$DMEVENTD_PATH"
+_ACEOF
+
+fi
+
+
+
+
+# Check whether --with-default-run-dir was given.
+if test "${with_default_run_dir+set}" = set; then
+ withval=$with_default_run_dir; DEFAULT_RUN_DIR="$withval"
+else
+ DEFAULT_RUN_DIR="/var/run/lvm"
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_RUN_DIR "$DEFAULT_RUN_DIR"
+_ACEOF
+
+
+################################################################################
+
+# Check whether --with-default-system-dir was given.
+if test "${with_default_system_dir+set}" = set; then
+ withval=$with_default_system_dir; DEFAULT_SYS_DIR=$withval
+else
+ DEFAULT_SYS_DIR="/etc/lvm"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_SYS_DIR "$DEFAULT_SYS_DIR"
+_ACEOF
+
+
+
+# Check whether --with-default-archive-subdir was given.
+if test "${with_default_archive_subdir+set}" = set; then
+ withval=$with_default_archive_subdir; DEFAULT_ARCHIVE_SUBDIR=$withval
+else
+ DEFAULT_ARCHIVE_SUBDIR=archive
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_ARCHIVE_SUBDIR "$DEFAULT_ARCHIVE_SUBDIR"
+_ACEOF
+
+
+
+# Check whether --with-default-backup-subdir was given.
+if test "${with_default_backup_subdir+set}" = set; then
+ withval=$with_default_backup_subdir; DEFAULT_BACKUP_SUBDIR=$withval
+else
+ DEFAULT_BACKUP_SUBDIR=backup
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_BACKUP_SUBDIR "$DEFAULT_BACKUP_SUBDIR"
+_ACEOF
+
+
+
+# Check whether --with-default-cache-subdir was given.
+if test "${with_default_cache_subdir+set}" = set; then
+ withval=$with_default_cache_subdir; DEFAULT_CACHE_SUBDIR=$withval
+else
+ DEFAULT_CACHE_SUBDIR=cache
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_CACHE_SUBDIR "$DEFAULT_CACHE_SUBDIR"
+_ACEOF
+
+
+
+# Check whether --with-default-locking-dir was given.
+if test "${with_default_locking_dir+set}" = set; then
+ withval=$with_default_locking_dir; DEFAULT_LOCK_DIR=$withval
+else
+ DEFAULT_LOCK_DIR="/var/lock/lvm"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_LOCK_DIR "$DEFAULT_LOCK_DIR"
+_ACEOF
+
+
+################################################################################
+
+# Check whether --with-default-data-alignment was given.
+if test "${with_default_data_alignment+set}" = set; then
+ withval=$with_default_data_alignment; DEFAULT_DATA_ALIGNMENT=$withval
+else
+ DEFAULT_DATA_ALIGNMENT=1
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_DATA_ALIGNMENT $DEFAULT_DATA_ALIGNMENT
+_ACEOF
+
+
+################################################################################
+{ $as_echo "$as_me:$LINENO: checking for kernel interface choice" >&5
+$as_echo_n "checking for kernel interface choice... " >&6; }
+
+# Check whether --with-interface was given.
+if test "${with_interface+set}" = set; then
+ withval=$with_interface; interface=$withval
+else
+ interface=ioctl
+fi
+
+if [ "x$interface" != xioctl ];
+then
+ { { $as_echo "$as_me:$LINENO: error: --with-interface=ioctl required. fs no longer supported." >&5
+$as_echo "$as_me: error: --with-interface=ioctl required. fs no longer supported." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+{ $as_echo "$as_me:$LINENO: result: $interface" >&5
+$as_echo "$interface" >&6; }
+
+################################################################################
+DM_LIB_VERSION="\"`cat "$srcdir"/VERSION_DM 2>/dev/null || echo Unknown`\""
+
+cat >>confdefs.h <<_ACEOF
+#define DM_LIB_VERSION $DM_LIB_VERSION
+_ACEOF
+
+
+DM_LIB_PATCHLEVEL=`cat "$srcdir"/VERSION_DM | $AWK -F '[-. ]' '{printf "%s.%s.%s",$1,$2,$3}'`
+
+LVM_VERSION="\"`cat "$srcdir"/VERSION 2>/dev/null || echo Unknown`\""
+
+VER=`cat "$srcdir"/VERSION`
+LVM_RELEASE_DATE="\"`echo $VER | $SED 's/.* (//;s/).*//'`\""
+VER=`echo "$VER" | $AWK '{print $1}'`
+LVM_RELEASE="\"`echo "$VER" | $AWK -F '-' '{print $2}'`\""
+VER=`echo "$VER" | $AWK -F '-' '{print $1}'`
+LVM_MAJOR=`echo "$VER" | $AWK -F '.' '{print $1}'`
+LVM_MINOR=`echo "$VER" | $AWK -F '.' '{print $2}'`
+LVM_PATCHLEVEL=`echo "$VER" | $AWK -F '[(.]' '{print $3}'`
+LVM_LIBAPI=`echo "$VER" | $AWK -F '[()]' '{print $2}'`
+
+################################################################################
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+################################################################################
+ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile doc/Makefile doc/example.conf include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/replicator/Makefile lib/misc/lvm-version.h lib/snapshot/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/lvm2_monitoring_init_red_hat scripts/Makefile test/Makefile test/api/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) $as_unset $ac_var ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ test "x$cache_file" != "x/dev/null" &&
+ { $as_echo "$as_me:$LINENO: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ cat confcache >$cache_file
+ else
+ { $as_echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line after each line using $LINENO; the second 'sed'
+ # does the real work. The second script uses 'N' to pair each
+ # line-number line with the line containing $LINENO, and appends
+ # trailing '-' during substitution so that $LINENO is not a special
+ # case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # scripts with optimization help from Paolo Bonzini. Blame Lee
+ # E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+ case `echo 'x\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ *) ECHO_C='\c';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+
+# Save the log message, to keep $[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.63. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTION]... [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <bug-autoconf@gnu.org>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.63,
+ with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2008 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ CONFIG_FILES="$CONFIG_FILES '$ac_optarg'"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ CONFIG_HEADERS="$CONFIG_HEADERS '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ { $as_echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { $as_echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "lib/misc/configure.h") CONFIG_HEADERS="$CONFIG_HEADERS lib/misc/configure.h" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "make.tmpl") CONFIG_FILES="$CONFIG_FILES make.tmpl" ;;
+ "daemons/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/Makefile" ;;
+ "daemons/clvmd/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/clvmd/Makefile" ;;
+ "daemons/cmirrord/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/cmirrord/Makefile" ;;
+ "daemons/dmeventd/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/Makefile" ;;
+ "daemons/dmeventd/libdevmapper-event.pc") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/libdevmapper-event.pc" ;;
+ "daemons/dmeventd/plugins/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/Makefile" ;;
+ "daemons/dmeventd/plugins/lvm2/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/lvm2/Makefile" ;;
+ "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" ;;
+ "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
+ "doc/example.conf") CONFIG_FILES="$CONFIG_FILES doc/example.conf" ;;
+ "include/.symlinks") CONFIG_FILES="$CONFIG_FILES include/.symlinks" ;;
+ "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
+ "lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;;
+ "lib/format1/Makefile") CONFIG_FILES="$CONFIG_FILES lib/format1/Makefile" ;;
+ "lib/format_pool/Makefile") CONFIG_FILES="$CONFIG_FILES lib/format_pool/Makefile" ;;
+ "lib/locking/Makefile") CONFIG_FILES="$CONFIG_FILES lib/locking/Makefile" ;;
+ "lib/mirror/Makefile") CONFIG_FILES="$CONFIG_FILES lib/mirror/Makefile" ;;
+ "lib/replicator/Makefile") CONFIG_FILES="$CONFIG_FILES lib/replicator/Makefile" ;;
+ "lib/misc/lvm-version.h") CONFIG_FILES="$CONFIG_FILES lib/misc/lvm-version.h" ;;
+ "lib/snapshot/Makefile") CONFIG_FILES="$CONFIG_FILES lib/snapshot/Makefile" ;;
+ "libdm/Makefile") CONFIG_FILES="$CONFIG_FILES libdm/Makefile" ;;
+ "libdm/libdevmapper.pc") CONFIG_FILES="$CONFIG_FILES libdm/libdevmapper.pc" ;;
+ "liblvm/Makefile") CONFIG_FILES="$CONFIG_FILES liblvm/Makefile" ;;
+ "liblvm/liblvm2app.pc") CONFIG_FILES="$CONFIG_FILES liblvm/liblvm2app.pc" ;;
+ "man/Makefile") CONFIG_FILES="$CONFIG_FILES man/Makefile" ;;
+ "po/Makefile") CONFIG_FILES="$CONFIG_FILES po/Makefile" ;;
+ "scripts/clvmd_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/clvmd_init_red_hat" ;;
+ "scripts/cmirrord_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/cmirrord_init_red_hat" ;;
+ "scripts/lvm2_monitoring_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_monitoring_init_red_hat" ;;
+ "scripts/Makefile") CONFIG_FILES="$CONFIG_FILES scripts/Makefile" ;;
+ "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
+ "test/api/Makefile") CONFIG_FILES="$CONFIG_FILES test/api/Makefile" ;;
+ "tools/Makefile") CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;;
+ "udev/Makefile") CONFIG_FILES="$CONFIG_FILES udev/Makefile" ;;
+ "unit-tests/datastruct/Makefile") CONFIG_FILES="$CONFIG_FILES unit-tests/datastruct/Makefile" ;;
+ "unit-tests/regex/Makefile") CONFIG_FILES="$CONFIG_FILES unit-tests/regex/Makefile" ;;
+ "unit-tests/mm/Makefile") CONFIG_FILES="$CONFIG_FILES unit-tests/mm/Makefile" ;;
+
+ *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+$as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp=
+ trap 'exit_status=$?
+ { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} ||
+{
+ $as_echo "$as_me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr='\r'
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\).*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\).*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = "\a"
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
+ || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5
+$as_echo "$as_me: error: could not setup config files machinery" >&2;}
+ { (exit 1); exit 1; }; }
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/
+s/:*\${srcdir}:*/:/
+s/:*@srcdir@:*/:/
+s/^\([^=]*=[ ]*\):*/\1/
+s/:*$//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+ ac_t=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_t"; then
+ break
+ elif $ac_last_try; then
+ { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_HEADERS" >&5
+$as_echo "$as_me: error: could not make $CONFIG_HEADERS" >&2;}
+ { (exit 1); exit 1; }; }
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any. Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[ ]*#[ ]*define[ ][ ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ for (key in D) D_is_set[key] = 1
+ FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+ line = \$ 0
+ split(line, arg, " ")
+ if (arg[1] == "#") {
+ defundef = arg[2]
+ mac1 = arg[3]
+ } else {
+ defundef = substr(arg[1], 2)
+ mac1 = arg[2]
+ }
+ split(mac1, mac2, "(") #)
+ macro = mac2[1]
+ prefix = substr(line, 1, index(line, defundef) - 1)
+ if (D_is_set[macro]) {
+ # Preserve the white space surrounding the "#".
+ print prefix "define", macro P[macro] D[macro]
+ next
+ } else {
+ # Replace #undef with comments. This is necessary, for example,
+ # in the case of _POSIX_SOURCE, which is predefined and required
+ # on some systems where configure will not decide to define it.
+ if (defundef == "undef") {
+ print "/*", prefix defundef, macro, "*/"
+ next
+ }
+ }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ { { $as_echo "$as_me:$LINENO: error: could not setup config headers machinery" >&5
+$as_echo "$as_me: error: could not setup config headers machinery" >&2;}
+ { (exit 1); exit 1; }; }
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS "
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5
+$as_echo "$as_me: error: invalid tag $ac_tag" >&2;}
+ { (exit 1); exit 1; }; };;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5
+$as_echo "$as_me: error: cannot find input file: $ac_f" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ ac_file_inputs="$ac_file_inputs '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:$LINENO: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$tmp/stdin" \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; } ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ { as_dir="$ac_dir"
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
+$as_echo "$as_me: error: cannot create directory $as_dir" >&2;}
+ { (exit 1); exit 1; }; }; }
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+ ac_MKDIR_P=$MKDIR_P
+ case $MKDIR_P in
+ [\\/$]* | ?:[\\/]* ) ;;
+ */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p
+'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; }
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined." >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined." >&2;}
+
+ rm -f "$tmp/stdin"
+ case $ac_file in
+ -) cat "$tmp/out" && rm -f "$tmp/out";;
+ *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
+ esac \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; }
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+ if test x"$ac_file" != x-; then
+ {
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs"
+ } >"$tmp/config.h" \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; }
+ if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then
+ { $as_echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f "$ac_file"
+ mv "$tmp/config.h" "$ac_file" \
+ || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5
+$as_echo "$as_me: error: could not create $ac_file" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ else
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \
+ || { { $as_echo "$as_me:$LINENO: error: could not create -" >&5
+$as_echo "$as_me: error: could not create -" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ ;;
+
+
+ esac
+
+done # for ac_tag
+
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ { { $as_echo "$as_me:$LINENO: error: write failure creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: error: write failure creating $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
+if test x$ODIRECT != xyes; then
+ { $as_echo "$as_me:$LINENO: WARNING: Warning: O_DIRECT disabled: low-memory pvmove may lock up" >&5
+$as_echo "$as_me: WARNING: Warning: O_DIRECT disabled: low-memory pvmove may lock up" >&2;}
+fi
--- /dev/null
+###############################################################################
+## Copyright (C) 2000-2004 Sistina Software, Inc. All rights reserved.
+## Copyright (C) 2004-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+################################################################################
+
+AC_PREREQ(2.61)
+################################################################################
+dnl -- Process this file with autoconf to produce a configure script.
+AC_INIT
+AC_CONFIG_SRCDIR([lib/device/dev-cache.h])
+AC_CONFIG_HEADERS([lib/misc/configure.h])
+
+################################################################################
+dnl -- Setup the directory where autoconf has auxilary files
+AC_CONFIG_AUX_DIR(autoconf)
+
+################################################################################
+dnl -- Get system type
+AC_CANONICAL_TARGET([])
+
+case "$host_os" in
+ linux*)
+ CFLAGS="$CFLAGS"
+ COPTIMISE_FLAG="-O2"
+ CLDFLAGS="$CLDFLAGS -Wl,--version-script,.export.sym"
+ CLDWHOLEARCHIVE="-Wl,-whole-archive"
+ CLDNOWHOLEARCHIVE="-Wl,-no-whole-archive"
+ LDDEPS="$LDDEPS .export.sym"
+ LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+ LIB_SUFFIX=so
+ DEVMAPPER=yes
+ ODIRECT=yes
+ DM_IOCTLS=yes
+ SELINUX=yes
+ CLUSTER=internal
+ FSADM=yes
+ ;;
+ darwin*)
+ CFLAGS="$CFLAGS -no-cpp-precomp -fno-common"
+ COPTIMISE_FLAG="-O2"
+ CLDFLAGS="$CLDFLAGS"
+ CLDWHOLEARCHIVE="-all_load"
+ CLDNOWHOLEARCHIVE=
+ LIB_SUFFIX=dylib
+ DEVMAPPER=yes
+ ODIRECT=no
+ DM_IOCTLS=no
+ SELINUX=no
+ CLUSTER=none
+ FSADM=no
+ ;;
+esac
+
+################################################################################
+dnl -- Checks for programs.
+AC_PROG_SED
+AC_PROG_AWK
+AC_PROG_CC
+
+dnl probably no longer needed in 2008, but...
+AC_PROG_GCC_TRADITIONAL
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AC_PROG_MKDIR_P
+AC_PROG_RANLIB
+AC_PATH_PROG(CFLOW_CMD, cflow)
+AC_PATH_PROG(CSCOPE_CMD, cscope)
+
+################################################################################
+dnl -- Check for header files.
+AC_HEADER_DIRENT
+AC_HEADER_MAJOR
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+AC_HEADER_TIME
+
+AC_CHECK_HEADERS([locale.h stddef.h syslog.h sys/file.h sys/time.h assert.h \
+ langinfo.h libgen.h signal.h sys/mman.h sys/resource.h sys/utsname.h \
+ sys/wait.h time.h], ,
+ [AC_MSG_ERROR(bailing out)])
+
+case "$host_os" in
+ linux*)
+ AC_CHECK_HEADERS(asm/byteorder.h linux/fs.h malloc.h,,AC_MSG_ERROR(bailing out)) ;;
+ darwin*)
+ AC_CHECK_HEADERS(machine/endian.h sys/disk.h,,AC_MSG_ERROR(bailing out)) ;;
+esac
+
+AC_CHECK_HEADERS([ctype.h dirent.h errno.h fcntl.h getopt.h inttypes.h limits.h \
+ stdarg.h stdio.h stdlib.h string.h sys/ioctl.h sys/param.h sys/stat.h \
+ sys/types.h unistd.h], , [AC_MSG_ERROR(bailing out)])
+AC_CHECK_HEADERS(termios.h sys/statvfs.h)
+
+################################################################################
+dnl -- Check for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_C_INLINE
+AC_CHECK_MEMBERS([struct stat.st_rdev])
+AC_TYPE_OFF_T
+AC_TYPE_PID_T
+AC_TYPE_SIGNAL
+AC_TYPE_SIZE_T
+AC_TYPE_MODE_T
+AC_TYPE_INT8_T
+AC_TYPE_INT16_T
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UID_T
+AC_TYPE_UINT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_CHECK_MEMBERS([struct stat.st_rdev])
+AC_STRUCT_TM
+
+################################################################################
+dnl -- Check for functions
+AC_CHECK_FUNCS([ftruncate gethostname getpagesize \
+ gettimeofday memset mkdir mkfifo rmdir munmap nl_langinfo setenv setlocale \
+ strcasecmp strchr strcspn strspn strdup strncasecmp strerror strrchr \
+ strstr strtol strtoul uname], , [AC_MSG_ERROR(bailing out)])
+AC_CHECK_FUNCS(siginterrupt)
+AC_FUNC_ALLOCA
+AC_FUNC_CLOSEDIR_VOID
+AC_FUNC_CHOWN
+AC_FUNC_FORK
+AC_FUNC_LSTAT
+AC_FUNC_MALLOC
+AC_FUNC_MEMCMP
+AC_FUNC_MMAP
+AC_FUNC_REALLOC
+AC_FUNC_STAT
+AC_FUNC_STRTOD
+AC_FUNC_VPRINTF
+
+################################################################################
+dnl -- Enables statically-linked tools
+AC_MSG_CHECKING(whether to use static linking)
+AC_ARG_ENABLE(static_link,
+ AC_HELP_STRING([--enable-static_link],
+ [use this to link the tools to their libraries
+ statically (default is dynamic linking]),
+ STATIC_LINK=$enableval, STATIC_LINK=no)
+AC_MSG_RESULT($STATIC_LINK)
+
+################################################################################
+dnl -- Prefix is /usr by default, the exec_prefix default is setup later
+AC_PREFIX_DEFAULT(/usr)
+
+################################################################################
+dnl -- Setup the ownership of the files
+AC_MSG_CHECKING(file owner)
+AC_ARG_WITH(user,
+ AC_HELP_STRING([--with-user=USER],
+ [set the owner of installed files [[USER=]]]),
+ OWNER=$withval)
+AC_MSG_RESULT($OWNER)
+
+if test x$OWNER != x; then
+ INSTALL="$INSTALL -o $OWNER"
+fi
+
+################################################################################
+dnl -- Setup the group ownership of the files
+AC_MSG_CHECKING(group owner)
+AC_ARG_WITH(group,
+ AC_HELP_STRING([--with-group=GROUP],
+ [set the group owner of installed files [[GROUP=]]]),
+ GROUP=$withval)
+AC_MSG_RESULT($GROUP)
+
+if test x$GROUP != x; then
+ INSTALL="$INSTALL -g $GROUP"
+fi
+
+################################################################################
+dnl -- Setup device node ownership
+AC_MSG_CHECKING(device node uid)
+
+AC_ARG_WITH(device-uid,
+ AC_HELP_STRING([--with-device-uid=UID],
+ [set the owner used for new device nodes [[UID=0]]]),
+ DM_DEVICE_UID=$withval, DM_DEVICE_UID=0)
+AC_MSG_RESULT($DM_DEVICE_UID)
+
+################################################################################
+dnl -- Setup device group ownership
+AC_MSG_CHECKING(device node gid)
+
+AC_ARG_WITH(device-gid,
+ AC_HELP_STRING([--with-device-gid=GID],
+ [set the group used for new device nodes [[GID=0]]]),
+ DM_DEVICE_GID=$withval, DM_DEVICE_GID=0)
+AC_MSG_RESULT($DM_DEVICE_GID)
+
+################################################################################
+dnl -- Setup device mode
+AC_MSG_CHECKING(device node mode)
+
+AC_ARG_WITH(device-mode,
+ AC_HELP_STRING([--with-device-mode=MODE],
+ [set the mode used for new device nodes [[MODE=0600]]]),
+ DM_DEVICE_MODE=$withval, DM_DEVICE_MODE=0600)
+AC_MSG_RESULT($DM_DEVICE_MODE)
+
+################################################################################
+dnl -- LVM1 tool fallback option
+AC_MSG_CHECKING(whether to enable lvm1 fallback)
+AC_ARG_ENABLE(lvm1_fallback,
+ AC_HELP_STRING([--enable-lvm1_fallback],
+ [use this to fall back and use LVM1 binaries if
+ device-mapper is missing from the kernel]),
+ LVM1_FALLBACK=$enableval, LVM1_FALLBACK=no)
+AC_MSG_RESULT($LVM1_FALLBACK)
+
+if test x$LVM1_FALLBACK = xyes; then
+ AC_DEFINE([LVM1_FALLBACK], 1, [Define to 1 if 'lvm' should fall back to using LVM1 binaries if device-mapper is missing from the kernel])
+fi
+
+################################################################################
+dnl -- format1 inclusion type
+AC_MSG_CHECKING(whether to include support for lvm1 metadata)
+AC_ARG_WITH(lvm1,
+ AC_HELP_STRING([--with-lvm1=TYPE],
+ [LVM1 metadata support: internal/shared/none
+ [[TYPE=internal]]]),
+ LVM1=$withval, LVM1=internal)
+AC_MSG_RESULT($LVM1)
+
+if [[ "x$LVM1" != xnone -a "x$LVM1" != xinternal -a "x$LVM1" != xshared ]];
+ then AC_MSG_ERROR(
+--with-lvm1 parameter invalid
+)
+fi;
+
+if test x$LVM1 = xinternal; then
+ AC_DEFINE([LVM1_INTERNAL], 1, [Define to 1 to include built-in support for LVM1 metadata.])
+fi
+
+################################################################################
+dnl -- format_pool inclusion type
+AC_MSG_CHECKING(whether to include support for GFS pool metadata)
+AC_ARG_WITH(pool,
+ AC_HELP_STRING([--with-pool=TYPE],
+ [GFS pool read-only support: internal/shared/none
+ [[TYPE=internal]]]),
+ POOL=$withval, POOL=internal)
+AC_MSG_RESULT($POOL)
+
+if [[ "x$POOL" != xnone -a "x$POOL" != xinternal -a "x$POOL" != xshared ]];
+ then AC_MSG_ERROR(
+--with-pool parameter invalid
+)
+fi;
+
+if test x$POOL = xinternal; then
+ AC_DEFINE([POOL_INTERNAL], 1, [Define to 1 to include built-in support for GFS pool metadata.])
+fi
+
+################################################################################
+dnl -- cluster_locking inclusion type
+AC_MSG_CHECKING(whether to include support for cluster locking)
+AC_ARG_WITH(cluster,
+ AC_HELP_STRING([--with-cluster=TYPE],
+ [cluster LVM locking support: internal/shared/none
+ [[TYPE=internal]]]),
+ CLUSTER=$withval)
+AC_MSG_RESULT($CLUSTER)
+
+if [[ "x$CLUSTER" != xnone -a "x$CLUSTER" != xinternal -a "x$CLUSTER" != xshared ]];
+ then AC_MSG_ERROR(
+--with-cluster parameter invalid
+)
+fi;
+
+if test x$CLUSTER = xinternal; then
+ AC_DEFINE([CLUSTER_LOCKING_INTERNAL], 1, [Define to 1 to include built-in support for clustered LVM locking.])
+fi
+
+################################################################################
+dnl -- snapshots inclusion type
+AC_MSG_CHECKING(whether to include snapshots)
+AC_ARG_WITH(snapshots,
+ AC_HELP_STRING([--with-snapshots=TYPE],
+ [snapshot support: internal/shared/none
+ [[TYPE=internal]]]),
+ SNAPSHOTS=$withval, SNAPSHOTS=internal)
+AC_MSG_RESULT($SNAPSHOTS)
+
+if [[ "x$SNAPSHOTS" != xnone -a "x$SNAPSHOTS" != xinternal -a "x$SNAPSHOTS" != xshared ]];
+ then AC_MSG_ERROR(
+--with-snapshots parameter invalid
+)
+fi;
+
+if test x$SNAPSHOTS = xinternal; then
+ AC_DEFINE([SNAPSHOT_INTERNAL], 1, [Define to 1 to include built-in support for snapshots.])
+fi
+
+################################################################################
+dnl -- mirrors inclusion type
+AC_MSG_CHECKING(whether to include mirrors)
+AC_ARG_WITH(mirrors,
+ AC_HELP_STRING([--with-mirrors=TYPE],
+ [mirror support: internal/shared/none
+ [[TYPE=internal]]]),
+ MIRRORS=$withval, MIRRORS=internal)
+AC_MSG_RESULT($MIRRORS)
+
+if [[ "x$MIRRORS" != xnone -a "x$MIRRORS" != xinternal -a "x$MIRRORS" != xshared ]];
+ then AC_MSG_ERROR(
+--with-mirrors parameter invalid
+)
+fi;
+
+if test x$MIRRORS = xinternal; then
+ AC_DEFINE([MIRRORED_INTERNAL], 1, [Define to 1 to include built-in support for mirrors.])
+fi
+
+################################################################################
+dnl -- asynchronous volume replicator inclusion type
+AC_MSG_CHECKING(whether to include replicators)
+AC_ARG_WITH(replicators,
+ AC_HELP_STRING([--with-replicators=TYPE],
+ [replicator support: internal/shared/none
+ [[TYPE=none]]]),
+ REPLICATORS=$withval, REPLICATORS=none)
+AC_MSG_RESULT($REPLICATORS)
+
+case "$REPLICATORS" in
+ none|shared) ;;
+ internal) AC_DEFINE([REPLICATOR_INTERNAL], 1,
+ [Define to 1 to include built-in support for replicators.]) ;;
+ *) AC_MSG_ERROR([--with-replicators parameter invalid ($REPLICATORS)]) ;;
+esac
+
+################################################################################
+dnl -- Disable readline
+AC_MSG_CHECKING(whether to enable readline)
+AC_ARG_ENABLE([readline],
+ AC_HELP_STRING([--disable-readline], [disable readline support]),
+ READLINE=$enableval, READLINE=maybe)
+AC_MSG_RESULT($READLINE)
+
+################################################################################
+dnl -- Disable realtime clock support
+AC_MSG_CHECKING(whether to enable realtime support)
+AC_ARG_ENABLE(realtime,
+ AC_HELP_STRING([--enable-realtime], [enable realtime clock support]),
+ REALTIME=$enableval)
+AC_MSG_RESULT($REALTIME)
+
+################################################################################
+dnl -- disable OCF resource agents
+AC_MSG_CHECKING(whether to enable OCF resource agents)
+AC_ARG_ENABLE(ocf,
+ AC_HELP_STRING([--enable-ocf],
+ [enable Open Cluster Framework (OCF) compliant resource agents]),
+ OCF=$enableval, OCF=no)
+AC_MSG_RESULT($OCF)
+
+################################################################################
+dnl -- Init pkg-config with dummy invokation:
+dnl -- this is required because PKG_CHECK_MODULES macro is expanded
+dnl -- to initialize the pkg-config environment only at the first invokation,
+dnl -- that would be conditional in this configure.in.
+pkg_config_init() {
+ PKG_CHECK_MODULES(PKGCONFIGINIT, pkgconfiginit, [],
+ [AC_MSG_RESULT([pkg-config initialized])])
+ PKGCONFIG_INIT=1
+}
+
+################################################################################
+dnl -- Build cluster LVM daemon
+AC_MSG_CHECKING(whether to build cluster LVM daemon)
+AC_ARG_WITH(clvmd,
+ [ --with-clvmd=TYPE build cluster LVM Daemon
+ The following cluster manager combinations are valid:
+ * cman,gulm (RHEL4 or equivalent)
+ * cman (RHEL5 or equivalent)
+ * cman,corosync,openais (or selection of them)
+ * singlenode (localhost only)
+ * all (autodetect)
+ * none (disable build)
+ [[TYPE=none]]],
+ CLVMD=$withval, CLVMD=none)
+if test x$CLVMD = xyes; then
+ CLVMD=all
+fi
+AC_MSG_RESULT($CLVMD)
+
+dnl -- If clvmd enabled without cluster locking, automagically include it
+if test x$CLVMD != xnone && test x$CLUSTER = xnone; then
+ CLUSTER=internal
+fi
+
+dnl -- init pkgconfig if required
+if test x$CLVMD != xnone && test x$PKGCONFIG_INIT != x1; then
+ pkg_config_init
+fi
+
+dnl -- Express clvmd init script Required-Start / Required-Stop
+CLVMD_CMANAGERS=""
+dnl -- On RHEL4/RHEL5, qdiskd is started from a separate init script.
+dnl -- Enable if we are build for either cman or gulm.
+CLVMD_NEEDS_QDISKD=no
+
+dnl -- define build types
+if [[ `expr x"$CLVMD" : '.*gulm.*'` != 0 ]]; then
+ BUILDGULM=yes
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS lock_gulmd"
+ CLVMD_NEEDS_QDISKD=yes
+fi
+if [[ `expr x"$CLVMD" : '.*cman.*'` != 0 ]]; then
+ BUILDCMAN=yes
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS cman"
+ CLVMD_NEEDS_QDISKD=yes
+fi
+if [[ `expr x"$CLVMD" : '.*corosync.*'` != 0 ]]; then
+ BUILDCOROSYNC=yes
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS corosync"
+fi
+if [[ `expr x"$CLVMD" : '.*openais.*'` != 0 ]]; then
+ BUILDOPENAIS=yes
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS openais"
+fi
+if test x$CLVMD_NEEDS_QDISKD != xno; then
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS qdiskd"
+fi
+
+dnl -- sanity check around user selection
+if test x$BUILDGULM = xyes; then
+ if test x$BUILDCOROSYNC = xyes || \
+ test x$BUILDOPENAIS = xyes; then
+ AC_MSG_ERROR([requested clvmd configuration is not valid])
+ fi
+fi
+
+dnl -- define a soft bailout if we are autodetecting
+soft_bailout() {
+ NOTFOUND=1
+}
+
+hard_bailout() {
+ AC_MSG_ERROR([bailing out])
+}
+
+dnl -- if clvmd=all then set soft_bailout (we don't want to error)
+dnl -- and set all builds to yes. We need to do this here
+dnl -- to skip the gulm + openais|corosync sanity check above.
+if test x$CLVMD = xall; then
+ bailout=soft_bailout
+ BUILDGULM=yes
+ BUILDCMAN=yes
+ BUILDCOROSYNC=yes
+ BUILDOPENAIS=yes
+else
+ bailout=hard_bailout
+fi
+
+dnl -- helper macro to check libs without adding them to LIBS
+check_lib_no_libs() {
+ lib_no_libs_arg1=$1
+ shift
+ lib_no_libs_arg2=$1
+ shift
+ lib_no_libs_args=$@
+ AC_CHECK_LIB([$lib_no_libs_arg1],
+ [$lib_no_libs_arg2],,
+ [$bailout],
+ [$lib_no_libs_args])
+ LIBS=$ac_check_lib_save_LIBS
+}
+
+dnl -- Look for gulm libraries if required.
+if test x$BUILDGULM = xyes; then
+ PKG_CHECK_MODULES(CCS, libccs, [HAVE_CCS=yes],
+ [NOTFOUND=0
+ AC_CHECK_HEADERS(ccs.h,,$bailout)
+ check_lib_no_libs ccs ccs_connect
+ if test $NOTFOUND = 0; then
+ AC_MSG_RESULT([no pkg for libccs, using -lccs])
+ CCS_LIBS="-lccs"
+ HAVE_CCS=yes
+ fi])
+ PKG_CHECK_MODULES(GULM, libgulm, [HAVE_GULM=yes],
+ [NOTFOUND=0
+ AC_CHECK_HEADERS(libgulm.h,,$bailout)
+ check_lib_no_libs gulm lg_core_login
+ if test $NOTFOUND = 0; then
+ AC_MSG_RESULT([no pkg for libgulm, using -lgulm])
+ GULM_LIBS="-lgulm"
+ HAVE_GULM=yes
+ fi])
+fi
+
+dnl -- Look for cman libraries if required.
+if test x$BUILDCMAN = xyes; then
+ PKG_CHECK_MODULES(CMAN, libcman, [HAVE_CMAN=yes],
+ [NOTFOUND=0
+ AC_CHECK_HEADERS(libcman.h,,$bailout)
+ check_lib_no_libs cman cman_init
+ if test $NOTFOUND = 0; then
+ AC_MSG_RESULT([no pkg for libcman, using -lcman])
+ CMAN_LIBS="-lcman"
+ HAVE_CMAN=yes
+ fi])
+ CHECKCONFDB=yes
+ CHECKDLM=yes
+fi
+
+dnl -- Look for corosync that's required also for openais build
+dnl -- only enough recent version of corosync ship pkg-config files.
+dnl -- We can safely rely on that to detect the correct bits.
+if test x$BUILDCOROSYNC = xyes || \
+ test x$BUILDOPENAIS = xyes; then
+ PKG_CHECK_MODULES(COROSYNC, corosync, [HAVE_COROSYNC=yes], $bailout)
+ CHECKCONFDB=yes
+fi
+
+dnl -- Look for corosync libraries if required.
+if test x$BUILDCOROSYNC = xyes; then
+ PKG_CHECK_MODULES(QUORUM, libquorum, [HAVE_QUORUM=yes], $bailout)
+ CHECKCPG=yes
+ CHECKDLM=yes
+fi
+
+dnl -- Look for openais libraries if required.
+if test x$BUILDOPENAIS = xyes; then
+ PKG_CHECK_MODULES(SALCK, libSaLck, [HAVE_SALCK=yes], $bailout)
+ CHECKCPG=yes
+fi
+
+dnl -- Below are checks for libraries common to more than one build.
+
+dnl -- Check confdb library.
+dnl -- mandatory for corosync build.
+dnl -- optional for openais/cman build.
+
+if test x$CHECKCONFDB = xyes; then
+ PKG_CHECK_MODULES(CONFDB, libconfdb,
+ [HAVE_CONFDB=yes],
+ [HAVE_CONFDB=no])
+
+ AC_CHECK_HEADERS(corosync/confdb.h,
+ [HAVE_CONFDB_H=yes],
+ [HAVE_CONFDB_H=no])
+
+ if test x$HAVE_CONFDB != xyes && \
+ test x$HAVE_CONFDB_H = xyes; then
+ check_lib_no_libs confdb confdb_initialize
+ AC_MSG_RESULT([no pkg for confdb, using -lconfdb])
+ CONFDB_LIBS="-lconfdb"
+ HAVE_CONFDB=yes
+ fi
+
+ if test x$BUILDCOROSYNC = xyes && \
+ test x$HAVE_CONFDB != xyes &&
+ test x$CLVMD != xall; then
+ AC_MSG_ERROR([bailing out... confdb library is required])
+ fi
+fi
+
+dnl -- Check cpg library.
+if test x$CHECKCPG = xyes; then
+ PKG_CHECK_MODULES(CPG, libcpg, [HAVE_CPG=yes], $bailout)
+fi
+
+dnl -- Check dlm library.
+if test x$CHECKDLM = xyes; then
+ PKG_CHECK_MODULES(DLM, libdlm, [HAVE_DLM=yes],
+ [NOTFOUND=0
+ AC_CHECK_HEADERS(libdlm.h,,$bailout)
+ check_lib_no_libs dlm dlm_lock -lpthread
+ if test $NOTFOUND = 0; then
+ AC_MSG_RESULT([no pkg for libdlm, using -ldlm])
+ DLM_LIBS="-ldlm -lpthread"
+ HAVE_DLM=yes
+ fi])
+fi
+
+dnl -- If we are autodetecting, we need to re-create
+dnl -- the depedencies checks and set a proper CLVMD,
+dnl -- together with init script Required-Start/Stop entries.
+if test x$CLVMD = xall; then
+ CLVMD=none
+ CLVMD_CMANAGERS=""
+ CLVMD_NEEDS_QDISKD=no
+ if test x$HAVE_CCS = xyes && \
+ test x$HAVE_GULM = xyes; then
+ AC_MSG_RESULT([Enabling clvmd gulm cluster manager])
+ CLVMD="$CLVMD,gulm"
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS lock_gulmd"
+ CLVMD_NEEDS_QDISKD=yes
+ fi
+ if test x$HAVE_CMAN = xyes && \
+ test x$HAVE_DLM = xyes; then
+ AC_MSG_RESULT([Enabling clvmd cman cluster manager])
+ CLVMD="$CLVMD,cman"
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS cman"
+ CLVMD_NEEDS_QDISKD=yes
+ fi
+ if test x$HAVE_COROSYNC = xyes && \
+ test x$HAVE_QUORUM = xyes && \
+ test x$HAVE_CPG = xyes && \
+ test x$HAVE_DLM = xyes && \
+ test x$HAVE_CONFDB = xyes; then
+ AC_MSG_RESULT([Enabling clvmd corosync cluster manager])
+ CLVMD="$CLVMD,corosync"
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS corosync"
+ fi
+ if test x$HAVE_COROSYNC = xyes && \
+ test x$HAVE_CPG = xyes && \
+ test x$HAVE_SALCK = xyes; then
+ AC_MSG_RESULT([Enabling clvmd openais cluster manager])
+ CLVMD="$CLVMD,openais"
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS openais"
+ fi
+ if test x$CLVMD_NEEDS_QDISKD != xno; then
+ CLVMD_CMANAGERS="$CLVMD_CMANAGERS qdiskd"
+ fi
+ if test x$CLVMD = xnone; then
+ AC_MSG_RESULT([Disabling clvmd build. No cluster manager detected.])
+ fi
+fi
+
+################################################################################
+dnl -- clvmd pidfile
+if test "x$CLVMD" != xnone; then
+ AC_ARG_WITH(clvmd-pidfile,
+ AC_HELP_STRING([--with-clvmd-pidfile=PATH],
+ [clvmd pidfile [[/var/run/clvmd.pid]]]),
+ CLVMD_PIDFILE=$withval,
+ CLVMD_PIDFILE="/var/run/clvmd.pid")
+ AC_DEFINE_UNQUOTED(CLVMD_PIDFILE, ["$CLVMD_PIDFILE"],
+ [Path to clvmd pidfile.])
+fi
+
+################################################################################
+dnl -- Build cluster mirror log daemon
+AC_MSG_CHECKING(whether to build cluster mirror log daemon)
+AC_ARG_ENABLE(cmirrord,
+ AC_HELP_STRING([--enable-cmirrord],
+ [enable the cluster mirror log daemon]),
+ CMIRRORD=$enableval, CMIRRORD=no)
+AC_MSG_RESULT($CMIRRORD)
+
+BUILD_CMIRRORD=$CMIRRORD
+
+################################################################################
+dnl -- cmirrord pidfile
+if test "x$BUILD_CMIRRORD" = xyes; then
+ AC_ARG_WITH(cmirrord-pidfile,
+ AC_HELP_STRING([--with-cmirrord-pidfile=PATH],
+ [cmirrord pidfile [[/var/run/cmirrord.pid]]]),
+ CMIRRORD_PIDFILE=$withval,
+ CMIRRORD_PIDFILE="/var/run/cmirrord.pid")
+ AC_DEFINE_UNQUOTED(CMIRRORD_PIDFILE, ["$CMIRRORD_PIDFILE"],
+ [Path to cmirrord pidfile.])
+fi
+
+################################################################################
+dnl -- Look for corosync libraries if required.
+if [[ "x$BUILD_CMIRRORD" = xyes ]]; then
+ dnl -- init pkgconfig if required
+ if test x$PKGCONFIG_INIT != x1; then
+ pkg_config_init
+ fi
+ PKG_CHECK_MODULES(SACKPT, libSaCkpt)
+ if test x$HAVE_CPG != xyes; then
+ PKG_CHECK_MODULES(CPG, libcpg)
+ fi
+fi
+
+################################################################################
+dnl -- Enable debugging
+AC_MSG_CHECKING(whether to enable debugging)
+AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [enable debugging]),
+ DEBUG=$enableval, DEBUG=no)
+AC_MSG_RESULT($DEBUG)
+
+dnl -- Normally turn off optimisation for debug builds
+if test x$DEBUG = xyes; then
+ COPTIMISE_FLAG=
+else
+ CSCOPE_CMD=
+fi
+
+################################################################################
+dnl -- Override optimisation
+AC_MSG_CHECKING(for C optimisation flag)
+AC_ARG_WITH(optimisation,
+ AC_HELP_STRING([--with-optimisation=OPT],
+ [C optimisation flag [[OPT=-O2]]]),
+ COPTIMISE_FLAG=$withval)
+AC_MSG_RESULT($COPTIMISE_FLAG)
+
+################################################################################
+dnl -- Enable profiling
+AC_MSG_CHECKING(whether to gather gcov profiling data)
+AC_ARG_ENABLE(profiling,
+ AC_HELP_STRING(--enable-profiling, [gather gcov profiling data]),
+ PROFILING=$enableval, PROFILING=no)
+AC_MSG_RESULT($PROFILING)
+
+if test "x$PROFILING" = xyes; then
+ COPTIMISE_FLAG="$COPTIMISE_FLAG -fprofile-arcs -ftest-coverage"
+ AC_PATH_PROG(LCOV, lcov)
+ AC_PATH_PROG(GENHTML, genhtml)
+ if test -z "$LCOV" -o -z "$GENHTML"; then
+ AC_MSG_ERROR([lcov and genhtml are required for profiling])
+ fi
+ AC_PATH_PROG(GENPNG, genpng)
+ if test -n "$GENPNG"; then
+ AC_MSG_CHECKING([whether $GENPNG has all required modules])
+ if $GENPNG --help > /dev/null 2>&1 ; then
+ AC_MSG_RESULT(ok)
+ GENHTML="$GENHTML --frames"
+ else
+ AC_MSG_RESULT(not supported)
+ AC_MSG_WARN([GD.pm perl module is not installed])
+ GENPNG=
+ fi
+ fi
+fi
+
+################################################################################
+dnl -- Enable testing
+AC_MSG_CHECKING(whether to enable unit testing)
+AC_ARG_ENABLE(testing,
+ AC_HELP_STRING(--enable-testing, [enable testing targets in the makefile]),
+ TESTING=$enableval, TESTING=no)
+AC_MSG_RESULT($TESTING)
+
+if test "$TESTING" = yes; then
+ AC_PATH_PROG(RUBY19, ruby1.9)
+ AC_PATH_PROG(VALGRIND, valgrind)
+ if test -z "$RUBY19" -o -z "$VALGRIND"; then
+ AC_MSG_ERROR([ruby1.9 and valgrind are required for testing])
+ fi
+fi
+
+################################################################################
+dnl -- Enable valgrind awareness of memory pools
+AC_MSG_CHECKING(whether to enable valgrind awareness of pools)
+AC_ARG_ENABLE(valgrind_pool,
+ AC_HELP_STRING(--enable-valgrind-pool, [enable valgrind awareness of pools]),
+ VALGRIND_POOL=$enableval, VALGRIND_POOL=no)
+AC_MSG_RESULT($VALGRIND_POOL)
+
+if test "$VALGRIND_POOL" = yes; then
+ AC_CHECK_HEADERS([valgrind/memcheck.h], , [AC_MSG_ERROR(bailing out)])
+ AC_DEFINE([VALGRIND_POOL], 1, [Enable a valgrind aware build of pool])
+fi
+
+################################################################################
+dnl -- Disable devmapper
+AC_MSG_CHECKING(whether to use device-mapper)
+AC_ARG_ENABLE(devmapper,
+ AC_HELP_STRING([--disable-devmapper],
+ [disable LVM2 device-mapper interaction]),
+ DEVMAPPER=$enableval)
+AC_MSG_RESULT($DEVMAPPER)
+
+if test x$DEVMAPPER = xyes; then
+ AC_DEFINE([DEVMAPPER_SUPPORT], 1, [Define to 1 to enable LVM2 device-mapper interaction.])
+fi
+
+################################################################################
+dnl -- Enable udev synchronisation
+AC_MSG_CHECKING(whether to enable synchronisation with udev processing)
+AC_ARG_ENABLE(udev_sync,
+ AC_HELP_STRING([--enable-udev_sync],
+ [enable synchronisation with udev processing]),
+ UDEV_SYNC=$enableval, UDEV_SYNC=no)
+AC_MSG_RESULT($UDEV_SYNC)
+
+if test x$UDEV_SYNC = xyes; then
+ AC_CHECK_LIB(udev, udev_queue_get_udev_is_active,
+ [UDEV_PC="libudev"; UDEV_LIBS="-ludev"],
+ [AC_MSG_ERROR([bailing out... libudev library is required])])
+ AC_DEFINE([UDEV_SYNC_SUPPORT], 1, [Define to 1 to enable synchronisation with udev processing.])
+fi
+
+dnl -- Enable udev rules
+AC_MSG_CHECKING(whether to enable installation of udev rules required for synchronisation)
+AC_ARG_ENABLE(udev_rules,
+ AC_HELP_STRING([--enable-udev_rules],
+ [install rule files needed for udev synchronisation]),
+ UDEV_RULES=$enableval, UDEV_RULES=$UDEV_SYNC)
+AC_MSG_RESULT($UDEV_RULES)
+
+################################################################################
+dnl -- Compatibility mode
+AC_ARG_ENABLE(compat,
+ AC_HELP_STRING([--enable-compat],
+ [enable support for old device-mapper versions]),
+ DM_COMPAT=$enableval, DM_COMPAT=no)
+
+################################################################################
+dnl -- Compatible units suffix mode
+AC_ARG_ENABLE(units-compat,
+ AC_HELP_STRING([--enable-units-compat],
+ [enable output compatibility with old versions that
+ that do not use KiB-style unit suffixes]),
+ UNITS_COMPAT=$enableval, UNITS_COMPAT=no)
+
+if test x$UNITS_COMPAT = xyes; then
+ AC_DEFINE([DEFAULT_SI_UNIT_CONSISTENCY], 0, [Define to 0 to reinstate the pre-2.02.54 handling of unit suffixes.])
+fi
+
+################################################################################
+dnl -- Disable ioctl
+AC_ARG_ENABLE(ioctl,
+ AC_HELP_STRING([--disable-driver],
+ [disable calls to device-mapper in the kernel]),
+ DM_IOCTLS=$enableval)
+
+################################################################################
+dnl -- Disable O_DIRECT
+AC_MSG_CHECKING(whether to enable O_DIRECT)
+AC_ARG_ENABLE(o_direct,
+ AC_HELP_STRING([--disable-o_direct], [disable O_DIRECT]),
+ ODIRECT=$enableval)
+AC_MSG_RESULT($ODIRECT)
+
+if test x$ODIRECT = xyes; then
+ AC_DEFINE([O_DIRECT_SUPPORT], 1, [Define to 1 to enable O_DIRECT support.])
+fi
+
+################################################################################
+dnl -- Enable liblvm2app.so
+AC_MSG_CHECKING(whether to build liblvm2app.so application library)
+AC_ARG_ENABLE(applib,
+ AC_HELP_STRING([--enable-applib], [build application library]),
+ APPLIB=$enableval, APPLIB=no)
+AC_MSG_RESULT($APPLIB)
+AC_SUBST([LVM2APP_LIB])
+test x$APPLIB = xyes \
+ && LVM2APP_LIB=-llvm2app \
+ || LVM2APP_LIB=
+
+################################################################################
+dnl -- Enable cmdlib
+AC_MSG_CHECKING(whether to compile liblvm2cmd.so)
+AC_ARG_ENABLE(cmdlib,
+ AC_HELP_STRING([--enable-cmdlib], [build shared command library]),
+ CMDLIB=$enableval, CMDLIB=no)
+AC_MSG_RESULT($CMDLIB)
+AC_SUBST([LVM2CMD_LIB])
+test x$CMDLIB = xyes \
+ && LVM2CMD_LIB=-llvm2cmd \
+ || LVM2CMD_LIB=
+
+################################################################################
+dnl -- Enable pkg-config
+AC_ARG_ENABLE(pkgconfig,
+ AC_HELP_STRING([--enable-pkgconfig], [install pkgconfig support]),
+ PKGCONFIG=$enableval, PKGCONFIG=no)
+
+################################################################################
+dnl -- Enable installation of writable files by user
+AC_ARG_ENABLE(write_install,
+ AC_HELP_STRING([--enable-write_install],
+ [install user writable files]),
+ WRITE_INSTALL=$enableval, WRITE_INSTALL=no)
+
+################################################################################
+dnl -- Enable fsadm
+AC_MSG_CHECKING(whether to install fsadm)
+AC_ARG_ENABLE(fsadm, AC_HELP_STRING([--disable-fsadm], [disable fsadm]),
+ FSADM=$enableval)
+AC_MSG_RESULT($FSADM)
+
+################################################################################
+dnl -- enable dmeventd handling
+AC_MSG_CHECKING(whether to use dmeventd)
+AC_ARG_ENABLE(dmeventd, AC_HELP_STRING([--enable-dmeventd],
+ [enable the device-mapper event daemon]),
+ DMEVENTD=$enableval)
+AC_MSG_RESULT($DMEVENTD)
+
+BUILD_DMEVENTD=$DMEVENTD
+
+dnl -- dmeventd currently requires internal mirror support
+if test x$DMEVENTD = xyes; then
+ if test x$MIRRORS != xinternal; then
+ AC_MSG_ERROR(
+ --enable-dmeventd currently requires --with-mirrors=internal
+ )
+ fi
+ if test x$CMDLIB = xno; then
+ AC_MSG_ERROR(
+ --enable-dmeventd requires --enable-cmdlib to be used as well
+ )
+ fi
+fi
+
+if test x$DMEVENTD = xyes; then
+ AC_DEFINE([DMEVENTD], 1, [Define to 1 to enable the device-mapper event daemon.])
+fi
+
+################################################################################
+dnl -- getline included in recent libc
+
+AC_CHECK_LIB(c, getline, AC_DEFINE([HAVE_GETLINE], 1,
+ [Define to 1 if getline is available.]))
+
+################################################################################
+dnl -- canonicalize_file_name included in recent libc
+
+AC_CHECK_LIB(c, canonicalize_file_name,
+ AC_DEFINE([HAVE_CANONICALIZE_FILE_NAME], 1,
+ [Define to 1 if canonicalize_file_name is available.]))
+
+################################################################################
+dnl -- Clear default exec_prefix - install into /sbin rather than /usr/sbin
+if [[ "x$exec_prefix" = xNONE -a "x$prefix" = xNONE ]];
+ then exec_prefix="";
+fi;
+
+################################################################################
+dnl -- Check for dlopen
+AC_CHECK_LIB(dl, dlopen, [
+ AC_DEFINE([HAVE_LIBDL], 1, [Define to 1 if dynamic libraries are available.])
+ DL_LIBS="-ldl"
+ HAVE_LIBDL=yes ], [
+ DL_LIBS=
+ HAVE_LIBDL=no ])
+
+################################################################################
+dnl -- Check for shared/static conflicts
+if [[ \( "x$LVM1" = xshared -o "x$POOL" = xshared -o "x$CLUSTER" = xshared \
+ -o "x$SNAPSHOTS" = xshared -o "x$MIRRORS" = xshared \
+ \) -a "x$STATIC_LINK" = xyes ]];
+ then AC_MSG_ERROR(
+Features cannot be 'shared' when building statically
+)
+fi
+
+################################################################################
+if [[ "$DMEVENTD" = yes -o "$CLVMD" != none ]] ; then
+ AC_CHECK_LIB([pthread], [pthread_mutex_lock],
+ [PTHREAD_LIBS="-lpthread"], hard_bailout)
+fi
+
+################################################################################
+dnl -- Disable selinux
+AC_MSG_CHECKING(whether to enable selinux support)
+AC_ARG_ENABLE(selinux,
+ AC_HELP_STRING([--disable-selinux], [disable selinux support]),
+ SELINUX=$enableval)
+AC_MSG_RESULT($SELINUX)
+
+################################################################################
+dnl -- Check for selinux
+if test x$SELINUX = xyes; then
+ AC_CHECK_LIB([sepol], [sepol_check_context], [
+ AC_DEFINE([HAVE_SEPOL], 1, [Define to 1 if sepol_check_context is available.])
+ SELINUX_LIBS="-lsepol"])
+
+ AC_CHECK_LIB([selinux], [is_selinux_enabled], [
+ AC_CHECK_HEADERS([selinux/selinux.h],, hard_bailout)
+ AC_CHECK_HEADERS([selinux/label.h])
+ AC_DEFINE([HAVE_SELINUX], 1, [Define to 1 to include support for selinux.])
+ SELINUX_LIBS="-lselinux $SELINUX_LIBS"
+ SELINUX_PC="libselinux"
+ HAVE_SELINUX=yes ], [
+ AC_MSG_WARN(Disabling selinux)
+ SELINUX_LIBS=
+ SELINUX_PC=
+ HAVE_SELINUX=no ])
+fi
+
+################################################################################
+dnl -- Check for realtime clock support
+if test x$REALTIME = xyes; then
+ AC_CHECK_LIB(rt, clock_gettime, HAVE_REALTIME=yes, HAVE_REALTIME=no)
+
+ if test x$HAVE_REALTIME = xyes; then
+ AC_DEFINE([HAVE_REALTIME], 1, [Define to 1 to include support for realtime clock.])
+ LIBS="-lrt $LIBS"
+ else
+ AC_MSG_WARN(Disabling realtime clock)
+ fi
+fi
+
+################################################################################
+dnl -- Check for getopt
+AC_CHECK_HEADERS(getopt.h, AC_DEFINE([HAVE_GETOPTLONG], 1, [Define to 1 if getopt_long is available.]))
+
+################################################################################
+dnl -- Check for readline (Shamelessly copied from parted 1.4.17)
+if test x$READLINE != xno; then
+ lvm_saved_libs=$LIBS
+ AC_SEARCH_LIBS([tgetent], [tinfo ncurses curses termcap termlib],
+ READLINE_LIBS=$ac_cv_search_tgetent, [
+ if test "$READLINE" = yes; then
+ AC_MSG_ERROR(
+[termcap could not be found which is required for the
+--enable-readline option (which is enabled by default). Either disable readline
+support with --disable-readline or download and install termcap from:
+ ftp.gnu.org/gnu/termcap
+Note: if you are using precompiled packages you will also need the development
+ package as well (which may be called termcap-devel or something similar).
+Note: (n)curses also seems to work as a substitute for termcap. This was
+ not found either - but you could try installing that as well.])
+ fi])
+ dnl -- Old systems may need extra termcap dependency explicitly in LIBS
+ AC_CHECK_LIB([readline], [readline], [
+ AC_DEFINE([READLINE_SUPPORT], 1,
+ [Define to 1 to include the LVM readline shell.])
+ dnl -- Try only with -lreadline and check for different symbol
+ LIBS=$lvm_saved_libs
+ AC_CHECK_LIB([readline], [rl_line_buffer],
+ [ READLINE_LIBS="-lreadline" ], [
+ AC_MSG_RESULT([linking -lreadline with $READLINE_LIBS needed])
+ READLINE_LIBS="-lreadline $READLINE_LIBS"
+ ]) ], [
+ READLINE_LIBS=
+ if test "$READLINE" = yes; then
+ AC_MSG_ERROR(
+[GNU Readline could not be found which is required for the
+--enable-readline option (which is enabled by default). Either disable readline
+support with --disable-readline or download and install readline from:
+ ftp.gnu.org/gnu/readline
+Note: if you are using precompiled packages you will also need the development
+package as well (which may be called readline-devel or something similar).])
+ fi ])
+ LIBS="$READLINE_LIBS $lvm_saved_libs"
+ AC_CHECK_FUNCS([rl_completion_matches])
+ LIBS=$lvm_saved_libs
+fi
+
+################################################################################
+dnl -- Internationalisation stuff
+AC_MSG_CHECKING(whether to enable internationalisation)
+AC_ARG_ENABLE(nls,
+ AC_HELP_STRING([--enable-nls], [enable Native Language Support]),
+ INTL=$enableval, INTL=no)
+AC_MSG_RESULT($INTL)
+
+if test x$INTL = xyes; then
+# FIXME - Move this - can be device-mapper too
+ INTL_PACKAGE="lvm2"
+ AC_PATH_PROG(MSGFMT, msgfmt)
+ if [[ "x$MSGFMT" == x ]];
+ then AC_MSG_ERROR(
+ msgfmt not found in path $PATH
+ )
+ fi;
+
+ AC_ARG_WITH(localedir,
+ AC_HELP_STRING([--with-localedir=DIR],
+ [translation files in DIR
+ [[PREFIX/share/locale]]]),
+ LOCALEDIR=$withval, LOCALEDIR='${prefix}/share/locale')
+fi
+
+################################################################################
+AC_ARG_WITH(confdir,
+ AC_HELP_STRING([--with-confdir=DIR],
+ [configuration files in DIR [[/etc]]]),
+ CONFDIR=$withval, CONFDIR="/etc")
+
+AC_ARG_WITH(staticdir,
+ AC_HELP_STRING([--with-staticdir=DIR],
+ [static binaries in DIR [[EPREFIX/sbin]]]),
+ STATICDIR=$withval, STATICDIR='${exec_prefix}/sbin')
+
+AC_ARG_WITH(usrlibdir,
+ AC_HELP_STRING([--with-usrlibdir=DIR],
+ [usrlib in DIR [[PREFIX/lib]]]),
+ usrlibdir=$withval, usrlibdir='${prefix}/lib')
+
+AC_ARG_WITH(usrsbindir,
+ AC_HELP_STRING([--with-usrsbindir=DIR],
+ [usrsbin executables in DIR [[PREFIX/sbin]]]),
+ usrsbindir=$withval, usrsbindir='${prefix}/sbin')
+
+################################################################################
+AC_ARG_WITH(udev_prefix,
+ AC_HELP_STRING([--with-udev-prefix=UPREFIX],
+ [install udev rule files in UPREFIX [[EPREFIX]]]),
+ udev_prefix=$withval, udev_prefix='${exec_prefix}')
+
+AC_ARG_WITH(udevdir,
+ AC_HELP_STRING([--with-udevdir=DIR],
+ [udev rules in DIR [[UPREFIX/lib/udev/rules.d]]]),
+ udevdir=$withval, udevdir='${udev_prefix}/lib/udev/rules.d')
+
+################################################################################
+dnl -- Ensure additional headers required
+if test x$READLINE = xyes; then
+ AC_CHECK_HEADERS(readline/readline.h readline/history.h,,AC_MSG_ERROR(bailing out))
+fi
+
+if test x$CLVMD != xnone; then
+ AC_CHECK_HEADERS(mntent.h netdb.h netinet/in.h pthread.h search.h sys/mount.h sys/socket.h sys/uio.h sys/un.h utmpx.h,,AC_MSG_ERROR(bailing out))
+ AC_CHECK_FUNCS(dup2 getmntent memmove select socket,,AC_MSG_ERROR(bailing out))
+ AC_FUNC_GETMNTENT
+ AC_FUNC_SELECT_ARGTYPES
+fi
+
+if test x$CLUSTER != xnone; then
+ AC_CHECK_HEADERS(sys/socket.h sys/un.h,,AC_MSG_ERROR(bailing out))
+ AC_CHECK_FUNCS(socket,,AC_MSG_ERROR(bailing out))
+fi
+
+if test x$DMEVENTD = xyes; then
+ AC_CHECK_HEADERS(arpa/inet.h,,AC_MSG_ERROR(bailing out))
+fi
+
+if test x$HAVE_LIBDL = xyes; then
+ AC_CHECK_HEADERS(dlfcn.h,,AC_MSG_ERROR(bailing out))
+fi
+
+if test x$INTL = xyes; then
+ AC_CHECK_HEADERS(libintl.h,,AC_MSG_ERROR(bailing out))
+fi
+
+if test x$UDEV_SYNC = xyes; then
+ AC_CHECK_HEADERS(sys/ipc.h sys/sem.h,,AC_MSG_ERROR(bailing out))
+fi
+
+################################################################################
+AC_PATH_PROG(MODPROBE_CMD, modprobe)
+
+if test x$MODPROBE_CMD != x; then
+ AC_DEFINE_UNQUOTED([MODPROBE_CMD], ["$MODPROBE_CMD"], [The path to 'modprobe', if available.])
+fi
+
+
+lvm_exec_prefix=$exec_prefix
+test "$lvm_exec_prefix" = NONE && lvm_exec_prefix=$prefix
+test "$lvm_exec_prefix" = NONE && lvm_exec_prefix=$ac_default_prefix
+AC_DEFINE_UNQUOTED(LVM_PATH, ["$lvm_exec_prefix/sbin/lvm"], [Path to lvm binary.])
+
+if test "$CLVMD" != none; then
+ clvmd_prefix=$ac_default_prefix
+ test "$prefix" != NONE && clvmd_prefix=$prefix
+ AC_DEFINE_UNQUOTED(CLVMD_PATH, ["$clvmd_prefix/sbin/clvmd"],
+ [Path to clvmd binary.])
+fi
+
+################################################################################
+dnl -- dmeventd pidfile and executable path
+if test "$BUILD_DMEVENTD" = yes; then
+ AC_ARG_WITH(dmeventd-pidfile,
+ AC_HELP_STRING([--with-dmeventd-pidfile=PATH],
+ [dmeventd pidfile [[/var/run/dmeventd.pid]]]),
+ DMEVENTD_PIDFILE=$withval,
+ DMEVENTD_PIDFILE="/var/run/dmeventd.pid")
+ AC_DEFINE_UNQUOTED(DMEVENTD_PIDFILE, ["$DMEVENTD_PIDFILE"],
+ [Path to dmeventd pidfile.])
+fi
+
+if test "$BUILD_DMEVENTD" = yes; then
+ AC_ARG_WITH(dmeventd-path,
+ AC_HELP_STRING([--with-dmeventd-path=PATH],
+ [dmeventd path [[EPREFIX/sbin/dmeventd]]]),
+ DMEVENTD_PATH=$withval,
+ DMEVENTD_PATH="$lvm_exec_prefix/sbin/dmeventd")
+ AC_DEFINE_UNQUOTED(DMEVENTD_PATH, ["$DMEVENTD_PATH"],
+ [Path to dmeventd binary.])
+fi
+
+AH_TEMPLATE(DEFAULT_RUN_DIR, [Name of default run directory.])
+AC_ARG_WITH(default-run-dir,
+ [ --with-default-run-dir=DIR Default run directory [[/var/run/lvm]] ],
+ [ DEFAULT_RUN_DIR="$withval" ],
+ [ DEFAULT_RUN_DIR="/var/run/lvm" ])
+AC_DEFINE_UNQUOTED(DEFAULT_RUN_DIR,["$DEFAULT_RUN_DIR"] )
+
+################################################################################
+dnl -- various defaults
+AC_ARG_WITH(default-system-dir,
+ AC_HELP_STRING([--with-default-system-dir=DIR],
+ [default LVM system directory [[/etc/lvm]]]),
+ DEFAULT_SYS_DIR=$withval, DEFAULT_SYS_DIR="/etc/lvm")
+AC_DEFINE_UNQUOTED(DEFAULT_SYS_DIR, ["$DEFAULT_SYS_DIR"],
+ [Path to LVM system directory.])
+
+AC_ARG_WITH(default-archive-subdir,
+ AC_HELP_STRING([--with-default-archive-subdir=SUBDIR],
+ [default metadata archive subdir [[archive]]]),
+ DEFAULT_ARCHIVE_SUBDIR=$withval, DEFAULT_ARCHIVE_SUBDIR=archive)
+AC_DEFINE_UNQUOTED(DEFAULT_ARCHIVE_SUBDIR, ["$DEFAULT_ARCHIVE_SUBDIR"],
+ [Name of default metadata archive subdirectory.])
+
+AC_ARG_WITH(default-backup-subdir,
+ AC_HELP_STRING([--with-default-backup-subdir=SUBDIR],
+ [default metadata backup subdir [[backup]]]),
+ DEFAULT_BACKUP_SUBDIR=$withval, DEFAULT_BACKUP_SUBDIR=backup)
+AC_DEFINE_UNQUOTED(DEFAULT_BACKUP_SUBDIR, ["$DEFAULT_BACKUP_SUBDIR"],
+ [Name of default metadata backup subdirectory.])
+
+AC_ARG_WITH(default-cache-subdir,
+ AC_HELP_STRING([--with-default-cache-subdir=SUBDIR],
+ [default metadata cache subdir [[cache]]]),
+ DEFAULT_CACHE_SUBDIR=$withval, DEFAULT_CACHE_SUBDIR=cache)
+AC_DEFINE_UNQUOTED(DEFAULT_CACHE_SUBDIR, ["$DEFAULT_CACHE_SUBDIR"],
+ [Name of default metadata cache subdirectory.])
+
+AC_ARG_WITH(default-locking-dir,
+ AC_HELP_STRING([--with-default-locking-dir=DIR],
+ [default locking directory [[/var/lock/lvm]]]),
+ DEFAULT_LOCK_DIR=$withval, DEFAULT_LOCK_DIR="/var/lock/lvm")
+AC_DEFINE_UNQUOTED(DEFAULT_LOCK_DIR, ["$DEFAULT_LOCK_DIR"],
+ [Name of default locking directory.])
+
+################################################################################
+dnl -- Setup default data alignment
+AC_ARG_WITH(default-data-alignment,
+ AC_HELP_STRING([--with-default-data-alignment=NUM],
+ [set the default data alignment in MiB [[1]]]),
+ DEFAULT_DATA_ALIGNMENT=$withval, DEFAULT_DATA_ALIGNMENT=1)
+AC_DEFINE_UNQUOTED(DEFAULT_DATA_ALIGNMENT, [$DEFAULT_DATA_ALIGNMENT],
+ [Default data alignment.])
+
+################################################################################
+dnl -- which kernel interface to use (ioctl only)
+AC_MSG_CHECKING(for kernel interface choice)
+AC_ARG_WITH(interface,
+ AC_HELP_STRING([--with-interface=IFACE],
+ [choose kernel interface (ioctl) [[ioctl]]]),
+ interface=$withval, interface=ioctl)
+if [[ "x$interface" != xioctl ]];
+then
+ AC_MSG_ERROR(--with-interface=ioctl required. fs no longer supported.)
+fi
+AC_MSG_RESULT($interface)
+
+################################################################################
+DM_LIB_VERSION="\"`cat "$srcdir"/VERSION_DM 2>/dev/null || echo Unknown`\""
+AC_DEFINE_UNQUOTED(DM_LIB_VERSION, $DM_LIB_VERSION, [Library version])
+
+DM_LIB_PATCHLEVEL=`cat "$srcdir"/VERSION_DM | $AWK -F '[[-. ]]' '{printf "%s.%s.%s",$1,$2,$3}'`
+
+LVM_VERSION="\"`cat "$srcdir"/VERSION 2>/dev/null || echo Unknown`\""
+
+VER=`cat "$srcdir"/VERSION`
+LVM_RELEASE_DATE="\"`echo $VER | $SED 's/.* (//;s/).*//'`\""
+VER=`echo "$VER" | $AWK '{print $1}'`
+LVM_RELEASE="\"`echo "$VER" | $AWK -F '-' '{print $2}'`\""
+VER=`echo "$VER" | $AWK -F '-' '{print $1}'`
+LVM_MAJOR=`echo "$VER" | $AWK -F '.' '{print $1}'`
+LVM_MINOR=`echo "$VER" | $AWK -F '.' '{print $2}'`
+LVM_PATCHLEVEL=`echo "$VER" | $AWK -F '[[(.]]' '{print $3}'`
+LVM_LIBAPI=`echo "$VER" | $AWK -F '[[()]]' '{print $2}'`
+
+################################################################################
+AC_SUBST(APPLIB)
+AC_SUBST(AWK)
+AC_SUBST(BUILD_CMIRRORD)
+AC_SUBST(BUILD_DMEVENTD)
+AC_SUBST(CCS_CFLAGS)
+AC_SUBST(CCS_LIBS)
+AC_SUBST(CFLAGS)
+AC_SUBST(CFLOW_CMD)
+AC_SUBST(CLDFLAGS)
+AC_SUBST(CLDNOWHOLEARCHIVE)
+AC_SUBST(CLDWHOLEARCHIVE)
+AC_SUBST(CLUSTER)
+AC_SUBST(CLVMD)
+AC_SUBST(CLVMD_CMANAGERS)
+AC_SUBST(CMAN_CFLAGS)
+AC_SUBST(CMAN_LIBS)
+AC_SUBST(CMDLIB)
+AC_SUBST(CONFDB_CFLAGS)
+AC_SUBST(CONFDB_LIBS)
+AC_SUBST(CONFDIR)
+AC_SUBST(COPTIMISE_FLAG)
+AC_SUBST(CPG_CFLAGS)
+AC_SUBST(CPG_LIBS)
+AC_SUBST(CSCOPE_CMD)
+AC_SUBST(DEBUG)
+AC_SUBST(DEFAULT_SYS_DIR)
+AC_SUBST(DEFAULT_ARCHIVE_SUBDIR)
+AC_SUBST(DEFAULT_BACKUP_SUBDIR)
+AC_SUBST(DEFAULT_CACHE_SUBDIR)
+AC_SUBST(DEFAULT_DATA_ALIGNMENT)
+AC_SUBST(DEFAULT_LOCK_DIR)
+AC_SUBST(DEFAULT_RUN_DIR)
+AC_SUBST(DEVMAPPER)
+AC_SUBST(DLM_CFLAGS)
+AC_SUBST(DLM_LIBS)
+AC_SUBST(DL_LIBS)
+AC_SUBST(DMEVENTD)
+AC_SUBST(DMEVENTD_PATH)
+AC_SUBST(DM_COMPAT)
+AC_SUBST(DM_DEVICE_GID)
+AC_SUBST(DM_DEVICE_MODE)
+AC_SUBST(DM_DEVICE_UID)
+AC_SUBST(DM_IOCTLS)
+AC_SUBST(DM_LIB_VERSION)
+AC_SUBST(DM_LIB_PATCHLEVEL)
+AC_SUBST(FSADM)
+AC_SUBST(GULM_CFLAGS)
+AC_SUBST(GULM_LIBS)
+AC_SUBST(HAVE_LIBDL)
+AC_SUBST(HAVE_REALTIME)
+AC_SUBST(INTL)
+AC_SUBST(INTL_PACKAGE)
+AC_SUBST(JOBS)
+AC_SUBST(LDDEPS)
+AC_SUBST(LIBS)
+AC_SUBST(LIB_SUFFIX)
+AC_SUBST(LOCALEDIR)
+AC_SUBST(LVM1)
+AC_SUBST(LVM1_FALLBACK)
+AC_SUBST(LVM_VERSION)
+AC_SUBST(LVM_LIBAPI)
+AC_SUBST(LVM_MAJOR)
+AC_SUBST(LVM_MINOR)
+AC_SUBST(LVM_PATCHLEVEL)
+AC_SUBST(LVM_RELEASE)
+AC_SUBST(LVM_RELEASE_DATE)
+AC_SUBST(MIRRORS)
+AC_SUBST(OCF)
+AC_SUBST(REPLICATORS)
+AC_SUBST(MSGFMT)
+AC_SUBST(PKGCONFIG)
+AC_SUBST(POOL)
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(QUORUM_CFLAGS)
+AC_SUBST(QUORUM_LIBS)
+AC_SUBST(READLINE_LIBS)
+AC_SUBST(SACKPT_CFLAGS)
+AC_SUBST(SACKPT_LIBS)
+AC_SUBST(SALCK_CFLAGS)
+AC_SUBST(SALCK_LIBS)
+AC_SUBST(SELINUX_LIBS)
+AC_SUBST(SELINUX_PC)
+AC_SUBST(SNAPSHOTS)
+AC_SUBST(STATICDIR)
+AC_SUBST(STATIC_LINK)
+AC_SUBST(TESTING)
+AC_SUBST(UDEV_LIBS)
+AC_SUBST(UDEV_PC)
+AC_SUBST(UDEV_RULES)
+AC_SUBST(UDEV_SYNC)
+AC_SUBST(WRITE_INSTALL)
+AC_SUBST(interface)
+AC_SUBST(kerneldir)
+AC_SUBST(missingkernel)
+AC_SUBST(kernelvsn)
+AC_SUBST(tmpdir)
+AC_SUBST(udev_prefix)
+AC_SUBST(udevdir)
+AC_SUBST(usrlibdir)
+AC_SUBST(usrsbindir)
+
+################################################################################
+dnl -- First and last lines should not contain files to generate in order to
+dnl -- keep utility scripts running properly
+AC_CONFIG_FILES([
+Makefile
+make.tmpl
+daemons/Makefile
+daemons/clvmd/Makefile
+daemons/cmirrord/Makefile
+daemons/dmeventd/Makefile
+daemons/dmeventd/libdevmapper-event.pc
+daemons/dmeventd/plugins/Makefile
+daemons/dmeventd/plugins/lvm2/Makefile
+daemons/dmeventd/plugins/mirror/Makefile
+daemons/dmeventd/plugins/snapshot/Makefile
+doc/Makefile
+doc/example.conf
+include/.symlinks
+include/Makefile
+lib/Makefile
+lib/format1/Makefile
+lib/format_pool/Makefile
+lib/locking/Makefile
+lib/mirror/Makefile
+lib/replicator/Makefile
+lib/misc/lvm-version.h
+lib/snapshot/Makefile
+libdm/Makefile
+libdm/libdevmapper.pc
+liblvm/Makefile
+liblvm/liblvm2app.pc
+man/Makefile
+po/Makefile
+scripts/clvmd_init_red_hat
+scripts/cmirrord_init_red_hat
+scripts/lvm2_monitoring_init_red_hat
+scripts/Makefile
+test/Makefile
+test/api/Makefile
+tools/Makefile
+udev/Makefile
+unit-tests/datastruct/Makefile
+unit-tests/regex/Makefile
+unit-tests/mm/Makefile
+])
+AC_OUTPUT
+
+if test x$ODIRECT != xyes; then
+ AC_MSG_WARN(Warning: O_DIRECT disabled: low-memory pvmove may lock up)
+fi
--- /dev/null
+#
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+.PHONY: dmeventd clvmd cmirrord
+
+ifneq ("@CLVMD@", "none")
+ SUBDIRS = clvmd
+endif
+
+ifeq ("@BUILD_CMIRRORD@", "yes")
+ SUBDIRS += cmirrord
+endif
+
+ifeq ("@BUILD_DMEVENTD@", "yes")
+ SUBDIRS += dmeventd
+ifneq ("$(CFLOW_CMD)", "")
+daemons.cflow: dmeventd.cflow
+endif
+endif
+
+ifeq ($(MAKECMDGOALS),distclean)
+ SUBDIRS = clvmd cmirrord dmeventd
+endif
+
+include $(top_builddir)/make.tmpl
+
+ifeq ("@BUILD_DMEVENTD@", "yes")
+device-mapper: dmeventd.device-mapper
+endif
--- /dev/null
+#
+# Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+CCS_LIBS = @CCS_LIBS@
+CCS_CFLAGS = @CCS_CFLAGS@
+CMAN_LIBS = @CMAN_LIBS@
+CMAN_CFLAGS = @CMAN_CFLAGS@
+CONFDB_LIBS = @CONFDB_LIBS@
+CONFDB_CFLAGS = @CONFDB_CFLAGS@
+CPG_LIBS = @CPG_LIBS@
+CPG_CFLAGS = @CPG_CFLAGS@
+DLM_LIBS = @DLM_LIBS@
+DLM_CFLAGS = @DLM_CFLAGS@
+GULM_LIBS = @GULM_LIBS@
+GULM_CFLAGS = @GULM_CFLAGS@
+QUORUM_LIBS = @QUORUM_LIBS@
+QUORUM_CFLAGS = @QUORUM_CFLAGS@
+SALCK_LIBS = @SALCK_LIBS@
+SALCK_CFLAGS = @SALCK_CFLAGS@
+
+SOURCES = \
+ clvmd-command.c \
+ clvmd.c \
+ lvm-functions.c \
+ refresh_clvmd.c
+
+ifeq ("@DEBUG@", "yes")
+ DEFS += -DDEBUG
+endif
+
+ifneq (,$(findstring gulm,, "@CLVMD@,"))
+ SOURCES += clvmd-gulm.c tcp-comms.c
+ LMLIBS += $(CCS_LIBS) $(GULM_LIBS)
+ CFLAGS += $(CCS_CFLAGS) $(GULM_CFLAGS)
+ DEFS += -DUSE_GULM
+endif
+
+ifneq (,$(findstring cman,, "@CLVMD@,"))
+ SOURCES += clvmd-cman.c
+ LMLIBS += $(CMAN_LIBS) $(CONFDB_LIBS) $(DLM_LIBS)
+ CFLAGS += $(CMAN_CFLAGS) $(CONFDB_CFLAGS) $(DLM_CFLAGS)
+ DEFS += -DUSE_CMAN
+endif
+
+ifneq (,$(findstring openais,, "@CLVMD@,"))
+ SOURCES += clvmd-openais.c
+ LMLIBS += $(CONFDB_LIBS) $(CPG_LIBS) $(SALCK_LIBS)
+ CFLAGS += $(CONFDB_CFLAGS) $(CPG_CFLAGS) $(SALCK_CFLAGS)
+ DEFS += -DUSE_OPENAIS
+endif
+
+ifneq (,$(findstring corosync,, "@CLVMD@,"))
+ SOURCES += clvmd-corosync.c
+ LMLIBS += $(CONFDB_LIBS) $(CPG_LIBS) $(DLM_LIBS) $(QUORUM_LIBS)
+ CFLAGS += $(CONFDB_CFLAGS) $(CPG_CFLAGS) $(DLM_CFLAGS) $(QUORUM_CFLAGS)
+ DEFS += -DUSE_COROSYNC
+endif
+
+ifneq (,$(findstring singlenode,, "@CLVMD@,"))
+ SOURCES += clvmd-singlenode.c
+ DEFS += -DUSE_SINGLENODE
+endif
+
+ifeq ($(MAKECMDGOALS),distclean)
+ SOURCES += clvmd-gulm.c tcp-comms.c
+ SOURCES += clvmd-cman.c
+ SOURCES += clvmd-openais.c
+ SOURCES += clvmd-corosync.c
+ SOURCES += clvmd-singlenode.c
+endif
+
+TARGETS = \
+ clvmd
+
+LVMLIBS = $(LVMINTERNAL_LIBS)
+
+ifeq ("@DMEVENTD@", "yes")
+ LVMLIBS += -ldevmapper-event
+endif
+
+include $(top_builddir)/make.tmpl
+
+LVMLIBS += -ldevmapper
+LIBS += $(PTHREAD_LIBS)
+
+DEFS += -D_REENTRANT
+CFLAGS += -fno-strict-aliasing
+
+INSTALL_TARGETS = \
+ install_clvmd
+
+clvmd: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a
+ $(CC) $(CFLAGS) $(LDFLAGS) -o clvmd $(OBJECTS) \
+ $(LVMLIBS) $(LMLIBS) $(LIBS)
+
+.PHONY: install_clvmd
+
+install_clvmd: $(TARGETS)
+ $(INSTALL_PROGRAM) -D clvmd $(usrsbindir)/clvmd
+
+install: $(INSTALL_TARGETS)
+
+install_cluster: $(INSTALL_TARGETS)
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Definitions for CLVMD server and clients */
+
+/*
+ * The protocol spoken over the cluster and across the local socket.
+ */
+
+#ifndef _CLVM_H
+#define _CLVM_H
+
+#include "configure.h"
+
+struct clvm_header {
+ uint8_t cmd; /* See below */
+ uint8_t flags; /* See below */
+ uint16_t xid; /* Transaction ID */
+ uint32_t clientid; /* Only used in Daemon->Daemon comms */
+ int32_t status; /* For replies, whether request succeeded */
+ uint32_t arglen; /* Length of argument below.
+ If >1500 then it will be passed
+ around the cluster in the system LV */
+ char node[1]; /* Actually a NUL-terminated string, node name.
+ If this is empty then the command is
+ forwarded to all cluster nodes unless
+ FLAG_LOCAL is also set. */
+ char args[1]; /* Arguments for the command follow the
+ node name, This member is only
+ valid if the node name is empty */
+} __attribute__ ((packed));
+
+/* Flags */
+#define CLVMD_FLAG_LOCAL 1 /* Only do this on the local node */
+#define CLVMD_FLAG_SYSTEMLV 2 /* Data in system LV under my node name */
+#define CLVMD_FLAG_NODEERRS 4 /* Reply has errors in node-specific portion */
+
+/* Name of the local socket to communicate between lvm and clvmd */
+static const char CLVMD_SOCKNAME[]= DEFAULT_RUN_DIR "/clvmd.sock";
+
+/* Internal commands & replies */
+#define CLVMD_CMD_REPLY 1
+#define CLVMD_CMD_VERSION 2 /* Send version around cluster when we start */
+#define CLVMD_CMD_GOAWAY 3 /* Die if received this - we are running
+ an incompatible version */
+#define CLVMD_CMD_TEST 4 /* Just for mucking about */
+
+#define CLVMD_CMD_LOCK 30
+#define CLVMD_CMD_UNLOCK 31
+
+/* Lock/Unlock commands */
+#define CLVMD_CMD_LOCK_LV 50
+#define CLVMD_CMD_LOCK_VG 51
+#define CLVMD_CMD_LOCK_QUERY 52
+
+/* Misc functions */
+#define CLVMD_CMD_REFRESH 40
+#define CLVMD_CMD_GET_CLUSTERNAME 41
+#define CLVMD_CMD_SET_DEBUG 42
+#define CLVMD_CMD_VG_BACKUP 43
+#define CLVMD_CMD_RESTART 44
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * CMAN communication layer for clvmd.
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+
+#include "clvmd-comms.h"
+#include "clvm.h"
+#include "clvmd.h"
+#include "lvm-functions.h"
+
+#include <libdlm.h>
+
+#include <syslog.h>
+
+#define LOCKSPACE_NAME "clvmd"
+
+struct clvmd_node
+{
+ struct cman_node *node;
+ int clvmd_up;
+};
+
+static int num_nodes;
+static struct cman_node *nodes = NULL;
+static struct cman_node this_node;
+static int count_nodes; /* size of allocated nodes array */
+static struct dm_hash_table *node_updown_hash;
+static dlm_lshandle_t *lockspace;
+static cman_handle_t c_handle;
+
+static void count_clvmds_running(void);
+static void get_members(void);
+static int nodeid_from_csid(const char *csid);
+static int name_from_nodeid(int nodeid, char *name);
+static void event_callback(cman_handle_t handle, void *private, int reason, int arg);
+static void data_callback(cman_handle_t handle, void *private,
+ char *buf, int len, uint8_t port, int nodeid);
+
+struct lock_wait {
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ struct dlm_lksb lksb;
+};
+
+static int _init_cluster(void)
+{
+ node_updown_hash = dm_hash_create(100);
+
+ /* Open the cluster communication socket */
+ c_handle = cman_init(NULL);
+ if (!c_handle) {
+ syslog(LOG_ERR, "Can't open cluster manager socket: %m");
+ return -1;
+ }
+ DEBUGLOG("Connected to CMAN\n");
+
+ if (cman_start_recv_data(c_handle, data_callback, CLUSTER_PORT_CLVMD)) {
+ syslog(LOG_ERR, "Can't bind cluster socket: %m");
+ return -1;
+ }
+
+ if (cman_start_notification(c_handle, event_callback)) {
+ syslog(LOG_ERR, "Can't start cluster event listening");
+ return -1;
+ }
+
+ /* Get the cluster members list */
+ get_members();
+ count_clvmds_running();
+
+ DEBUGLOG("CMAN initialisation complete\n");
+
+ /* Create a lockspace for LV & VG locks to live in */
+ lockspace = dlm_create_lockspace(LOCKSPACE_NAME, 0600);
+ if (!lockspace) {
+ if (errno == EEXIST) {
+ lockspace = dlm_open_lockspace(LOCKSPACE_NAME);
+ }
+ if (!lockspace) {
+ syslog(LOG_ERR, "Unable to create lockspace for CLVM: %m");
+ return -1;
+ }
+ }
+ dlm_ls_pthread_init(lockspace);
+ DEBUGLOG("DLM initialisation complete\n");
+ return 0;
+}
+
+static void _cluster_init_completed(void)
+{
+ clvmd_cluster_init_completed();
+}
+
+static int _get_main_cluster_fd()
+{
+ return cman_get_fd(c_handle);
+}
+
+static int _get_num_nodes()
+{
+ int i;
+ int nnodes = 0;
+
+ /* return number of ACTIVE nodes */
+ for (i=0; i<num_nodes; i++) {
+ if (nodes[i].cn_member && nodes[i].cn_nodeid)
+ nnodes++;
+ }
+ return nnodes;
+}
+
+/* send_message with the fd check removed */
+static int _cluster_send_message(const void *buf, int msglen, const char *csid,
+ const char *errtext)
+{
+ int nodeid = 0;
+
+ if (csid)
+ memcpy(&nodeid, csid, CMAN_MAX_CSID_LEN);
+
+ if (cman_send_data(c_handle, buf, msglen, 0, CLUSTER_PORT_CLVMD, nodeid) <= 0)
+ {
+ log_error("%s", errtext);
+ }
+ return msglen;
+}
+
+static void _get_our_csid(char *csid)
+{
+ if (this_node.cn_nodeid == 0) {
+ cman_get_node(c_handle, 0, &this_node);
+ }
+ memcpy(csid, &this_node.cn_nodeid, CMAN_MAX_CSID_LEN);
+}
+
+/* Call a callback routine for each node is that known (down means not running a clvmd) */
+static int _cluster_do_node_callback(struct local_client *client,
+ void (*callback) (struct local_client *,
+ const char *,
+ int))
+{
+ int i;
+ int somedown = 0;
+
+ for (i = 0; i < _get_num_nodes(); i++) {
+ if (nodes[i].cn_member && nodes[i].cn_nodeid) {
+ int up = (int)(long)dm_hash_lookup_binary(node_updown_hash, (char *)&nodes[i].cn_nodeid, sizeof(int));
+
+ callback(client, (char *)&nodes[i].cn_nodeid, up);
+ if (!up)
+ somedown = -1;
+ }
+ }
+ return somedown;
+}
+
+/* Process OOB messages from the cluster socket */
+static void event_callback(cman_handle_t handle, void *private, int reason, int arg)
+{
+ char namebuf[MAX_CLUSTER_MEMBER_NAME_LEN];
+
+ switch (reason) {
+ case CMAN_REASON_PORTCLOSED:
+ name_from_nodeid(arg, namebuf);
+ log_notice("clvmd on node %s has died\n", namebuf);
+ DEBUGLOG("Got port closed message, removing node %s\n", namebuf);
+
+ dm_hash_insert_binary(node_updown_hash, (char *)&arg, sizeof(int), (void *)0);
+ break;
+
+ case CMAN_REASON_STATECHANGE:
+ DEBUGLOG("Got state change message, re-reading members list\n");
+ get_members();
+ break;
+
+#if defined(LIBCMAN_VERSION) && LIBCMAN_VERSION >= 2
+ case CMAN_REASON_PORTOPENED:
+ /* Ignore this, wait for startup message from clvmd itself */
+ break;
+
+ case CMAN_REASON_TRY_SHUTDOWN:
+ DEBUGLOG("Got try shutdown, sending OK\n");
+ cman_replyto_shutdown(c_handle, 1);
+ break;
+#endif
+ default:
+ /* ERROR */
+ DEBUGLOG("Got unknown event callback message: %d\n", reason);
+ break;
+ }
+}
+
+static struct local_client *cman_client;
+static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
+ const char *csid,
+ struct local_client **new_client)
+{
+
+ /* Save this for data_callback */
+ cman_client = fd;
+
+ /* We never return a new client */
+ *new_client = NULL;
+
+ return cman_dispatch(c_handle, 0);
+}
+
+
+static void data_callback(cman_handle_t handle, void *private,
+ char *buf, int len, uint8_t port, int nodeid)
+{
+ /* Ignore looped back messages */
+ if (nodeid == this_node.cn_nodeid)
+ return;
+ process_message(cman_client, buf, len, (char *)&nodeid);
+}
+
+static void _add_up_node(const char *csid)
+{
+ /* It's up ! */
+ int nodeid = nodeid_from_csid(csid);
+
+ dm_hash_insert_binary(node_updown_hash, (char *)&nodeid, sizeof(int), (void *)1);
+ DEBUGLOG("Added new node %d to updown list\n", nodeid);
+}
+
+static void _cluster_closedown()
+{
+ destroy_lvhash();
+ dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
+ cman_finish(c_handle);
+}
+
+static int is_listening(int nodeid)
+{
+ int status;
+
+ do {
+ status = cman_is_listening(c_handle, nodeid, CLUSTER_PORT_CLVMD);
+ if (status < 0 && errno == EBUSY) { /* Don't busywait */
+ sleep(1);
+ errno = EBUSY; /* In case sleep trashes it */
+ }
+ }
+ while (status < 0 && errno == EBUSY);
+
+ return status;
+}
+
+/* Populate the list of CLVMDs running.
+ called only at startup time */
+static void count_clvmds_running(void)
+{
+ int i;
+
+ for (i = 0; i < num_nodes; i++) {
+ int nodeid = nodes[i].cn_nodeid;
+
+ if (is_listening(nodeid) == 1)
+ dm_hash_insert_binary(node_updown_hash, (void *)&nodeid, sizeof(int), (void*)1);
+ else
+ dm_hash_insert_binary(node_updown_hash, (void *)&nodeid, sizeof(int), (void*)0);
+ }
+}
+
+/* Get a list of active cluster members */
+static void get_members()
+{
+ int retnodes;
+ int status;
+ int i;
+ int high_nodeid = 0;
+
+ num_nodes = cman_get_node_count(c_handle);
+ if (num_nodes == -1) {
+ log_error("Unable to get node count");
+ return;
+ }
+
+ /* Not enough room for new nodes list ? */
+ if (num_nodes > count_nodes && nodes) {
+ free(nodes);
+ nodes = NULL;
+ }
+
+ if (nodes == NULL) {
+ count_nodes = num_nodes + 10; /* Overallocate a little */
+ nodes = malloc(count_nodes * sizeof(struct cman_node));
+ if (!nodes) {
+ log_error("Unable to allocate nodes array\n");
+ exit(5);
+ }
+ }
+
+ status = cman_get_nodes(c_handle, count_nodes, &retnodes, nodes);
+ if (status < 0) {
+ log_error("Unable to get node details");
+ exit(6);
+ }
+
+ /* Get the highest nodeid */
+ for (i=0; i<retnodes; i++) {
+ if (nodes[i].cn_nodeid > high_nodeid)
+ high_nodeid = nodes[i].cn_nodeid;
+ }
+}
+
+
+/* Convert a node name to a CSID */
+static int _csid_from_name(char *csid, const char *name)
+{
+ int i;
+
+ for (i = 0; i < num_nodes; i++) {
+ if (strcmp(name, nodes[i].cn_name) == 0) {
+ memcpy(csid, &nodes[i].cn_nodeid, CMAN_MAX_CSID_LEN);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/* Convert a CSID to a node name */
+static int _name_from_csid(const char *csid, char *name)
+{
+ int i;
+
+ for (i = 0; i < num_nodes; i++) {
+ if (memcmp(csid, &nodes[i].cn_nodeid, CMAN_MAX_CSID_LEN) == 0) {
+ strcpy(name, nodes[i].cn_name);
+ return 0;
+ }
+ }
+ /* Who?? */
+ strcpy(name, "Unknown");
+ return -1;
+}
+
+/* Convert a node ID to a node name */
+static int name_from_nodeid(int nodeid, char *name)
+{
+ int i;
+
+ for (i = 0; i < num_nodes; i++) {
+ if (nodeid == nodes[i].cn_nodeid) {
+ strcpy(name, nodes[i].cn_name);
+ return 0;
+ }
+ }
+ /* Who?? */
+ strcpy(name, "Unknown");
+ return -1;
+}
+
+/* Convert a CSID to a node ID */
+static int nodeid_from_csid(const char *csid)
+{
+ int nodeid;
+
+ memcpy(&nodeid, csid, CMAN_MAX_CSID_LEN);
+
+ return nodeid;
+}
+
+static int _is_quorate()
+{
+ return cman_is_quorate(c_handle);
+}
+
+static void sync_ast_routine(void *arg)
+{
+ struct lock_wait *lwait = arg;
+
+ pthread_mutex_lock(&lwait->mutex);
+ pthread_cond_signal(&lwait->cond);
+ pthread_mutex_unlock(&lwait->mutex);
+}
+
+static int _sync_lock(const char *resource, int mode, int flags, int *lockid)
+{
+ int status;
+ struct lock_wait lwait;
+
+ if (!lockid) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ DEBUGLOG("sync_lock: '%s' mode:%d flags=%d\n", resource,mode,flags);
+ /* Conversions need the lockid in the LKSB */
+ if (flags & LKF_CONVERT)
+ lwait.lksb.sb_lkid = *lockid;
+
+ pthread_cond_init(&lwait.cond, NULL);
+ pthread_mutex_init(&lwait.mutex, NULL);
+ pthread_mutex_lock(&lwait.mutex);
+
+ status = dlm_ls_lock(lockspace,
+ mode,
+ &lwait.lksb,
+ flags,
+ resource,
+ strlen(resource),
+ 0, sync_ast_routine, &lwait, NULL, NULL);
+ if (status)
+ return status;
+
+ /* Wait for it to complete */
+ pthread_cond_wait(&lwait.cond, &lwait.mutex);
+ pthread_mutex_unlock(&lwait.mutex);
+
+ *lockid = lwait.lksb.sb_lkid;
+
+ errno = lwait.lksb.sb_status;
+ DEBUGLOG("sync_lock: returning lkid %x\n", *lockid);
+ if (lwait.lksb.sb_status)
+ return -1;
+ else
+ return 0;
+}
+
+static int _sync_unlock(const char *resource /* UNUSED */, int lockid)
+{
+ int status;
+ struct lock_wait lwait;
+
+ DEBUGLOG("sync_unlock: '%s' lkid:%x\n", resource, lockid);
+
+ pthread_cond_init(&lwait.cond, NULL);
+ pthread_mutex_init(&lwait.mutex, NULL);
+ pthread_mutex_lock(&lwait.mutex);
+
+ status = dlm_ls_unlock(lockspace, lockid, 0, &lwait.lksb, &lwait);
+
+ if (status)
+ return status;
+
+ /* Wait for it to complete */
+ pthread_cond_wait(&lwait.cond, &lwait.mutex);
+ pthread_mutex_unlock(&lwait.mutex);
+
+ errno = lwait.lksb.sb_status;
+ if (lwait.lksb.sb_status != EUNLOCK)
+ return -1;
+ else
+ return 0;
+
+}
+
+static int _get_cluster_name(char *buf, int buflen)
+{
+ cman_cluster_t cluster_info;
+ int status;
+
+ status = cman_get_cluster(c_handle, &cluster_info);
+ if (!status) {
+ strncpy(buf, cluster_info.ci_name, buflen);
+ }
+ return status;
+}
+
+static struct cluster_ops _cluster_cman_ops = {
+ .cluster_init_completed = _cluster_init_completed,
+ .cluster_send_message = _cluster_send_message,
+ .name_from_csid = _name_from_csid,
+ .csid_from_name = _csid_from_name,
+ .get_num_nodes = _get_num_nodes,
+ .cluster_fd_callback = _cluster_fd_callback,
+ .get_main_cluster_fd = _get_main_cluster_fd,
+ .cluster_do_node_callback = _cluster_do_node_callback,
+ .is_quorate = _is_quorate,
+ .get_our_csid = _get_our_csid,
+ .add_up_node = _add_up_node,
+ .cluster_closedown = _cluster_closedown,
+ .get_cluster_name = _get_cluster_name,
+ .sync_lock = _sync_lock,
+ .sync_unlock = _sync_unlock,
+};
+
+struct cluster_ops *init_cman_cluster(void)
+{
+ if (!_init_cluster())
+ return &_cluster_cman_ops;
+ else
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+
+ CLVMD Cluster LVM daemon command processor.
+
+ To add commands to the daemon simply add a processor in do_command and return
+ and messages back in buf and the length in *retlen. The initial value of
+ buflen is the maximum size of the buffer. if buf is not large enough then it
+ may be reallocated by the functions in here to a suitable size bearing in
+ mind that anything larger than the passed-in size will have to be returned
+ using the system LV and so performance will suffer.
+
+ The status return will be negated and passed back to the originating node.
+
+ pre- and post- command routines are called only on the local node. The
+ purpose is primarily to get and release locks, though the pre- routine should
+ also do any other local setups required by the command (if any) and can
+ return a failure code that prevents the command from being distributed around
+ the cluster
+
+ The pre- and post- routines are run in their own thread so can block as long
+ they like, do_command is run in the main clvmd thread so should not block for
+ too long. If the pre-command returns an error code (!=0) then the command
+ will not be propogated around the cluster but the post-command WILL be called
+
+ Also note that the pre and post routine are *always* called on the local
+ node, even if the command to be executed was only requested to run on a
+ remote node. It may peek inside the client structure to check the status of
+ the command.
+
+ The clients of the daemon must, naturally, understand the return messages and
+ codes.
+
+ Routines in here may only READ the values in the client structure passed in
+ apart from client->private which they are free to do what they like with.
+
+*/
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+
+#include "clvmd-comms.h"
+#include "clvm.h"
+#include "clvmd.h"
+#include "lvm-functions.h"
+
+#include "locking.h"
+
+#include <sys/utsname.h>
+
+extern debug_t debug;
+extern struct cluster_ops *clops;
+static int restart_clvmd(void);
+
+/* This is where all the real work happens:
+ NOTE: client will be NULL when this is executed on a remote node */
+int do_command(struct local_client *client, struct clvm_header *msg, int msglen,
+ char **buf, int buflen, int *retlen)
+{
+ char *args = msg->node + strlen(msg->node) + 1;
+ int arglen = msglen - sizeof(struct clvm_header) - strlen(msg->node);
+ int status = 0;
+ char *lockname;
+ const char *locktype;
+ struct utsname nodeinfo;
+ unsigned char lock_cmd;
+ unsigned char lock_flags;
+
+ /* Do the command */
+ switch (msg->cmd) {
+ /* Just a test message */
+ case CLVMD_CMD_TEST:
+ if (arglen > buflen) {
+ char *new_buf;
+ buflen = arglen + 200;
+ new_buf = realloc(*buf, buflen);
+ if (new_buf == NULL) {
+ status = errno;
+ free (*buf);
+ }
+ *buf = new_buf;
+ }
+ if (*buf) {
+ uname(&nodeinfo);
+ *retlen = 1 + dm_snprintf(*buf, buflen,
+ "TEST from %s: %s v%s",
+ nodeinfo.nodename, args,
+ nodeinfo.release);
+ }
+ break;
+
+ case CLVMD_CMD_LOCK_VG:
+ lock_cmd = args[0];
+ lock_flags = args[1];
+ lockname = &args[2];
+ /* Check to see if the VG is in use by LVM1 */
+ status = do_check_lvm1(lockname);
+ do_lock_vg(lock_cmd, lock_flags, lockname);
+ break;
+
+ case CLVMD_CMD_LOCK_LV:
+ /* This is the biggie */
+ lock_cmd = args[0] & (LCK_NONBLOCK | LCK_HOLD | LCK_SCOPE_MASK | LCK_TYPE_MASK);
+ lock_flags = args[1];
+ lockname = &args[2];
+ status = do_lock_lv(lock_cmd, lock_flags, lockname);
+ /* Replace EIO with something less scary */
+ if (status == EIO) {
+ *retlen = 1 + dm_snprintf(*buf, buflen, "%s",
+ get_last_lvm_error());
+ return EIO;
+ }
+ break;
+
+ case CLVMD_CMD_LOCK_QUERY:
+ lockname = &args[2];
+ if (buflen < 3)
+ return EIO;
+ if ((locktype = do_lock_query(lockname)))
+ *retlen = 1 + dm_snprintf(*buf, buflen, "%s", locktype);
+ break;
+
+ case CLVMD_CMD_REFRESH:
+ do_refresh_cache();
+ break;
+
+ case CLVMD_CMD_SET_DEBUG:
+ debug = args[0];
+ break;
+
+ case CLVMD_CMD_RESTART:
+ restart_clvmd();
+ break;
+
+ case CLVMD_CMD_GET_CLUSTERNAME:
+ status = clops->get_cluster_name(*buf, buflen);
+ if (!status)
+ *retlen = strlen(*buf)+1;
+ break;
+
+ case CLVMD_CMD_VG_BACKUP:
+ /*
+ * Do not run backup on local node, caller should do that.
+ */
+ if (!client)
+ lvm_do_backup(&args[2]);
+ break;
+
+ default:
+ /* Won't get here because command is validated in pre_command */
+ break;
+ }
+
+ /* Check the status of the command and return the error text */
+ if (status) {
+ *retlen = 1 + ((*buf) ? dm_snprintf(*buf, buflen, "%s",
+ strerror(status)) : -1);
+ }
+
+ return status;
+
+}
+
+static int lock_vg(struct local_client *client)
+{
+ struct dm_hash_table *lock_hash;
+ struct clvm_header *header =
+ (struct clvm_header *) client->bits.localsock.cmd;
+ unsigned char lock_cmd;
+ unsigned char lock_flags;
+ int lock_mode;
+ char *args = header->node + strlen(header->node) + 1;
+ int lkid;
+ int status = 0;
+ char *lockname;
+
+ /* Keep a track of VG locks in our own hash table. In current
+ practice there should only ever be more than two VGs locked
+ if a user tries to merge lots of them at once */
+ if (client->bits.localsock.private) {
+ lock_hash = (struct dm_hash_table *)client->bits.localsock.private;
+ }
+ else {
+ lock_hash = dm_hash_create(3);
+ if (!lock_hash)
+ return ENOMEM;
+ client->bits.localsock.private = (void *)lock_hash;
+ }
+
+ lock_cmd = args[0] & (LCK_NONBLOCK | LCK_HOLD | LCK_SCOPE_MASK | LCK_TYPE_MASK);
+ lock_mode = ((int)lock_cmd & LCK_TYPE_MASK);
+ lock_flags = args[1];
+ lockname = &args[2];
+ DEBUGLOG("doing PRE command LOCK_VG '%s' at %x (client=%p)\n", lockname, lock_cmd, client);
+
+ if (lock_mode == LCK_UNLOCK) {
+
+ lkid = (int)(long)dm_hash_lookup(lock_hash, lockname);
+ if (lkid == 0)
+ return EINVAL;
+
+ status = sync_unlock(lockname, lkid);
+ if (status)
+ status = errno;
+ else
+ dm_hash_remove(lock_hash, lockname);
+ }
+ else {
+ /* Read locks need to be PR; other modes get passed through */
+ if (lock_mode == LCK_READ)
+ lock_mode = LCK_PREAD;
+ status = sync_lock(lockname, lock_mode, (lock_cmd & LCK_NONBLOCK) ? LCKF_NOQUEUE : 0, &lkid);
+ if (status)
+ status = errno;
+ else
+ dm_hash_insert(lock_hash, lockname, (void *)(long)lkid);
+ }
+
+ return status;
+}
+
+
+/* Pre-command is a good place to get locks that are needed only for the duration
+ of the commands around the cluster (don't forget to free them in post-command),
+ and to sanity check the command arguments */
+int do_pre_command(struct local_client *client)
+{
+ struct clvm_header *header =
+ (struct clvm_header *) client->bits.localsock.cmd;
+ unsigned char lock_cmd;
+ unsigned char lock_flags;
+ char *args = header->node + strlen(header->node) + 1;
+ int lockid;
+ int status = 0;
+ char *lockname;
+
+ switch (header->cmd) {
+ case CLVMD_CMD_TEST:
+ status = sync_lock("CLVMD_TEST", LCK_EXCL, 0, &lockid);
+ client->bits.localsock.private = (void *)(long)lockid;
+ break;
+
+ case CLVMD_CMD_LOCK_VG:
+ lockname = &args[2];
+ /* We take out a real lock unless LCK_CACHE was set */
+ if (!strncmp(lockname, "V_", 2) ||
+ !strncmp(lockname, "P_#", 3))
+ status = lock_vg(client);
+ break;
+
+ case CLVMD_CMD_LOCK_LV:
+ lock_cmd = args[0];
+ lock_flags = args[1];
+ lockname = &args[2];
+ status = pre_lock_lv(lock_cmd, lock_flags, lockname);
+ break;
+
+ case CLVMD_CMD_REFRESH:
+ case CLVMD_CMD_GET_CLUSTERNAME:
+ case CLVMD_CMD_SET_DEBUG:
+ case CLVMD_CMD_VG_BACKUP:
+ case CLVMD_CMD_LOCK_QUERY:
+ case CLVMD_CMD_RESTART:
+ break;
+
+ default:
+ log_error("Unknown command %d received\n", header->cmd);
+ status = EINVAL;
+ }
+ return status;
+}
+
+/* Note that the post-command routine is called even if the pre-command or the real command
+ failed */
+int do_post_command(struct local_client *client)
+{
+ struct clvm_header *header =
+ (struct clvm_header *) client->bits.localsock.cmd;
+ int status = 0;
+ unsigned char lock_cmd;
+ unsigned char lock_flags;
+ char *args = header->node + strlen(header->node) + 1;
+ char *lockname;
+
+ switch (header->cmd) {
+ case CLVMD_CMD_TEST:
+ status =
+ sync_unlock("CLVMD_TEST", (int) (long) client->bits.localsock.private);
+ client->bits.localsock.private = 0;
+ break;
+
+ case CLVMD_CMD_LOCK_VG:
+ case CLVMD_CMD_VG_BACKUP:
+ case CLVMD_CMD_LOCK_QUERY:
+ /* Nothing to do here */
+ break;
+
+ case CLVMD_CMD_LOCK_LV:
+ lock_cmd = args[0];
+ lock_flags = args[1];
+ lockname = &args[2];
+ status = post_lock_lv(lock_cmd, lock_flags, lockname);
+ break;
+ }
+ return status;
+}
+
+
+/* Called when the client is about to be deleted */
+void cmd_client_cleanup(struct local_client *client)
+{
+ if (client->bits.localsock.private) {
+
+ struct dm_hash_node *v;
+ struct dm_hash_table *lock_hash =
+ (struct dm_hash_table *)client->bits.localsock.private;
+
+ dm_hash_iterate(v, lock_hash) {
+ int lkid = (int)(long)dm_hash_get_data(lock_hash, v);
+ char *lockname = dm_hash_get_key(lock_hash, v);
+
+ DEBUGLOG("cleanup: Unlocking lock %s %x\n", lockname, lkid);
+ sync_unlock(lockname, lkid);
+ }
+
+ dm_hash_destroy(lock_hash);
+ client->bits.localsock.private = 0;
+ }
+}
+
+
+static int restart_clvmd(void)
+{
+ char **argv = NULL;
+ char *debug_arg = NULL, *lv_name;
+ int i, argc = 0, max_locks = 0;
+ struct dm_hash_node *hn = NULL;
+
+ DEBUGLOG("clvmd restart requested\n");
+
+ /* Count exclusively-open LVs */
+ hn = NULL;
+ do {
+ hn = get_next_excl_lock(hn, &lv_name);
+ if (lv_name)
+ max_locks++;
+ } while (hn && *lv_name);
+
+ /* clvmd + locks (-E uuid) + debug (-d X) + NULL */
+ argv = malloc((max_locks * 2 + 4) * sizeof(*argv));
+ if (!argv)
+ goto_out;
+
+ /*
+ * Build the command-line
+ */
+ argv[argc++] = strdup("clvmd");
+ if (!argv[0])
+ goto_out;
+
+ /* Propogate debug options */
+ if (debug) {
+ if (!(debug_arg = malloc(16)) ||
+ dm_snprintf(debug_arg, 16, "-d%d", (int)debug) < 0)
+ goto_out;
+ argv[argc++] = debug_arg;
+ }
+
+ /* Now add the exclusively-open LVs */
+ do {
+ hn = get_next_excl_lock(hn, &lv_name);
+ if (lv_name) {
+ argv[argc] = strdup("-E");
+ if (!argv[argc++])
+ goto_out;
+ argv[argc] = strdup(lv_name);
+ if (!argv[argc++])
+ goto_out;
+
+ DEBUGLOG("excl lock: %s\n", lv_name);
+ hn = get_next_excl_lock(hn, &lv_name);
+ }
+ } while (hn && *lv_name);
+ argv[argc++] = NULL;
+
+ /* Exec new clvmd */
+ /* NOTE: This will fail when downgrading! */
+ execve(CLVMD_PATH, argv, NULL);
+out:
+ /* We failed */
+ DEBUGLOG("Restart of clvmd failed.\n");
+
+ for (i = 0; i < argc && argv[i]; i++)
+ free(argv[i]);
+ free(argv);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This file must be included first by every clvmd source file.
+ */
+#ifndef _LVM_CLVMD_COMMON_H
+#define _LVM_CLVMD_COMMON_H
+
+#include "configure.h"
+
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "libdevmapper.h"
+
+#include "lvm-logging.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Abstraction layer for clvmd cluster communications
+ */
+
+#ifndef _CLVMD_COMMS_H
+#define _CLVMD_COMMS_H
+
+struct local_client;
+
+struct cluster_ops {
+ void (*cluster_init_completed) (void);
+
+ int (*cluster_send_message) (const void *buf, int msglen,
+ const char *csid,
+ const char *errtext);
+ int (*name_from_csid) (const char *csid, char *name);
+ int (*csid_from_name) (char *csid, const char *name);
+ int (*get_num_nodes) (void);
+ int (*cluster_fd_callback) (struct local_client *fd, char *buf, int len,
+ const char *csid,
+ struct local_client **new_client);
+ int (*get_main_cluster_fd) (void); /* gets accept FD or cman cluster socket */
+ int (*cluster_do_node_callback) (struct local_client *client,
+ void (*callback) (struct local_client *,
+ const char *csid,
+ int node_up));
+ int (*is_quorate) (void);
+
+ void (*get_our_csid) (char *csid);
+ void (*add_up_node) (const char *csid);
+ void (*reread_config) (void);
+ void (*cluster_closedown) (void);
+
+ int (*get_cluster_name)(char *buf, int buflen);
+
+ int (*sync_lock) (const char *resource, int mode,
+ int flags, int *lockid);
+ int (*sync_unlock) (const char *resource, int lockid);
+
+};
+
+#ifdef USE_GULM
+# include "tcp-comms.h"
+struct cluster_ops *init_gulm_cluster(void);
+#define MAX_CSID_LEN GULM_MAX_CSID_LEN
+#define MAX_CLUSTER_MEMBER_NAME_LEN GULM_MAX_CLUSTER_MEMBER_NAME_LEN
+#endif
+
+#ifdef USE_CMAN
+# include <netinet/in.h>
+# include "libcman.h"
+# define CMAN_MAX_CSID_LEN 4
+# ifndef MAX_CSID_LEN
+# define MAX_CSID_LEN CMAN_MAX_CSID_LEN
+# endif
+# undef MAX_CLUSTER_MEMBER_NAME_LEN
+# define MAX_CLUSTER_MEMBER_NAME_LEN CMAN_MAX_NODENAME_LEN
+# define CMAN_MAX_CLUSTER_MESSAGE 1500
+# define CLUSTER_PORT_CLVMD 11
+struct cluster_ops *init_cman_cluster(void);
+#endif
+
+#ifdef USE_OPENAIS
+# include <openais/saAis.h>
+# include <corosync/totem/totem.h>
+# define OPENAIS_CSID_LEN (sizeof(int))
+# define OPENAIS_MAX_CLUSTER_MESSAGE MESSAGE_SIZE_MAX
+# define OPENAIS_MAX_CLUSTER_MEMBER_NAME_LEN SA_MAX_NAME_LENGTH
+# ifndef MAX_CLUSTER_MEMBER_NAME_LEN
+# define MAX_CLUSTER_MEMBER_NAME_LEN SA_MAX_NAME_LENGTH
+# endif
+# ifndef CMAN_MAX_CLUSTER_MESSAGE
+# define CMAN_MAX_CLUSTER_MESSAGE MESSAGE_SIZE_MAX
+# endif
+# ifndef MAX_CSID_LEN
+# define MAX_CSID_LEN sizeof(int)
+# endif
+struct cluster_ops *init_openais_cluster(void);
+#endif
+
+#ifdef USE_COROSYNC
+# include <corosync/corotypes.h>
+# define COROSYNC_CSID_LEN (sizeof(int))
+# define COROSYNC_MAX_CLUSTER_MESSAGE 65535
+# define COROSYNC_MAX_CLUSTER_MEMBER_NAME_LEN CS_MAX_NAME_LENGTH
+# ifndef MAX_CLUSTER_MEMBER_NAME_LEN
+# define MAX_CLUSTER_MEMBER_NAME_LEN CS_MAX_NAME_LENGTH
+# endif
+# ifndef CMAN_MAX_CLUSTER_MESSAGE
+# define CMAN_MAX_CLUSTER_MESSAGE 65535
+# endif
+# ifndef MAX_CSID_LEN
+# define MAX_CSID_LEN sizeof(int)
+# endif
+struct cluster_ops *init_corosync_cluster(void);
+#endif
+
+#ifdef USE_SINGLENODE
+# define SINGLENODE_CSID_LEN (sizeof(int))
+# ifndef MAX_CLUSTER_MEMBER_NAME_LEN
+# define MAX_CLUSTER_MEMBER_NAME_LEN 64
+# endif
+# define SINGLENODE_MAX_CLUSTER_MESSAGE 65535
+# ifndef MAX_CSID_LEN
+# define MAX_CSID_LEN sizeof(int)
+# endif
+struct cluster_ops *init_singlenode_cluster(void);
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This provides the interface between clvmd and corosync/DLM as the cluster
+ * and lock manager.
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+
+#include "clvm.h"
+#include "clvmd-comms.h"
+#include "clvmd.h"
+#include "lvm-functions.h"
+
+#include "locking.h"
+
+#include <corosync/cpg.h>
+#include <corosync/quorum.h>
+#include <corosync/confdb.h>
+#include <libdlm.h>
+
+#include <syslog.h>
+
+/* Timeout value for several corosync calls */
+#define LOCKSPACE_NAME "clvmd"
+
+static void corosync_cpg_deliver_callback (cpg_handle_t handle,
+ const struct cpg_name *groupName,
+ uint32_t nodeid,
+ uint32_t pid,
+ void *msg,
+ size_t msg_len);
+static void corosync_cpg_confchg_callback(cpg_handle_t handle,
+ const struct cpg_name *groupName,
+ const struct cpg_address *member_list, size_t member_list_entries,
+ const struct cpg_address *left_list, size_t left_list_entries,
+ const struct cpg_address *joined_list, size_t joined_list_entries);
+static void _cluster_closedown(void);
+
+/* Hash list of nodes in the cluster */
+static struct dm_hash_table *node_hash;
+
+/* Number of active nodes */
+static int num_nodes;
+static unsigned int our_nodeid;
+
+static struct local_client *cluster_client;
+
+/* Corosync handles */
+static cpg_handle_t cpg_handle;
+static quorum_handle_t quorum_handle;
+
+/* DLM Handle */
+static dlm_lshandle_t *lockspace;
+
+static struct cpg_name cpg_group_name;
+
+/* Corosync callback structs */
+cpg_callbacks_t corosync_cpg_callbacks = {
+ .cpg_deliver_fn = corosync_cpg_deliver_callback,
+ .cpg_confchg_fn = corosync_cpg_confchg_callback,
+};
+
+quorum_callbacks_t quorum_callbacks = {
+ .quorum_notify_fn = NULL,
+};
+
+struct node_info
+{
+ enum {NODE_UNKNOWN, NODE_DOWN, NODE_UP, NODE_CLVMD} state;
+ int nodeid;
+};
+
+
+/* Set errno to something approximating the right value and return 0 or -1 */
+static int cs_to_errno(cs_error_t err)
+{
+ switch(err)
+ {
+ case CS_OK:
+ return 0;
+ case CS_ERR_LIBRARY:
+ errno = EINVAL;
+ break;
+ case CS_ERR_VERSION:
+ errno = EINVAL;
+ break;
+ case CS_ERR_INIT:
+ errno = EINVAL;
+ break;
+ case CS_ERR_TIMEOUT:
+ errno = ETIME;
+ break;
+ case CS_ERR_TRY_AGAIN:
+ errno = EAGAIN;
+ break;
+ case CS_ERR_INVALID_PARAM:
+ errno = EINVAL;
+ break;
+ case CS_ERR_NO_MEMORY:
+ errno = ENOMEM;
+ break;
+ case CS_ERR_BAD_HANDLE:
+ errno = EINVAL;
+ break;
+ case CS_ERR_BUSY:
+ errno = EBUSY;
+ break;
+ case CS_ERR_ACCESS:
+ errno = EPERM;
+ break;
+ case CS_ERR_NOT_EXIST:
+ errno = ENOENT;
+ break;
+ case CS_ERR_NAME_TOO_LONG:
+ errno = ENAMETOOLONG;
+ break;
+ case CS_ERR_EXIST:
+ errno = EEXIST;
+ break;
+ case CS_ERR_NO_SPACE:
+ errno = ENOSPC;
+ break;
+ case CS_ERR_INTERRUPT:
+ errno = EINTR;
+ break;
+ case CS_ERR_NAME_NOT_FOUND:
+ errno = ENOENT;
+ break;
+ case CS_ERR_NO_RESOURCES:
+ errno = ENOMEM;
+ break;
+ case CS_ERR_NOT_SUPPORTED:
+ errno = EOPNOTSUPP;
+ break;
+ case CS_ERR_BAD_OPERATION:
+ errno = EINVAL;
+ break;
+ case CS_ERR_FAILED_OPERATION:
+ errno = EIO;
+ break;
+ case CS_ERR_MESSAGE_ERROR:
+ errno = EIO;
+ break;
+ case CS_ERR_QUEUE_FULL:
+ errno = EXFULL;
+ break;
+ case CS_ERR_QUEUE_NOT_AVAILABLE:
+ errno = EINVAL;
+ break;
+ case CS_ERR_BAD_FLAGS:
+ errno = EINVAL;
+ break;
+ case CS_ERR_TOO_BIG:
+ errno = E2BIG;
+ break;
+ case CS_ERR_NO_SECTIONS:
+ errno = ENOMEM;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
+ return -1;
+}
+
+static char *print_corosync_csid(const char *csid)
+{
+ static char buf[128];
+ int id;
+
+ memcpy(&id, csid, sizeof(int));
+ sprintf(buf, "%d", id);
+ return buf;
+}
+
+static void corosync_cpg_deliver_callback (cpg_handle_t handle,
+ const struct cpg_name *groupName,
+ uint32_t nodeid,
+ uint32_t pid,
+ void *msg,
+ size_t msg_len)
+{
+ int target_nodeid;
+
+ memcpy(&target_nodeid, msg, COROSYNC_CSID_LEN);
+
+ DEBUGLOG("%u got message from nodeid %d for %d. len %zd\n",
+ our_nodeid, nodeid, target_nodeid, msg_len-4);
+
+ if (nodeid != our_nodeid)
+ if (target_nodeid == our_nodeid || target_nodeid == 0)
+ process_message(cluster_client, (char *)msg+COROSYNC_CSID_LEN,
+ msg_len-COROSYNC_CSID_LEN, (char*)&nodeid);
+}
+
+static void corosync_cpg_confchg_callback(cpg_handle_t handle,
+ const struct cpg_name *groupName,
+ const struct cpg_address *member_list, size_t member_list_entries,
+ const struct cpg_address *left_list, size_t left_list_entries,
+ const struct cpg_address *joined_list, size_t joined_list_entries)
+{
+ int i;
+ struct node_info *ninfo;
+
+ DEBUGLOG("confchg callback. %zd joined, %zd left, %zd members\n",
+ joined_list_entries, left_list_entries, member_list_entries);
+
+ for (i=0; i<joined_list_entries; i++) {
+ ninfo = dm_hash_lookup_binary(node_hash,
+ (char *)&joined_list[i].nodeid,
+ COROSYNC_CSID_LEN);
+ if (!ninfo) {
+ ninfo = malloc(sizeof(struct node_info));
+ if (!ninfo) {
+ break;
+ }
+ else {
+ ninfo->nodeid = joined_list[i].nodeid;
+ dm_hash_insert_binary(node_hash,
+ (char *)&ninfo->nodeid,
+ COROSYNC_CSID_LEN, ninfo);
+ }
+ }
+ ninfo->state = NODE_CLVMD;
+ }
+
+ for (i=0; i<left_list_entries; i++) {
+ ninfo = dm_hash_lookup_binary(node_hash,
+ (char *)&left_list[i].nodeid,
+ COROSYNC_CSID_LEN);
+ if (ninfo)
+ ninfo->state = NODE_DOWN;
+ }
+
+ for (i=0; i<member_list_entries; i++) {
+ if (member_list[i].nodeid == 0) continue;
+ ninfo = dm_hash_lookup_binary(node_hash,
+ (char *)&member_list[i].nodeid,
+ COROSYNC_CSID_LEN);
+ if (!ninfo) {
+ ninfo = malloc(sizeof(struct node_info));
+ if (!ninfo) {
+ break;
+ }
+ else {
+ ninfo->nodeid = member_list[i].nodeid;
+ dm_hash_insert_binary(node_hash,
+ (char *)&ninfo->nodeid,
+ COROSYNC_CSID_LEN, ninfo);
+ }
+ }
+ ninfo->state = NODE_CLVMD;
+ }
+
+ num_nodes = member_list_entries;
+}
+
+static int _init_cluster(void)
+{
+ cs_error_t err;
+
+ node_hash = dm_hash_create(100);
+
+ err = cpg_initialize(&cpg_handle,
+ &corosync_cpg_callbacks);
+ if (err != CS_OK) {
+ syslog(LOG_ERR, "Cannot initialise Corosync CPG service: %d",
+ err);
+ DEBUGLOG("Cannot initialise Corosync CPG service: %d", err);
+ return cs_to_errno(err);
+ }
+
+ err = quorum_initialize(&quorum_handle,
+ &quorum_callbacks);
+ if (err != CS_OK) {
+ syslog(LOG_ERR, "Cannot initialise Corosync quorum service: %d",
+ err);
+ DEBUGLOG("Cannot initialise Corosync quorum service: %d", err);
+ return cs_to_errno(err);
+ }
+
+
+ /* Create a lockspace for LV & VG locks to live in */
+ lockspace = dlm_create_lockspace(LOCKSPACE_NAME, 0600);
+ if (!lockspace) {
+ if (errno == EEXIST) {
+ lockspace = dlm_open_lockspace(LOCKSPACE_NAME);
+ }
+ if (!lockspace) {
+ syslog(LOG_ERR, "Unable to create lockspace for CLVM: %m");
+ quorum_finalize(quorum_handle);
+ return -1;
+ }
+ }
+ dlm_ls_pthread_init(lockspace);
+ DEBUGLOG("DLM initialisation complete\n");
+
+ /* Connect to the clvmd group */
+ strcpy((char *)cpg_group_name.value, "clvmd");
+ cpg_group_name.length = strlen((char *)cpg_group_name.value);
+ err = cpg_join(cpg_handle, &cpg_group_name);
+ if (err != CS_OK) {
+ cpg_finalize(cpg_handle);
+ quorum_finalize(quorum_handle);
+ dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
+ syslog(LOG_ERR, "Cannot join clvmd process group");
+ DEBUGLOG("Cannot join clvmd process group: %d\n", err);
+ return cs_to_errno(err);
+ }
+
+ err = cpg_local_get(cpg_handle,
+ &our_nodeid);
+ if (err != CS_OK) {
+ cpg_finalize(cpg_handle);
+ quorum_finalize(quorum_handle);
+ dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
+ syslog(LOG_ERR, "Cannot get local node id\n");
+ return cs_to_errno(err);
+ }
+ DEBUGLOG("Our local node id is %d\n", our_nodeid);
+
+ DEBUGLOG("Connected to Corosync\n");
+
+ return 0;
+}
+
+static void _cluster_closedown(void)
+{
+ DEBUGLOG("cluster_closedown\n");
+ destroy_lvhash();
+
+ dlm_release_lockspace(LOCKSPACE_NAME, lockspace, 1);
+ cpg_finalize(cpg_handle);
+ quorum_finalize(quorum_handle);
+}
+
+static void _get_our_csid(char *csid)
+{
+ memcpy(csid, &our_nodeid, sizeof(int));
+}
+
+/* Corosync doesn't really have nmode names so we
+ just use the node ID in hex instead */
+static int _csid_from_name(char *csid, const char *name)
+{
+ int nodeid;
+ struct node_info *ninfo;
+
+ if (sscanf(name, "%x", &nodeid) == 1) {
+ ninfo = dm_hash_lookup_binary(node_hash, csid, COROSYNC_CSID_LEN);
+ if (ninfo)
+ return nodeid;
+ }
+ return -1;
+}
+
+static int _name_from_csid(const char *csid, char *name)
+{
+ struct node_info *ninfo;
+
+ ninfo = dm_hash_lookup_binary(node_hash, csid, COROSYNC_CSID_LEN);
+ if (!ninfo)
+ {
+ sprintf(name, "UNKNOWN %s", print_corosync_csid(csid));
+ return -1;
+ }
+
+ sprintf(name, "%x", ninfo->nodeid);
+ return 0;
+}
+
+static int _get_num_nodes()
+{
+ DEBUGLOG("num_nodes = %d\n", num_nodes);
+ return num_nodes;
+}
+
+/* Node is now known to be running a clvmd */
+static void _add_up_node(const char *csid)
+{
+ struct node_info *ninfo;
+
+ ninfo = dm_hash_lookup_binary(node_hash, csid, COROSYNC_CSID_LEN);
+ if (!ninfo) {
+ DEBUGLOG("corosync_add_up_node no node_hash entry for csid %s\n",
+ print_corosync_csid(csid));
+ return;
+ }
+
+ DEBUGLOG("corosync_add_up_node %d\n", ninfo->nodeid);
+
+ ninfo->state = NODE_CLVMD;
+
+ return;
+}
+
+/* Call a callback for each node, so the caller knows whether it's up or down */
+static int _cluster_do_node_callback(struct local_client *master_client,
+ void (*callback)(struct local_client *,
+ const char *csid, int node_up))
+{
+ struct dm_hash_node *hn;
+ struct node_info *ninfo;
+ int somedown = 0;
+
+ dm_hash_iterate(hn, node_hash)
+ {
+ char csid[COROSYNC_CSID_LEN];
+
+ ninfo = dm_hash_get_data(node_hash, hn);
+ memcpy(csid, dm_hash_get_key(node_hash, hn), COROSYNC_CSID_LEN);
+
+ DEBUGLOG("down_callback. node %d, state = %d\n", ninfo->nodeid,
+ ninfo->state);
+
+ if (ninfo->state != NODE_DOWN)
+ callback(master_client, csid, ninfo->state == NODE_CLVMD);
+ if (ninfo->state != NODE_CLVMD)
+ somedown = -1;
+ }
+ return somedown;
+}
+
+/* Real locking */
+static int _lock_resource(const char *resource, int mode, int flags, int *lockid)
+{
+ struct dlm_lksb lksb;
+ int err;
+
+ DEBUGLOG("lock_resource '%s', flags=%d, mode=%d\n", resource, flags, mode);
+
+ if (flags & LKF_CONVERT)
+ lksb.sb_lkid = *lockid;
+
+ err = dlm_ls_lock_wait(lockspace,
+ mode,
+ &lksb,
+ flags,
+ resource,
+ strlen(resource),
+ 0,
+ NULL, NULL, NULL);
+
+ if (err != 0)
+ {
+ DEBUGLOG("dlm_ls_lock returned %d\n", errno);
+ return err;
+ }
+ if (lksb.sb_status != 0)
+ {
+ DEBUGLOG("dlm_ls_lock returns lksb.sb_status %d\n", lksb.sb_status);
+ errno = lksb.sb_status;
+ return -1;
+ }
+
+ DEBUGLOG("lock_resource returning %d, lock_id=%x\n", err, lksb.sb_lkid);
+
+ *lockid = lksb.sb_lkid;
+
+ return 0;
+}
+
+
+static int _unlock_resource(const char *resource, int lockid)
+{
+ struct dlm_lksb lksb;
+ int err;
+
+ DEBUGLOG("unlock_resource: %s lockid: %x\n", resource, lockid);
+ lksb.sb_lkid = lockid;
+
+ err = dlm_ls_unlock_wait(lockspace,
+ lockid,
+ 0,
+ &lksb);
+ if (err != 0)
+ {
+ DEBUGLOG("Unlock returned %d\n", err);
+ return err;
+ }
+ if (lksb.sb_status != EUNLOCK)
+ {
+ DEBUGLOG("dlm_ls_unlock_wait returns lksb.sb_status: %d\n", lksb.sb_status);
+ errno = lksb.sb_status;
+ return -1;
+ }
+
+
+ return 0;
+}
+
+static int _is_quorate()
+{
+ int quorate;
+ if (quorum_getquorate(quorum_handle, &quorate) == CS_OK)
+ return quorate;
+ else
+ return 0;
+}
+
+static int _get_main_cluster_fd(void)
+{
+ int select_fd;
+
+ cpg_fd_get(cpg_handle, &select_fd);
+ return select_fd;
+}
+
+static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
+ const char *csid,
+ struct local_client **new_client)
+{
+ cluster_client = fd;
+ *new_client = NULL;
+ cpg_dispatch(cpg_handle, CS_DISPATCH_ONE);
+ return 1;
+}
+
+static int _cluster_send_message(const void *buf, int msglen, const char *csid,
+ const char *errtext)
+{
+ struct iovec iov[2];
+ cs_error_t err;
+ int target_node;
+
+ if (csid)
+ memcpy(&target_node, csid, COROSYNC_CSID_LEN);
+ else
+ target_node = 0;
+
+ iov[0].iov_base = &target_node;
+ iov[0].iov_len = sizeof(int);
+ iov[1].iov_base = (char *)buf;
+ iov[1].iov_len = msglen;
+
+ err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2);
+ return cs_to_errno(err);
+}
+
+/*
+ * We are not necessarily connected to a Red Hat Cluster system,
+ * but if we are, this returns the cluster name from cluster.conf.
+ * I've used confdb rather than ccs to reduce the inter-package
+ * dependancies as well as to allow people to set a cluster name
+ * for themselves even if they are not running on RH cluster.
+ */
+static int _get_cluster_name(char *buf, int buflen)
+{
+ confdb_handle_t handle;
+ int result;
+ size_t namelen = buflen;
+ hdb_handle_t cluster_handle;
+ confdb_callbacks_t callbacks = {
+ .confdb_key_change_notify_fn = NULL,
+ .confdb_object_create_change_notify_fn = NULL,
+ .confdb_object_delete_change_notify_fn = NULL
+ };
+
+ /* This is a default in case everything else fails */
+ strncpy(buf, "Corosync", buflen);
+
+ /* Look for a cluster name in confdb */
+ result = confdb_initialize (&handle, &callbacks);
+ if (result != CS_OK)
+ return 0;
+
+ result = confdb_object_find_start(handle, OBJECT_PARENT_HANDLE);
+ if (result != CS_OK)
+ goto out;
+
+ result = confdb_object_find(handle, OBJECT_PARENT_HANDLE, (void *)"cluster", strlen("cluster"), &cluster_handle);
+ if (result != CS_OK)
+ goto out;
+
+ result = confdb_key_get(handle, cluster_handle, (void *)"name", strlen("name"), buf, &namelen);
+ if (result != CS_OK)
+ goto out;
+
+ buf[namelen] = '\0';
+
+out:
+ confdb_finalize(handle);
+ return 0;
+}
+
+static struct cluster_ops _cluster_corosync_ops = {
+ .cluster_init_completed = NULL,
+ .cluster_send_message = _cluster_send_message,
+ .name_from_csid = _name_from_csid,
+ .csid_from_name = _csid_from_name,
+ .get_num_nodes = _get_num_nodes,
+ .cluster_fd_callback = _cluster_fd_callback,
+ .get_main_cluster_fd = _get_main_cluster_fd,
+ .cluster_do_node_callback = _cluster_do_node_callback,
+ .is_quorate = _is_quorate,
+ .get_our_csid = _get_our_csid,
+ .add_up_node = _add_up_node,
+ .reread_config = NULL,
+ .cluster_closedown = _cluster_closedown,
+ .get_cluster_name = _get_cluster_name,
+ .sync_lock = _lock_resource,
+ .sync_unlock = _unlock_resource,
+};
+
+struct cluster_ops *init_corosync_cluster(void)
+{
+ if (!_init_cluster())
+ return &_cluster_corosync_ops;
+ else
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2003 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This provides the interface between clvmd and gulm as the cluster
+ * and lock manager.
+ *
+ * It also provides the "liblm" functions too as it's hard (and pointless)
+ * to seperate them out when using gulm.
+ *
+ * What it does /not/ provide is the communications between clvmd daemons
+ * on the cluster nodes. That is done in tcp-comms.c
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+#include <sys/utsname.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdint.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <utmpx.h>
+#include <syslog.h>
+#include <assert.h>
+#include <ccs.h>
+#include <libgulm.h>
+
+#include "locking.h"
+#include "clvm.h"
+#include "clvmd-comms.h"
+#include "lvm-functions.h"
+#include "clvmd.h"
+#include "clvmd-gulm.h"
+
+/* Hash list of nodes in the cluster */
+static struct dm_hash_table *node_hash;
+
+/* hash list of outstanding lock requests */
+static struct dm_hash_table *lock_hash;
+
+/* Copy of the current quorate state */
+static uint8_t gulm_quorate = 0;
+static enum {INIT_NOTDONE, INIT_DONE, INIT_WAITQUORATE} init_state = INIT_NOTDONE;
+
+/* Number of active nodes */
+static int num_nodes;
+
+static char *cluster_name;
+static int in_shutdown = 0;
+
+static pthread_mutex_t lock_start_mutex;
+static volatile int lock_start_flag;
+
+struct node_info
+{
+ enum {NODE_UNKNOWN, NODE_DOWN, NODE_UP, NODE_CLVMD} state;
+ char name[GULM_MAX_CLUSTER_MEMBER_NAME_LEN];
+};
+
+struct lock_wait
+{
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ int status;
+};
+
+/* Forward */
+static int read_from_core_sock(struct local_client *client, char *buf, int len, const char *csid,
+ struct local_client **new_client);
+static int read_from_lock_sock(struct local_client *client, char *buf, int len, const char *csid,
+ struct local_client **new_client);
+static int get_all_cluster_nodes(void);
+static int _csid_from_name(char *csid, const char *name);
+static void _cluster_closedown(void);
+
+/* In tcp-comms.c */
+extern struct dm_hash_table *sock_hash;
+
+static int add_internal_client(int fd, fd_callback_t callback)
+{
+ struct local_client *client;
+
+ DEBUGLOG("Add_internal_client, fd = %d\n", fd);
+
+ /* Add a GULM file descriptor it to the main loop */
+ client = malloc(sizeof(struct local_client));
+ if (!client)
+ {
+ DEBUGLOG("malloc failed\n");
+ return -1;
+ }
+
+ memset(client, 0, sizeof(struct local_client));
+ client->fd = fd;
+ client->type = CLUSTER_INTERNAL;
+ client->callback = callback;
+ add_client(client);
+
+ /* Set Close-on-exec */
+ fcntl(fd, F_SETFD, 1);
+
+ return 0;
+}
+
+/* Gulm library handle */
+static gulm_interface_p gulm_if;
+static lg_core_callbacks_t core_callbacks;
+static lg_lockspace_callbacks_t lock_callbacks;
+
+static void badsig_handler(int sig)
+{
+ DEBUGLOG("got sig %d\n", sig);
+ _cluster_closedown();
+ exit(0);
+}
+
+static void _reread_config(void)
+{
+ /* Re-read CCS node list */
+ DEBUGLOG("Re-reading CCS config\n");
+ get_all_cluster_nodes();
+}
+
+static int _init_cluster(void)
+{
+ int status;
+ int ccs_h;
+ int port = 0;
+ char *portstr;
+
+ /* Get cluster name from CCS */
+ ccs_h = ccs_force_connect(NULL, 0);
+ if (ccs_h < 0)
+ {
+ syslog(LOG_ERR, "Cannot login in to CCSD server\n");
+ return -1;
+ }
+
+ ccs_get(ccs_h, "//cluster/@name", &cluster_name);
+ DEBUGLOG("got cluster name %s\n", cluster_name);
+
+ if (!ccs_get(ccs_h, "//cluster/clvm/@port", &portstr))
+ {
+ port = atoi(portstr);
+ free(portstr);
+ DEBUGLOG("got port number %d\n", port);
+
+ if (port <= 0 && port >= 65536)
+ port = 0;
+ }
+
+ ccs_disconnect(ccs_h);
+
+ /* Block locking until we are logged in */
+ pthread_mutex_init(&lock_start_mutex, NULL);
+ pthread_mutex_lock(&lock_start_mutex);
+ lock_start_flag = 1;
+
+ node_hash = dm_hash_create(100);
+ lock_hash = dm_hash_create(10);
+
+ /* Get all nodes from CCS */
+ if (get_all_cluster_nodes())
+ return -1;
+
+ /* Initialise GULM library */
+ status = lg_initialize(&gulm_if, cluster_name, "clvmd");
+ if (status)
+ {
+ DEBUGLOG("lg_initialize failed: %d\n", status);
+ return status;
+ }
+
+ /* Connect to core - we are not "important" :-) */
+ status = lg_core_login(gulm_if, 0);
+ if (status)
+ {
+ DEBUGLOG("lg_core_login failed: %d\n", status);
+ return status;
+ }
+
+ /* Initialise the inter-node comms */
+ status = init_comms(port);
+ if (status)
+ return status;
+
+ /* Add core FD to the list */
+ status = add_internal_client(lg_core_selector(gulm_if), read_from_core_sock);
+ if (status)
+ {
+ DEBUGLOG("can't allocate client space\n");
+ return status;
+ }
+
+ /* Connect to the lock server */
+ if (lg_lock_login(gulm_if, "CLVM"))
+ {
+ syslog(LOG_ERR, "Cannot login in to LOCK server\n");
+ DEBUGLOG("Cannot login in to LOCK server\n");
+ exit(88);
+ }
+
+ /* Add lockspace FD to the list */
+ status = add_internal_client(lg_lock_selector(gulm_if), read_from_lock_sock);
+ if (status)
+ {
+ DEBUGLOG("can't allocate client space\n");
+ exit(status);
+ }
+
+ /* Request a list of nodes, we can't really do anything until
+ this comes back */
+ status = lg_core_nodelist(gulm_if);
+ if (status)
+ {
+ DEBUGLOG("lg_core_nodelist failed: %d\n", status);
+ return status;
+ }
+
+ /* So I can kill it without taking GULM down too */
+ signal(SIGINT, badsig_handler);
+ signal(SIGTERM, badsig_handler);
+
+ return 0;
+}
+
+static void _cluster_closedown(void)
+{
+ DEBUGLOG("cluster_closedown\n");
+ in_shutdown = 1;
+ destroy_lvhash();
+ lg_lock_logout(gulm_if);
+ lg_core_logout(gulm_if);
+ lg_release(gulm_if);
+}
+
+/* Expire locks for a named node, or us */
+#define GIO_KEY_SIZE 46
+static void drop_expired_locks(char *nodename)
+{
+ struct utsname nodeinfo;
+ uint8_t mask[GIO_KEY_SIZE];
+
+ DEBUGLOG("Dropping expired locks for %s\n", nodename?nodename:"(null)");
+ memset(mask, 0xff, GIO_KEY_SIZE);
+
+ if (!nodename)
+ {
+ uname(&nodeinfo);
+ nodename = nodeinfo.nodename;
+ }
+
+ if (lg_lock_drop_exp(gulm_if, nodename, mask, GIO_KEY_SIZE))
+ {
+ DEBUGLOG("Error calling lg_lock_drop_exp()\n");
+ }
+}
+
+
+static int read_from_core_sock(struct local_client *client, char *buf, int len, const char *csid,
+ struct local_client **new_client)
+{
+ int status;
+
+ *new_client = NULL;
+ status = lg_core_handle_messages(gulm_if, &core_callbacks, NULL);
+ return status<0 ? status : 1;
+}
+
+static int read_from_lock_sock(struct local_client *client, char *buf, int len, const char *csid,
+ struct local_client **new_client)
+{
+ int status;
+
+ *new_client = NULL;
+ status = lg_lock_handle_messages(gulm_if, &lock_callbacks, NULL);
+ return status<0 ? status : 1;
+}
+
+
+/* CORE callback routines */
+static int core_login_reply(void *misc, uint64_t gen, uint32_t error, uint32_t rank, uint8_t corestate)
+{
+ DEBUGLOG("CORE Got a Login reply. gen:%lld err:%d rank:%d corestate:%d\n",
+ gen, error, rank, corestate);
+
+ if (error)
+ exit(error);
+
+ /* Get the current core state (for quorum) */
+ lg_core_corestate(gulm_if);
+
+ return 0;
+}
+
+static void set_node_state(struct node_info *ninfo, char *csid, uint8_t nodestate)
+{
+ if (nodestate == lg_core_Logged_in)
+ {
+ /* Don't clobber NODE_CLVMD state */
+ if (ninfo->state != NODE_CLVMD)
+ {
+ if (ninfo->state == NODE_UNKNOWN ||
+ ninfo->state == NODE_DOWN)
+ num_nodes++;
+
+ ninfo->state = NODE_UP;
+ }
+ }
+ else
+ {
+ if (nodestate == lg_core_Expired ||
+ nodestate == lg_core_Fenced ||
+ nodestate == lg_core_Logged_out)
+ {
+ if (ninfo->state != NODE_DOWN)
+ num_nodes--;
+ ninfo->state = NODE_DOWN;
+ }
+ }
+ /* Gulm doesn't always send node DOWN events, so even if this a a node UP we must
+ * assume (ahem) that it prevously went down at some time. So we close
+ * the sockets here to make sure that we don't have any dead connections
+ * to that node.
+ */
+ tcp_remove_client(csid);
+
+ DEBUGLOG("set_node_state, '%s' state = %d num_nodes=%d\n",
+ ninfo->name, ninfo->state, num_nodes);
+}
+
+static struct node_info *add_or_set_node(char *name, struct in6_addr *ip, uint8_t state)
+{
+ struct node_info *ninfo;
+
+ ninfo = dm_hash_lookup_binary(node_hash, (char *)ip, GULM_MAX_CSID_LEN);
+ if (!ninfo)
+ {
+ /* If we can't find that node then re-read the config file in case it
+ was added after we were started */
+ DEBUGLOG("Node %s not found, re-reading config file\n", name);
+ get_all_cluster_nodes();
+
+ /* Now try again */
+ ninfo = dm_hash_lookup_binary(node_hash, (char *)ip, GULM_MAX_CSID_LEN);
+ if (!ninfo)
+ {
+ DEBUGLOG("Ignoring node %s, not part of the SAN cluster\n", name);
+ return NULL;
+ }
+ }
+
+ set_node_state(ninfo, (char *)ip, state);
+
+ return ninfo;
+}
+
+static void _get_our_csid(char *csid)
+{
+ get_our_gulm_csid(csid);
+}
+
+static int core_nodelist(void *misc, lglcb_t type, char *name, struct in6_addr *ip, uint8_t state)
+{
+ DEBUGLOG("CORE nodelist\n");
+
+ if (type == lglcb_start)
+ {
+ DEBUGLOG("Got Nodelist, start\n");
+ }
+ else
+ {
+ if (type == lglcb_item)
+ {
+ DEBUGLOG("Got nodelist, item: %s, %#x\n", name, state);
+
+ add_or_set_node(name, ip, state);
+ }
+ else
+ {
+ if (type == lglcb_stop)
+ {
+ char ourcsid[GULM_MAX_CSID_LEN];
+
+ DEBUGLOG("Got Nodelist, stop\n");
+ if (gulm_quorate)
+ {
+ clvmd_cluster_init_completed();
+ init_state = INIT_DONE;
+ }
+ else
+ {
+ if (init_state == INIT_NOTDONE)
+ init_state = INIT_WAITQUORATE;
+ }
+
+ /* Mark ourself as up */
+ _get_our_csid(ourcsid);
+ gulm_add_up_node(ourcsid);
+ }
+ else
+ {
+ DEBUGLOG("Unknown lglcb_t %#x\n", type);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int core_statechange(void *misc, uint8_t corestate, uint8_t quorate, struct in6_addr *masterip, char *mastername)
+{
+ DEBUGLOG("CORE Got statechange. quorate:%d, corestate:%x mastername:%s\n",
+ quorate, corestate, mastername);
+
+ gulm_quorate = quorate;
+ if (quorate && init_state == INIT_WAITQUORATE)
+ {
+ clvmd_cluster_init_completed();
+ init_state = INIT_DONE;
+ }
+ return 0;
+}
+
+static int core_nodechange(void *misc, char *nodename, struct in6_addr *nodeip, uint8_t nodestate)
+{
+ struct node_info *ninfo;
+
+ DEBUGLOG("CORE node change, name=%s, state = %d\n", nodename, nodestate);
+
+ /* If we don't get nodeip here, try a lookup by name */
+ if (!nodeip)
+ _csid_from_name((char *)nodeip, nodename);
+ if (!nodeip)
+ return 0;
+
+ ninfo = add_or_set_node(nodename, nodeip, nodestate);
+ if (!ninfo)
+ return 0;
+
+ /* Check if we need to drop any expired locks */
+ if (ninfo->state == NODE_DOWN)
+ {
+ drop_expired_locks(nodename);
+ }
+
+ return 0;
+}
+static int core_error(void *misc, uint32_t err)
+{
+ DEBUGLOG("CORE error: %d\n", err);
+ // Not sure what happens here
+ return 0;
+}
+
+/* LOCK callback routines */
+static int lock_login_reply(void *misc, uint32_t error, uint8_t which)
+{
+ DEBUGLOG("LOCK Got a Login reply. err:%d which:%d\n",
+ error, which);
+
+ if (error)
+ exit(error);
+
+ /* Drop any expired locks for us that might be hanging around */
+ drop_expired_locks(NULL);
+
+ /* Enable locking operations in other threads */
+ if (lock_start_flag)
+ {
+ lock_start_flag = 0;
+ pthread_mutex_unlock(&lock_start_mutex);
+ }
+
+ return 0;
+}
+
+static int lock_lock_state(void *misc, uint8_t *key, uint16_t keylen,
+ uint64_t subid, uint64_t start, uint64_t stop,
+ uint8_t state, uint32_t flags, uint32_t error,
+ uint8_t *LVB, uint16_t LVBlen)
+{
+ struct lock_wait *lwait;
+
+ DEBUGLOG("LOCK lock state: %s, error = %d\n", key, error);
+
+ /* No waiting process to wake up when we are shutting down */
+ if (in_shutdown)
+ return 0;
+
+ lwait = dm_hash_lookup(lock_hash, key);
+ if (!lwait)
+ {
+ DEBUGLOG("Can't find hash entry for resource %s\n", key);
+ return 0;
+ }
+ lwait->status = error;
+ pthread_mutex_lock(&lwait->mutex);
+ pthread_cond_signal(&lwait->cond);
+ pthread_mutex_unlock(&lwait->mutex);
+
+ return 0;
+}
+static int lock_error(void *misc, uint32_t err)
+{
+ DEBUGLOG("LOCK error: %d\n", err);
+ // Not sure what happens here
+ return 0;
+}
+
+
+/* CORE callbacks */
+static lg_core_callbacks_t core_callbacks = {
+ .login_reply = core_login_reply,
+ .nodelist = core_nodelist,
+ .statechange = core_statechange,
+ .nodechange = core_nodechange,
+ .error = core_error,
+};
+
+/* LOCK callbacks */
+static lg_lockspace_callbacks_t lock_callbacks = {
+ .login_reply = lock_login_reply,
+ .lock_state = lock_lock_state,
+ .error = lock_error,
+};
+
+/* Allow tcp-comms to loop round the list of active nodes */
+int get_next_node_csid(void **context, char *csid)
+{
+ struct node_info *ninfo = NULL;
+
+ /* First node */
+ if (!*context)
+ {
+ *context = dm_hash_get_first(node_hash);
+ }
+ else
+ {
+ *context = dm_hash_get_next(node_hash, *context);
+ }
+ if (*context)
+ ninfo = dm_hash_get_data(node_hash, *context);
+
+ /* Find a node that is UP */
+ while (*context && ninfo->state == NODE_DOWN)
+ {
+ *context = dm_hash_get_next(node_hash, *context);
+ if (*context)
+ {
+ ninfo = dm_hash_get_data(node_hash, *context);
+ }
+ }
+
+ if (!*context || ninfo->state == NODE_DOWN)
+ {
+ return 0;
+ }
+
+ memcpy(csid, dm_hash_get_key(node_hash, *context), GULM_MAX_CSID_LEN);
+ return 1;
+}
+
+int gulm_name_from_csid(const char *csid, char *name)
+{
+ struct node_info *ninfo;
+
+ ninfo = dm_hash_lookup_binary(node_hash, csid, GULM_MAX_CSID_LEN);
+ if (!ninfo)
+ {
+ sprintf(name, "UNKNOWN %s", print_csid(csid));
+ return -1;
+ }
+
+ strcpy(name, ninfo->name);
+ return 0;
+}
+
+
+static int _csid_from_name(char *csid, const char *name)
+{
+ struct dm_hash_node *hn;
+ struct node_info *ninfo;
+
+ dm_hash_iterate(hn, node_hash)
+ {
+ ninfo = dm_hash_get_data(node_hash, hn);
+ if (strcmp(ninfo->name, name) == 0)
+ {
+ memcpy(csid, dm_hash_get_key(node_hash, hn), GULM_MAX_CSID_LEN);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int _get_num_nodes()
+{
+ DEBUGLOG("num_nodes = %d\n", num_nodes);
+ return num_nodes;
+}
+
+/* Node is now known to be running a clvmd */
+void gulm_add_up_node(const char *csid)
+{
+ struct node_info *ninfo;
+
+ ninfo = dm_hash_lookup_binary(node_hash, csid, GULM_MAX_CSID_LEN);
+ if (!ninfo) {
+ DEBUGLOG("gulm_add_up_node no node_hash entry for csid %s\n", print_csid(csid));
+ return;
+ }
+
+ DEBUGLOG("gulm_add_up_node %s\n", ninfo->name);
+
+ if (ninfo->state == NODE_DOWN)
+ num_nodes++;
+ ninfo->state = NODE_CLVMD;
+
+ return;
+
+}
+/* Node is now known to be NOT running a clvmd */
+void add_down_node(char *csid)
+{
+ struct node_info *ninfo;
+
+ ninfo = dm_hash_lookup_binary(node_hash, csid, GULM_MAX_CSID_LEN);
+ if (!ninfo)
+ return;
+
+ /* Only set it to UP if it was previously known to be
+ running clvmd - gulm may set it DOWN quite soon */
+ if (ninfo->state == NODE_CLVMD)
+ ninfo->state = NODE_UP;
+ drop_expired_locks(ninfo->name);
+ return;
+
+}
+
+/* Call a callback for each node, so the caller knows whether it's up or down */
+static int _cluster_do_node_callback(struct local_client *master_client,
+ void (*callback)(struct local_client *, const char *csid, int node_up))
+{
+ struct dm_hash_node *hn;
+ struct node_info *ninfo;
+ int somedown = 0;
+
+ dm_hash_iterate(hn, node_hash)
+ {
+ char csid[GULM_MAX_CSID_LEN];
+ struct local_client *client;
+
+ ninfo = dm_hash_get_data(node_hash, hn);
+ memcpy(csid, dm_hash_get_key(node_hash, hn), GULM_MAX_CSID_LEN);
+
+ DEBUGLOG("down_callback. node %s, state = %d\n", ninfo->name, ninfo->state);
+
+ client = dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
+ if (!client)
+ {
+ /* If it's up but not connected, try to make contact */
+ if (ninfo->state == NODE_UP)
+ gulm_connect_csid(csid, &client);
+
+ client = dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
+
+ }
+ DEBUGLOG("down_callback2. node %s, state = %d\n", ninfo->name, ninfo->state);
+ if (ninfo->state != NODE_DOWN)
+ callback(master_client, csid, ninfo->state == NODE_CLVMD);
+
+ if (ninfo->state != NODE_CLVMD)
+ somedown = -1;
+ }
+ return somedown;
+}
+
+/* Convert gulm error codes to unix errno numbers */
+static int gulm_to_errno(int gulm_ret)
+{
+ switch (gulm_ret)
+ {
+ case lg_err_TryFailed:
+ case lg_err_AlreadyPend:
+ errno = EAGAIN;
+ break;
+
+ /* More?? */
+ default:
+ errno = EINVAL;
+ }
+
+ return gulm_ret ? -1 : 0;
+}
+
+/* Real locking */
+static int _lock_resource(char *resource, int mode, int flags, int *lockid)
+{
+ int status;
+ struct lock_wait lwait;
+
+ /* Wait until the lock module is ready */
+ if (lock_start_flag)
+ {
+ pthread_mutex_lock(&lock_start_mutex);
+ pthread_mutex_unlock(&lock_start_mutex);
+ }
+
+ pthread_cond_init(&lwait.cond, NULL);
+ pthread_mutex_init(&lwait.mutex, NULL);
+ pthread_mutex_lock(&lwait.mutex);
+
+ /* This needs to be converted from DLM/LVM2 value for GULM */
+ if (flags & LCKF_NOQUEUE) flags = lg_lock_flag_Try;
+
+ dm_hash_insert(lock_hash, resource, &lwait);
+ DEBUGLOG("lock_resource '%s', flags=%d, mode=%d\n", resource, flags, mode);
+
+ status = lg_lock_state_req(gulm_if, resource, strlen(resource)+1,
+ 0, 0, 0,
+ mode, flags, NULL, 0);
+ if (status)
+ {
+ DEBUGLOG("lg_lock_state returned %d\n", status);
+ return status;
+ }
+
+ /* Wait for it to complete */
+ pthread_cond_wait(&lwait.cond, &lwait.mutex);
+ pthread_mutex_unlock(&lwait.mutex);
+
+ dm_hash_remove(lock_hash, resource);
+ DEBUGLOG("lock-resource returning %d\n", lwait.status);
+
+ return gulm_to_errno(lwait.status);
+}
+
+
+static int _unlock_resource(char *resource, int lockid)
+{
+ int status;
+ struct lock_wait lwait;
+
+ pthread_cond_init(&lwait.cond, NULL);
+ pthread_mutex_init(&lwait.mutex, NULL);
+ pthread_mutex_lock(&lwait.mutex);
+
+ dm_hash_insert(lock_hash, resource, &lwait);
+
+ DEBUGLOG("unlock_resource %s\n", resource);
+ status = lg_lock_state_req(gulm_if, resource, strlen(resource)+1,
+ 0, 0, 0,
+ lg_lock_state_Unlock, 0, NULL, 0);
+
+ if (status)
+ {
+ DEBUGLOG("lg_lock_state(unlock) returned %d\n", status);
+ return status;
+ }
+
+ /* When we are shutting down, don't wait for unlocks
+ to be acknowledged, just do it. */
+ if (in_shutdown)
+ return status;
+
+ /* Wait for it to complete */
+
+ pthread_cond_wait(&lwait.cond, &lwait.mutex);
+ pthread_mutex_unlock(&lwait.mutex);
+
+ dm_hash_remove(lock_hash, resource);
+
+ return gulm_to_errno(lwait.status);
+}
+
+
+/* These two locking functions MUST be called in a seperate thread from
+ the clvmd main loop because they expect to be woken up by it.
+
+ These are abstractions around the real locking functions (above)
+ as we need to emulate the DLM's EX/PW/CW interaction with GULM using
+ two locks.
+ To aid unlocking, we store the lock mode in the lockid (as GULM
+ doesn't use this).
+*/
+static int _sync_lock(const char *resource, int mode, int flags, int *lockid)
+{
+ int status;
+ char lock1[strlen(resource)+3];
+ char lock2[strlen(resource)+3];
+
+ snprintf(lock1, sizeof(lock1), "%s-1", resource);
+ snprintf(lock2, sizeof(lock2), "%s-2", resource);
+
+ switch (mode)
+ {
+ case LCK_EXCL:
+ status = _lock_resource(lock1, lg_lock_state_Exclusive, flags, lockid);
+ if (status)
+ goto out;
+
+ /* If we can't get this lock too then bail out */
+ status = _lock_resource(lock2, lg_lock_state_Exclusive, LCK_NONBLOCK, lockid);
+ if (status == lg_err_TryFailed)
+ {
+ _unlock_resource(lock1, *lockid);
+ status = -1;
+ errno = EAGAIN;
+ }
+ break;
+
+ case LCK_PREAD:
+ case LCK_READ:
+ status = _lock_resource(lock1, lg_lock_state_Shared, flags, lockid);
+ if (status)
+ goto out;
+ status = _unlock_resource(lock2, *lockid);
+ break;
+
+ case LCK_WRITE:
+ status = _lock_resource(lock2, lg_lock_state_Exclusive, flags, lockid);
+ if (status)
+ goto out;
+ status = _unlock_resource(lock1, *lockid);
+ break;
+
+ default:
+ status = -1;
+ errno = EINVAL;
+ break;
+ }
+ out:
+ *lockid = mode;
+ return status;
+}
+
+static int _sync_unlock(const char *resource, int lockid)
+{
+ int status = 0;
+ char lock1[strlen(resource)+3];
+ char lock2[strlen(resource)+3];
+
+ snprintf(lock1, sizeof(lock1), "%s-1", resource);
+ snprintf(lock2, sizeof(lock2), "%s-2", resource);
+
+ /* The held lock mode is in the lock id */
+ assert(lockid == LCK_EXCL ||
+ lockid == LCK_READ ||
+ lockid == LCK_PREAD ||
+ lockid == LCK_WRITE);
+
+ status = _unlock_resource(lock1, lockid);
+ if (!status)
+ status = _unlock_resource(lock2, lockid);
+
+ return status;
+}
+
+static int _is_quorate()
+{
+ return gulm_quorate;
+}
+
+/* Get all the cluster node names & IPs from CCS and
+ add them to our node list so we know who to talk to.
+ Called when we start up and if we get sent SIGHUP.
+*/
+static int get_all_cluster_nodes()
+{
+ int ctree;
+ char *nodename;
+ int error;
+ int i;
+
+ /* Open the config file */
+ ctree = ccs_force_connect(NULL, 1);
+ if (ctree < 0)
+ {
+ log_error("Error connecting to CCS");
+ return -1;
+ }
+
+ for (i=1;;i++)
+ {
+ char nodekey[256];
+ char nodeip[GULM_MAX_CSID_LEN];
+ int clvmflag = 1;
+ char *clvmflagstr;
+ char key[256];
+
+ sprintf(nodekey, "//cluster/clusternodes/clusternode[%d]/@name", i);
+ error = ccs_get(ctree, nodekey, &nodename);
+ if (error)
+ break;
+
+ sprintf(key, "//cluster/clusternodes/clusternode[@name=\"%s\"]/clvm", nodename);
+ if (!ccs_get(ctree, key, &clvmflagstr))
+ {
+ clvmflag = atoi(clvmflagstr);
+ free(clvmflagstr);
+ }
+
+ DEBUGLOG("Got node %s from ccs(clvmflag = %d)\n", nodename, clvmflag);
+ if ((get_ip_address(nodename, nodeip) == 0) && clvmflag)
+ {
+ struct node_info *ninfo;
+
+ /* If it's not in the list, then add it */
+ ninfo = dm_hash_lookup_binary(node_hash, nodeip, GULM_MAX_CSID_LEN);
+ if (!ninfo)
+ {
+ ninfo = malloc(sizeof(struct node_info));
+ if (!ninfo)
+ {
+ syslog(LOG_ERR, "Cannot alloc memory for node info\n");
+ ccs_disconnect(ctree);
+ return -1;
+ }
+ strcpy(ninfo->name, nodename);
+
+ ninfo->state = NODE_DOWN;
+ dm_hash_insert_binary(node_hash, nodeip, GULM_MAX_CSID_LEN, ninfo);
+ }
+ }
+ else
+ {
+ if (!clvmflag) {
+ DEBUGLOG("node %s has clvm disabled\n", nodename);
+ }
+ else {
+ DEBUGLOG("Cannot resolve host name %s\n", nodename);
+ log_error("Cannot resolve host name %s\n", nodename);
+ }
+ }
+ free(nodename);
+ }
+
+ /* Finished with config file */
+ ccs_disconnect(ctree);
+
+ return 0;
+}
+
+static int _get_main_cluster_fd(void)
+{
+ return get_main_gulm_cluster_fd();
+}
+
+static int _cluster_fd_callback(struct local_client *fd, char *buf, int len, const char *csid, struct local_client **new_client)
+{
+ return cluster_fd_gulm_callback(fd, buf, len, csid, new_client);
+}
+
+static int _cluster_send_message(const void *buf, int msglen, const char *csid, const char *errtext)
+{
+ return gulm_cluster_send_message((char *)buf, msglen, csid, errtext);
+}
+
+static int _get_cluster_name(char *buf, int buflen)
+{
+ strncpy(buf, cluster_name, buflen);
+ return 0;
+}
+
+static struct cluster_ops _cluster_gulm_ops = {
+ .cluster_init_completed = NULL,
+ .cluster_send_message = _cluster_send_message,
+ .name_from_csid = gulm_name_from_csid,
+ .csid_from_name = _csid_from_name,
+ .get_num_nodes = _get_num_nodes,
+ .cluster_fd_callback = _cluster_fd_callback,
+ .get_main_cluster_fd = _get_main_cluster_fd,
+ .cluster_do_node_callback = _cluster_do_node_callback,
+ .is_quorate = _is_quorate,
+ .get_our_csid = _get_our_csid,
+ .add_up_node = gulm_add_up_node,
+ .reread_config = _reread_config,
+ .cluster_closedown = _cluster_closedown,
+ .get_cluster_name = _get_cluster_name,
+ .sync_lock = _sync_lock,
+ .sync_unlock = _sync_unlock,
+};
+
+struct cluster_ops *init_gulm_cluster(void)
+{
+ if (!_init_cluster())
+ return &_cluster_gulm_ops;
+ else
+ return NULL;
+}
--- /dev/null
+extern int get_next_node_csid(void **context, char *csid);
+extern void add_down_node(char *csid);
+extern int gulm_fd(void);
+extern int get_ip_address(const char *node, char *addr);
+extern void tcp_remove_client(const char *csid);
+extern int alloc_client(int fd, const char *csid, struct local_client **new_client);
+
+void gulm_add_up_node(const char *csid);
+int gulm_name_from_csid(const char *csid, char *name);
--- /dev/null
+/*
+ * Copyright (C) 2007-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This provides the interface between clvmd and OpenAIS as the cluster
+ * and lock manager.
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+#include <fcntl.h>
+#include <syslog.h>
+
+#include <openais/saAis.h>
+#include <openais/saLck.h>
+
+#include <corosync/corotypes.h>
+#include <corosync/cpg.h>
+
+#include "locking.h"
+#include "clvm.h"
+#include "clvmd-comms.h"
+#include "lvm-functions.h"
+#include "clvmd.h"
+
+/* Timeout value for several openais calls */
+#define TIMEOUT 10
+
+static void openais_cpg_deliver_callback (cpg_handle_t handle,
+ const struct cpg_name *groupName,
+ uint32_t nodeid,
+ uint32_t pid,
+ void *msg,
+ size_t msg_len);
+static void openais_cpg_confchg_callback(cpg_handle_t handle,
+ const struct cpg_name *groupName,
+ const struct cpg_address *member_list, size_t member_list_entries,
+ const struct cpg_address *left_list, size_t left_list_entries,
+ const struct cpg_address *joined_list, size_t joined_list_entries);
+
+static void _cluster_closedown(void);
+
+/* Hash list of nodes in the cluster */
+static struct dm_hash_table *node_hash;
+
+/* For associating lock IDs & resource handles */
+static struct dm_hash_table *lock_hash;
+
+/* Number of active nodes */
+static int num_nodes;
+static unsigned int our_nodeid;
+
+static struct local_client *cluster_client;
+
+/* OpenAIS handles */
+static cpg_handle_t cpg_handle;
+static SaLckHandleT lck_handle;
+
+static struct cpg_name cpg_group_name;
+
+/* Openais callback structs */
+cpg_callbacks_t openais_cpg_callbacks = {
+ .cpg_deliver_fn = openais_cpg_deliver_callback,
+ .cpg_confchg_fn = openais_cpg_confchg_callback,
+};
+
+struct node_info
+{
+ enum {NODE_UNKNOWN, NODE_DOWN, NODE_UP, NODE_CLVMD} state;
+ int nodeid;
+};
+
+struct lock_info
+{
+ SaLckResourceHandleT res_handle;
+ SaLckLockIdT lock_id;
+ SaNameT lock_name;
+};
+
+/* Set errno to something approximating the right value and return 0 or -1 */
+static int ais_to_errno(SaAisErrorT err)
+{
+ switch(err)
+ {
+ case SA_AIS_OK:
+ return 0;
+ case SA_AIS_ERR_LIBRARY:
+ errno = EINVAL;
+ break;
+ case SA_AIS_ERR_VERSION:
+ errno = EINVAL;
+ break;
+ case SA_AIS_ERR_INIT:
+ errno = EINVAL;
+ break;
+ case SA_AIS_ERR_TIMEOUT:
+ errno = ETIME;
+ break;
+ case SA_AIS_ERR_TRY_AGAIN:
+ errno = EAGAIN;
+ break;
+ case SA_AIS_ERR_INVALID_PARAM:
+ errno = EINVAL;
+ break;
+ case SA_AIS_ERR_NO_MEMORY:
+ errno = ENOMEM;
+ break;
+ case SA_AIS_ERR_BAD_HANDLE:
+ errno = EINVAL;
+ break;
+ case SA_AIS_ERR_BUSY:
+ errno = EBUSY;
+ break;
+ case SA_AIS_ERR_ACCESS:
+ errno = EPERM;
+ break;
+ case SA_AIS_ERR_NOT_EXIST:
+ errno = ENOENT;
+ break;
+ case SA_AIS_ERR_NAME_TOO_LONG:
+ errno = ENAMETOOLONG;
+ break;
+ case SA_AIS_ERR_EXIST:
+ errno = EEXIST;
+ break;
+ case SA_AIS_ERR_NO_SPACE:
+ errno = ENOSPC;
+ break;
+ case SA_AIS_ERR_INTERRUPT:
+ errno = EINTR;
+ break;
+ case SA_AIS_ERR_NAME_NOT_FOUND:
+ errno = ENOENT;
+ break;
+ case SA_AIS_ERR_NO_RESOURCES:
+ errno = ENOMEM;
+ break;
+ case SA_AIS_ERR_NOT_SUPPORTED:
+ errno = EOPNOTSUPP;
+ break;
+ case SA_AIS_ERR_BAD_OPERATION:
+ errno = EINVAL;
+ break;
+ case SA_AIS_ERR_FAILED_OPERATION:
+ errno = EIO;
+ break;
+ case SA_AIS_ERR_MESSAGE_ERROR:
+ errno = EIO;
+ break;
+ case SA_AIS_ERR_QUEUE_FULL:
+ errno = EXFULL;
+ break;
+ case SA_AIS_ERR_QUEUE_NOT_AVAILABLE:
+ errno = EINVAL;
+ break;
+ case SA_AIS_ERR_BAD_FLAGS:
+ errno = EINVAL;
+ break;
+ case SA_AIS_ERR_TOO_BIG:
+ errno = E2BIG;
+ break;
+ case SA_AIS_ERR_NO_SECTIONS:
+ errno = ENOMEM;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
+ return -1;
+}
+
+static char *print_openais_csid(const char *csid)
+{
+ static char buf[128];
+ int id;
+
+ memcpy(&id, csid, sizeof(int));
+ sprintf(buf, "%d", id);
+ return buf;
+}
+
+static int add_internal_client(int fd, fd_callback_t callback)
+{
+ struct local_client *client;
+
+ DEBUGLOG("Add_internal_client, fd = %d\n", fd);
+
+ client = malloc(sizeof(struct local_client));
+ if (!client)
+ {
+ DEBUGLOG("malloc failed\n");
+ return -1;
+ }
+
+ memset(client, 0, sizeof(struct local_client));
+ client->fd = fd;
+ client->type = CLUSTER_INTERNAL;
+ client->callback = callback;
+ add_client(client);
+
+ /* Set Close-on-exec */
+ fcntl(fd, F_SETFD, 1);
+
+ return 0;
+}
+
+static void openais_cpg_deliver_callback (cpg_handle_t handle,
+ const struct cpg_name *groupName,
+ uint32_t nodeid,
+ uint32_t pid,
+ void *msg,
+ size_t msg_len)
+{
+ int target_nodeid;
+
+ memcpy(&target_nodeid, msg, OPENAIS_CSID_LEN);
+
+ DEBUGLOG("%u got message from nodeid %d for %d. len %d\n",
+ our_nodeid, nodeid, target_nodeid, msg_len-4);
+
+ if (nodeid != our_nodeid)
+ if (target_nodeid == our_nodeid || target_nodeid == 0)
+ process_message(cluster_client, (char *)msg+OPENAIS_CSID_LEN,
+ msg_len-OPENAIS_CSID_LEN, (char*)&nodeid);
+}
+
+static void openais_cpg_confchg_callback(cpg_handle_t handle,
+ const struct cpg_name *groupName,
+ const struct cpg_address *member_list, size_t member_list_entries,
+ const struct cpg_address *left_list, size_t left_list_entries,
+ const struct cpg_address *joined_list, size_t joined_list_entries)
+{
+ int i;
+ struct node_info *ninfo;
+
+ DEBUGLOG("confchg callback. %d joined, %d left, %d members\n",
+ joined_list_entries, left_list_entries, member_list_entries);
+
+ for (i=0; i<joined_list_entries; i++) {
+ ninfo = dm_hash_lookup_binary(node_hash,
+ (char *)&joined_list[i].nodeid,
+ OPENAIS_CSID_LEN);
+ if (!ninfo) {
+ ninfo = malloc(sizeof(struct node_info));
+ if (!ninfo) {
+ break;
+ }
+ else {
+ ninfo->nodeid = joined_list[i].nodeid;
+ dm_hash_insert_binary(node_hash,
+ (char *)&ninfo->nodeid,
+ OPENAIS_CSID_LEN, ninfo);
+ }
+ }
+ ninfo->state = NODE_CLVMD;
+ }
+
+ for (i=0; i<left_list_entries; i++) {
+ ninfo = dm_hash_lookup_binary(node_hash,
+ (char *)&left_list[i].nodeid,
+ OPENAIS_CSID_LEN);
+ if (ninfo)
+ ninfo->state = NODE_DOWN;
+ }
+
+ for (i=0; i<member_list_entries; i++) {
+ if (member_list[i].nodeid == 0) continue;
+ ninfo = dm_hash_lookup_binary(node_hash,
+ (char *)&member_list[i].nodeid,
+ OPENAIS_CSID_LEN);
+ if (!ninfo) {
+ ninfo = malloc(sizeof(struct node_info));
+ if (!ninfo) {
+ break;
+ }
+ else {
+ ninfo->nodeid = member_list[i].nodeid;
+ dm_hash_insert_binary(node_hash,
+ (char *)&ninfo->nodeid,
+ OPENAIS_CSID_LEN, ninfo);
+ }
+ }
+ ninfo->state = NODE_CLVMD;
+ }
+
+ num_nodes = member_list_entries;
+}
+
+static int lck_dispatch(struct local_client *client, char *buf, int len,
+ const char *csid, struct local_client **new_client)
+{
+ *new_client = NULL;
+ saLckDispatch(lck_handle, SA_DISPATCH_ONE);
+ return 1;
+}
+
+static int _init_cluster(void)
+{
+ SaAisErrorT err;
+ SaVersionT ver = { 'B', 1, 1 };
+ int select_fd;
+
+ node_hash = dm_hash_create(100);
+ lock_hash = dm_hash_create(10);
+
+ err = cpg_initialize(&cpg_handle,
+ &openais_cpg_callbacks);
+ if (err != SA_AIS_OK) {
+ syslog(LOG_ERR, "Cannot initialise OpenAIS CPG service: %d",
+ err);
+ DEBUGLOG("Cannot initialise OpenAIS CPG service: %d", err);
+ return ais_to_errno(err);
+ }
+
+ err = saLckInitialize(&lck_handle,
+ NULL,
+ &ver);
+ if (err != SA_AIS_OK) {
+ cpg_initialize(&cpg_handle, &openais_cpg_callbacks);
+ syslog(LOG_ERR, "Cannot initialise OpenAIS lock service: %d",
+ err);
+ DEBUGLOG("Cannot initialise OpenAIS lock service: %d\n\n", err);
+ return ais_to_errno(err);
+ }
+
+ /* Connect to the clvmd group */
+ strcpy((char *)cpg_group_name.value, "clvmd");
+ cpg_group_name.length = strlen((char *)cpg_group_name.value);
+ err = cpg_join(cpg_handle, &cpg_group_name);
+ if (err != SA_AIS_OK) {
+ cpg_finalize(cpg_handle);
+ saLckFinalize(lck_handle);
+ syslog(LOG_ERR, "Cannot join clvmd process group");
+ DEBUGLOG("Cannot join clvmd process group: %d\n", err);
+ return ais_to_errno(err);
+ }
+
+ err = cpg_local_get(cpg_handle,
+ &our_nodeid);
+ if (err != SA_AIS_OK) {
+ cpg_finalize(cpg_handle);
+ saLckFinalize(lck_handle);
+ syslog(LOG_ERR, "Cannot get local node id\n");
+ return ais_to_errno(err);
+ }
+ DEBUGLOG("Our local node id is %d\n", our_nodeid);
+
+ saLckSelectionObjectGet(lck_handle, (SaSelectionObjectT *)&select_fd);
+ add_internal_client(select_fd, lck_dispatch);
+
+ DEBUGLOG("Connected to OpenAIS\n");
+
+ return 0;
+}
+
+static void _cluster_closedown(void)
+{
+ DEBUGLOG("cluster_closedown\n");
+ destroy_lvhash();
+
+ saLckFinalize(lck_handle);
+ cpg_finalize(cpg_handle);
+}
+
+static void _get_our_csid(char *csid)
+{
+ memcpy(csid, &our_nodeid, sizeof(int));
+}
+
+/* OpenAIS doesn't really have nmode names so we
+ just use the node ID in hex instead */
+static int _csid_from_name(char *csid, const char *name)
+{
+ int nodeid;
+ struct node_info *ninfo;
+
+ if (sscanf(name, "%x", &nodeid) == 1) {
+ ninfo = dm_hash_lookup_binary(node_hash, csid, OPENAIS_CSID_LEN);
+ if (ninfo)
+ return nodeid;
+ }
+ return -1;
+}
+
+static int _name_from_csid(const char *csid, char *name)
+{
+ struct node_info *ninfo;
+
+ ninfo = dm_hash_lookup_binary(node_hash, csid, OPENAIS_CSID_LEN);
+ if (!ninfo)
+ {
+ sprintf(name, "UNKNOWN %s", print_openais_csid(csid));
+ return -1;
+ }
+
+ sprintf(name, "%x", ninfo->nodeid);
+ return 0;
+}
+
+static int _get_num_nodes()
+{
+ DEBUGLOG("num_nodes = %d\n", num_nodes);
+ return num_nodes;
+}
+
+/* Node is now known to be running a clvmd */
+static void _add_up_node(const char *csid)
+{
+ struct node_info *ninfo;
+
+ ninfo = dm_hash_lookup_binary(node_hash, csid, OPENAIS_CSID_LEN);
+ if (!ninfo) {
+ DEBUGLOG("openais_add_up_node no node_hash entry for csid %s\n",
+ print_openais_csid(csid));
+ return;
+ }
+
+ DEBUGLOG("openais_add_up_node %d\n", ninfo->nodeid);
+
+ ninfo->state = NODE_CLVMD;
+
+ return;
+}
+
+/* Call a callback for each node, so the caller knows whether it's up or down */
+static int _cluster_do_node_callback(struct local_client *master_client,
+ void (*callback)(struct local_client *,
+ const char *csid, int node_up))
+{
+ struct dm_hash_node *hn;
+ struct node_info *ninfo;
+ int somedown = 0;
+
+ dm_hash_iterate(hn, node_hash)
+ {
+ char csid[OPENAIS_CSID_LEN];
+
+ ninfo = dm_hash_get_data(node_hash, hn);
+ memcpy(csid, dm_hash_get_key(node_hash, hn), OPENAIS_CSID_LEN);
+
+ DEBUGLOG("down_callback. node %d, state = %d\n", ninfo->nodeid,
+ ninfo->state);
+
+ if (ninfo->state != NODE_DOWN)
+ callback(master_client, csid, ninfo->state == NODE_CLVMD);
+ if (ninfo->state != NODE_CLVMD)
+ somedown = -1;
+ }
+ return somedown;
+}
+
+/* Real locking */
+static int _lock_resource(char *resource, int mode, int flags, int *lockid)
+{
+ struct lock_info *linfo;
+ SaLckResourceHandleT res_handle;
+ SaAisErrorT err;
+ SaLckLockIdT lock_id;
+ SaLckLockStatusT lockStatus;
+
+ /* This needs to be converted from DLM/LVM2 value for OpenAIS LCK */
+ if (flags & LCK_NONBLOCK) flags = SA_LCK_LOCK_NO_QUEUE;
+
+ linfo = malloc(sizeof(struct lock_info));
+ if (!linfo)
+ return -1;
+
+ DEBUGLOG("lock_resource '%s', flags=%d, mode=%d\n", resource, flags, mode);
+
+ linfo->lock_name.length = strlen(resource)+1;
+ strcpy((char *)linfo->lock_name.value, resource);
+
+ err = saLckResourceOpen(lck_handle, &linfo->lock_name,
+ SA_LCK_RESOURCE_CREATE, TIMEOUT, &res_handle);
+ if (err != SA_AIS_OK)
+ {
+ DEBUGLOG("ResourceOpen returned %d\n", err);
+ free(linfo);
+ return ais_to_errno(err);
+ }
+
+ err = saLckResourceLock(
+ res_handle,
+ &lock_id,
+ mode,
+ flags,
+ 0,
+ SA_TIME_END,
+ &lockStatus);
+ if (err != SA_AIS_OK && lockStatus != SA_LCK_LOCK_GRANTED)
+ {
+ free(linfo);
+ saLckResourceClose(res_handle);
+ return ais_to_errno(err);
+ }
+
+ /* Wait for it to complete */
+
+ DEBUGLOG("lock_resource returning %d, lock_id=%llx\n", err,
+ lock_id);
+
+ linfo->lock_id = lock_id;
+ linfo->res_handle = res_handle;
+
+ dm_hash_insert(lock_hash, resource, linfo);
+
+ return ais_to_errno(err);
+}
+
+
+static int _unlock_resource(char *resource, int lockid)
+{
+ SaAisErrorT err;
+ struct lock_info *linfo;
+
+ DEBUGLOG("unlock_resource %s\n", resource);
+ linfo = dm_hash_lookup(lock_hash, resource);
+ if (!linfo)
+ return 0;
+
+ DEBUGLOG("unlock_resource: lockid: %llx\n", linfo->lock_id);
+ err = saLckResourceUnlock(linfo->lock_id, SA_TIME_END);
+ if (err != SA_AIS_OK)
+ {
+ DEBUGLOG("Unlock returned %d\n", err);
+ return ais_to_errno(err);
+ }
+
+ /* Release the resource */
+ dm_hash_remove(lock_hash, resource);
+ saLckResourceClose(linfo->res_handle);
+ free(linfo);
+
+ return ais_to_errno(err);
+}
+
+static int _sync_lock(const char *resource, int mode, int flags, int *lockid)
+{
+ int status;
+ char lock1[strlen(resource)+3];
+ char lock2[strlen(resource)+3];
+
+ snprintf(lock1, sizeof(lock1), "%s-1", resource);
+ snprintf(lock2, sizeof(lock2), "%s-2", resource);
+
+ switch (mode)
+ {
+ case LCK_EXCL:
+ status = _lock_resource(lock1, SA_LCK_EX_LOCK_MODE, flags, lockid);
+ if (status)
+ goto out;
+
+ /* If we can't get this lock too then bail out */
+ status = _lock_resource(lock2, SA_LCK_EX_LOCK_MODE, LCK_NONBLOCK,
+ lockid);
+ if (status == SA_LCK_LOCK_NOT_QUEUED)
+ {
+ _unlock_resource(lock1, *lockid);
+ status = -1;
+ errno = EAGAIN;
+ }
+ break;
+
+ case LCK_PREAD:
+ case LCK_READ:
+ status = _lock_resource(lock1, SA_LCK_PR_LOCK_MODE, flags, lockid);
+ if (status)
+ goto out;
+ _unlock_resource(lock2, *lockid);
+ break;
+
+ case LCK_WRITE:
+ status = _lock_resource(lock2, SA_LCK_EX_LOCK_MODE, flags, lockid);
+ if (status)
+ goto out;
+ _unlock_resource(lock1, *lockid);
+ break;
+
+ default:
+ status = -1;
+ errno = EINVAL;
+ break;
+ }
+out:
+ *lockid = mode;
+ return status;
+}
+
+static int _sync_unlock(const char *resource, int lockid)
+{
+ int status = 0;
+ char lock1[strlen(resource)+3];
+ char lock2[strlen(resource)+3];
+
+ snprintf(lock1, sizeof(lock1), "%s-1", resource);
+ snprintf(lock2, sizeof(lock2), "%s-2", resource);
+
+ _unlock_resource(lock1, lockid);
+ _unlock_resource(lock2, lockid);
+
+ return status;
+}
+
+/* We are always quorate ! */
+static int _is_quorate()
+{
+ return 1;
+}
+
+static int _get_main_cluster_fd(void)
+{
+ int select_fd;
+
+ cpg_fd_get(cpg_handle, &select_fd);
+ return select_fd;
+}
+
+static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
+ const char *csid,
+ struct local_client **new_client)
+{
+ cluster_client = fd;
+ *new_client = NULL;
+ cpg_dispatch(cpg_handle, SA_DISPATCH_ONE);
+ return 1;
+}
+
+static int _cluster_send_message(const void *buf, int msglen, const char *csid,
+ const char *errtext)
+{
+ struct iovec iov[2];
+ SaAisErrorT err;
+ int target_node;
+
+ if (csid)
+ memcpy(&target_node, csid, OPENAIS_CSID_LEN);
+ else
+ target_node = 0;
+
+ iov[0].iov_base = &target_node;
+ iov[0].iov_len = sizeof(int);
+ iov[1].iov_base = (char *)buf;
+ iov[1].iov_len = msglen;
+
+ err = cpg_mcast_joined(cpg_handle, CPG_TYPE_AGREED, iov, 2);
+ return ais_to_errno(err);
+}
+
+/* We don't have a cluster name to report here */
+static int _get_cluster_name(char *buf, int buflen)
+{
+ strncpy(buf, "OpenAIS", buflen);
+ return 0;
+}
+
+static struct cluster_ops _cluster_openais_ops = {
+ .cluster_init_completed = NULL,
+ .cluster_send_message = _cluster_send_message,
+ .name_from_csid = _name_from_csid,
+ .csid_from_name = _csid_from_name,
+ .get_num_nodes = _get_num_nodes,
+ .cluster_fd_callback = _cluster_fd_callback,
+ .get_main_cluster_fd = _get_main_cluster_fd,
+ .cluster_do_node_callback = _cluster_do_node_callback,
+ .is_quorate = _is_quorate,
+ .get_our_csid = _get_our_csid,
+ .add_up_node = _add_up_node,
+ .reread_config = NULL,
+ .cluster_closedown = _cluster_closedown,
+ .get_cluster_name = _get_cluster_name,
+ .sync_lock = _sync_lock,
+ .sync_unlock = _sync_unlock,
+};
+
+struct cluster_ops *init_openais_cluster(void)
+{
+ if (!_init_cluster())
+ return &_cluster_openais_ops;
+ else
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+
+#include "locking.h"
+#include "clvm.h"
+#include "clvmd-comms.h"
+#include "lvm-functions.h"
+#include "clvmd.h"
+
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+
+static const char SINGLENODE_CLVMD_SOCKNAME[] = DEFAULT_RUN_DIR "/clvmd_singlenode.sock";
+static int listen_fd = -1;
+
+static void close_comms(void)
+{
+ if (listen_fd != -1 && close(listen_fd))
+ stack;
+ (void)unlink(SINGLENODE_CLVMD_SOCKNAME);
+ listen_fd = -1;
+}
+
+static int init_comms(void)
+{
+ struct sockaddr_un addr;
+ mode_t old_mask;
+
+ close_comms();
+
+ (void) dm_prepare_selinux_context(SINGLENODE_CLVMD_SOCKNAME, S_IFSOCK);
+ old_mask = umask(0077);
+
+ listen_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (listen_fd < 0) {
+ DEBUGLOG("Can't create local socket: %s\n", strerror(errno));
+ goto error;
+ }
+ /* Set Close-on-exec */
+ fcntl(listen_fd, F_SETFD, 1);
+
+ memset(&addr, 0, sizeof(addr));
+ memcpy(addr.sun_path, SINGLENODE_CLVMD_SOCKNAME,
+ sizeof(SINGLENODE_CLVMD_SOCKNAME));
+ addr.sun_family = AF_UNIX;
+
+ if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ DEBUGLOG("Can't bind local socket: %s\n", strerror(errno));
+ goto error;
+ }
+ if (listen(listen_fd, 10) < 0) {
+ DEBUGLOG("Can't listen local socket: %s\n", strerror(errno));
+ goto error;
+ }
+
+ umask(old_mask);
+ (void) dm_prepare_selinux_context(NULL, 0);
+ return 0;
+error:
+ umask(old_mask);
+ (void) dm_prepare_selinux_context(NULL, 0);
+ close_comms();
+ return -1;
+}
+
+static int _init_cluster(void)
+{
+ int r;
+
+ r = init_comms();
+ if (r)
+ return r;
+
+ DEBUGLOG("Single-node cluster initialised.\n");
+ return 0;
+}
+
+static void _cluster_closedown(void)
+{
+ close_comms();
+
+ DEBUGLOG("cluster_closedown\n");
+ destroy_lvhash();
+}
+
+static void _get_our_csid(char *csid)
+{
+ int nodeid = 1;
+ memcpy(csid, &nodeid, sizeof(int));
+}
+
+static int _csid_from_name(char *csid, const char *name)
+{
+ return 1;
+}
+
+static int _name_from_csid(const char *csid, char *name)
+{
+ sprintf(name, "SINGLENODE");
+ return 0;
+}
+
+static int _get_num_nodes(void)
+{
+ return 1;
+}
+
+/* Node is now known to be running a clvmd */
+static void _add_up_node(const char *csid)
+{
+}
+
+/* Call a callback for each node, so the caller knows whether it's up or down */
+static int _cluster_do_node_callback(struct local_client *master_client,
+ void (*callback)(struct local_client *,
+ const char *csid, int node_up))
+{
+ return 0;
+}
+
+int _lock_file(const char *file, uint32_t flags);
+
+static int *_locks = NULL;
+static char **_resources = NULL;
+static int _lock_max = 1;
+static pthread_mutex_t _lock_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Real locking */
+static int _lock_resource(const char *resource, int mode, int flags, int *lockid)
+{
+ int *_locks_1;
+ char **_resources_1;
+ int i, j;
+
+ DEBUGLOG("lock_resource '%s', flags=%d, mode=%d\n",
+ resource, flags, mode);
+
+ retry:
+ pthread_mutex_lock(&_lock_mutex);
+
+ /* look for an existing lock for this resource */
+ for (i = 1; i < _lock_max; ++i) {
+ if (!_resources[i])
+ break;
+ if (!strcmp(_resources[i], resource)) {
+ if ((_locks[i] & LCK_TYPE_MASK) == LCK_WRITE ||
+ (_locks[i] & LCK_TYPE_MASK) == LCK_EXCL) {
+ DEBUGLOG("%s already write/exclusively locked...\n", resource);
+ goto maybe_retry;
+ }
+ if ((mode & LCK_TYPE_MASK) == LCK_WRITE ||
+ (mode & LCK_TYPE_MASK) == LCK_EXCL) {
+ DEBUGLOG("%s already locked and WRITE/EXCL lock requested...\n",
+ resource);
+ goto maybe_retry;
+ }
+ }
+ }
+
+ if (i == _lock_max) { /* out of lock slots, extend */
+ _locks_1 = dm_realloc(_locks, 2 * _lock_max * sizeof(int));
+ if (!_locks_1)
+ return 1; /* fail */
+ _locks = _locks_1;
+ _resources_1 = dm_realloc(_resources, 2 * _lock_max * sizeof(char *));
+ if (!_resources_1) {
+ /* _locks may get realloc'd twice, but that should be safe */
+ return 1; /* fail */
+ }
+ _resources = _resources_1;
+ /* clear the new resource entries */
+ for (j = _lock_max; j < 2 * _lock_max; ++j)
+ _resources[j] = NULL;
+ _lock_max = 2 * _lock_max;
+ }
+
+ /* resource is not currently locked, grab it */
+
+ *lockid = i;
+ _locks[i] = mode;
+ _resources[i] = dm_strdup(resource);
+
+ DEBUGLOG("%s locked -> %d\n", resource, i);
+
+ pthread_mutex_unlock(&_lock_mutex);
+ return 0;
+ maybe_retry:
+ pthread_mutex_unlock(&_lock_mutex);
+ if (!(flags & LCK_NONBLOCK)) {
+ usleep(10000);
+ goto retry;
+ }
+
+ return 1; /* fail */
+}
+
+static int _unlock_resource(const char *resource, int lockid)
+{
+ DEBUGLOG("unlock_resource: %s lockid: %x\n", resource, lockid);
+ if(!_resources[lockid]) {
+ DEBUGLOG("(%s) %d not locked\n", resource, lockid);
+ return 1;
+ }
+ if(strcmp(_resources[lockid], resource)) {
+ DEBUGLOG("%d has wrong resource (requested %s, got %s)\n",
+ lockid, resource, _resources[lockid]);
+ return 1;
+ }
+
+ dm_free(_resources[lockid]);
+ _resources[lockid] = 0;
+ return 0;
+}
+
+static int _is_quorate(void)
+{
+ return 1;
+}
+
+static int _get_main_cluster_fd(void)
+{
+ return listen_fd;
+}
+
+static int _cluster_fd_callback(struct local_client *fd, char *buf, int len,
+ const char *csid,
+ struct local_client **new_client)
+{
+ return 1;
+}
+
+static int _cluster_send_message(const void *buf, int msglen,
+ const char *csid,
+ const char *errtext)
+{
+ return 0;
+}
+
+static int _get_cluster_name(char *buf, int buflen)
+{
+ strncpy(buf, "localcluster", buflen);
+ buf[buflen - 1] = 0;
+ return 0;
+}
+
+static struct cluster_ops _cluster_singlenode_ops = {
+ .cluster_init_completed = NULL,
+ .cluster_send_message = _cluster_send_message,
+ .name_from_csid = _name_from_csid,
+ .csid_from_name = _csid_from_name,
+ .get_num_nodes = _get_num_nodes,
+ .cluster_fd_callback = _cluster_fd_callback,
+ .get_main_cluster_fd = _get_main_cluster_fd,
+ .cluster_do_node_callback = _cluster_do_node_callback,
+ .is_quorate = _is_quorate,
+ .get_our_csid = _get_our_csid,
+ .add_up_node = _add_up_node,
+ .reread_config = NULL,
+ .cluster_closedown = _cluster_closedown,
+ .get_cluster_name = _get_cluster_name,
+ .sync_lock = _lock_resource,
+ .sync_unlock = _unlock_resource,
+};
+
+struct cluster_ops *init_singlenode_cluster(void)
+{
+ if (!_init_cluster())
+ return &_cluster_singlenode_ops;
+ else
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * CLVMD: Cluster LVM daemon
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+
+#include "clvmd-comms.h"
+#include "clvm.h"
+#include "clvmd.h"
+#include "lvm-functions.h"
+#include "lvm-version.h"
+#include "refresh_clvmd.h"
+
+#ifdef HAVE_COROSYNC_CONFDB_H
+#include <corosync/confdb.h>
+#endif
+
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stddef.h>
+#include <syslog.h>
+#include <sys/un.h>
+#include <sys/utsname.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define MAX_RETRIES 4
+
+#define ISLOCAL_CSID(c) (memcmp(c, our_csid, max_csid_len) == 0)
+
+/* Head of the fd list. Also contains
+ the cluster_socket details */
+static struct local_client local_client_head;
+
+static unsigned short global_xid = 0; /* Last transaction ID issued */
+
+struct cluster_ops *clops = NULL;
+
+static char our_csid[MAX_CSID_LEN];
+static unsigned max_csid_len;
+static unsigned max_cluster_message;
+static unsigned max_cluster_member_name_len;
+
+/* Structure of items on the LVM thread list */
+struct lvm_thread_cmd {
+ struct dm_list list;
+
+ struct local_client *client;
+ struct clvm_header *msg;
+ char csid[MAX_CSID_LEN];
+ int remote; /* Flag */
+ int msglen;
+ unsigned short xid;
+};
+
+struct lvm_startup_params {
+ int using_gulm;
+ char **argv;
+};
+
+debug_t debug;
+static pthread_t lvm_thread;
+static pthread_mutex_t lvm_thread_mutex;
+static pthread_cond_t lvm_thread_cond;
+static pthread_mutex_t lvm_start_mutex;
+static struct dm_list lvm_cmd_head;
+static volatile sig_atomic_t quit = 0;
+static volatile sig_atomic_t reread_config = 0;
+static int child_pipe[2];
+
+/* Reasons the daemon failed initialisation */
+#define DFAIL_INIT 1
+#define DFAIL_LOCAL_SOCK 2
+#define DFAIL_CLUSTER_IF 3
+#define DFAIL_MALLOC 4
+#define DFAIL_TIMEOUT 5
+#define SUCCESS 0
+
+typedef enum {IF_AUTO, IF_CMAN, IF_GULM, IF_OPENAIS, IF_COROSYNC, IF_SINGLENODE} if_type_t;
+
+typedef void *(lvm_pthread_fn_t)(void*);
+
+/* Prototypes for code further down */
+static void sigusr2_handler(int sig);
+static void sighup_handler(int sig);
+static void sigterm_handler(int sig);
+static void send_local_reply(struct local_client *client, int status,
+ int clientid);
+static void free_reply(struct local_client *client);
+static void send_version_message(void);
+static void *pre_and_post_thread(void *arg);
+static int send_message(void *buf, int msglen, const char *csid, int fd,
+ const char *errtext);
+static int read_from_local_sock(struct local_client *thisfd);
+static int process_local_command(struct clvm_header *msg, int msglen,
+ struct local_client *client,
+ unsigned short xid);
+static void process_remote_command(struct clvm_header *msg, int msglen, int fd,
+ const char *csid);
+static int process_reply(const struct clvm_header *msg, int msglen,
+ const char *csid);
+static int open_local_sock(void);
+static void close_local_sock(int local_socket);
+static int check_local_clvmd(void);
+static struct local_client *find_client(int clientid);
+static void main_loop(int local_sock, int cmd_timeout);
+static void be_daemon(int start_timeout);
+static int check_all_clvmds_running(struct local_client *client);
+static int local_rendezvous_callback(struct local_client *thisfd, char *buf,
+ int len, const char *csid,
+ struct local_client **new_client);
+static void lvm_thread_fn(void *) __attribute__ ((noreturn));
+static int add_to_lvmqueue(struct local_client *client, struct clvm_header *msg,
+ int msglen, const char *csid);
+static int distribute_command(struct local_client *thisfd);
+static void hton_clvm(struct clvm_header *hdr);
+static void ntoh_clvm(struct clvm_header *hdr);
+static void add_reply_to_list(struct local_client *client, int status,
+ const char *csid, const char *buf, int len);
+static if_type_t parse_cluster_interface(char *ifname);
+static if_type_t get_cluster_type(void);
+
+static void usage(const char *prog, FILE *file)
+{
+ fprintf(file, "Usage:\n"
+ "%s [Vhd]\n\n"
+ " -V Show version of clvmd\n"
+ " -h Show this help information\n"
+ " -d Set debug level\n"
+ " If starting clvmd then don't fork, run in the foreground\n"
+ " -R Tell all running clvmds in the cluster to reload their device cache\n"
+ " -S Restart clvmd, preserving exclusive locks\n"
+ " -C Sets debug level (from -d) on all clvmd instances clusterwide\n"
+ " -t<secs> Command timeout (default 60 seconds)\n"
+ " -T<secs> Startup timeout (default none)\n"
+ " -I<cmgr> Cluster manager (default: auto)\n"
+ " Available cluster managers: "
+#ifdef USE_COROSYNC
+ "corosync "
+#endif
+#ifdef USE_CMAN
+ "cman "
+#endif
+#ifdef USE_OPENAIS
+ "openais "
+#endif
+#ifdef USE_GULM
+ "gulm "
+#endif
+#ifdef USE_SINGLENODE
+ "singlenode "
+#endif
+ "\n", prog);
+}
+
+/* Called to signal the parent how well we got on during initialisation */
+static void child_init_signal(int status)
+{
+ if (child_pipe[1]) {
+ /* FIXME Use a proper wrapper around write */
+ if (write(child_pipe[1], &status, sizeof(status)) < 0)
+ log_sys_error("write", "child_pipe");
+ if (close(child_pipe[1]))
+ log_sys_error("close", "child_pipe");
+ }
+}
+
+static __attribute__((noreturn)) void child_init_signal_and_exit(int status)
+{
+ child_init_signal(status);
+ exit(status);
+}
+
+static void safe_close(int *fd)
+{
+ if (*fd >= 0) {
+ int to_close = *fd;
+ *fd = -1;
+ close(to_close);
+ }
+}
+
+void debuglog(const char *fmt, ...)
+{
+ time_t P;
+ va_list ap;
+ static int syslog_init = 0;
+
+ if (debug == DEBUG_STDERR) {
+ va_start(ap,fmt);
+ time(&P);
+ fprintf(stderr, "CLVMD[%x]: %.15s ", (int)pthread_self(), ctime(&P)+4 );
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+ if (debug == DEBUG_SYSLOG) {
+ if (!syslog_init) {
+ openlog("clvmd", LOG_PID, LOG_DAEMON);
+ syslog_init = 1;
+ }
+
+ va_start(ap,fmt);
+ vsyslog(LOG_DEBUG, fmt, ap);
+ va_end(ap);
+ }
+}
+
+static const char *decode_cmd(unsigned char cmdl)
+{
+ static char buf[128];
+ const char *command;
+
+ switch (cmdl) {
+ case CLVMD_CMD_TEST:
+ command = "TEST";
+ break;
+ case CLVMD_CMD_LOCK_VG:
+ command = "LOCK_VG";
+ break;
+ case CLVMD_CMD_LOCK_LV:
+ command = "LOCK_LV";
+ break;
+ case CLVMD_CMD_REFRESH:
+ command = "REFRESH";
+ break;
+ case CLVMD_CMD_SET_DEBUG:
+ command = "SET_DEBUG";
+ break;
+ case CLVMD_CMD_GET_CLUSTERNAME:
+ command = "GET_CLUSTERNAME";
+ break;
+ case CLVMD_CMD_VG_BACKUP:
+ command = "VG_BACKUP";
+ break;
+ case CLVMD_CMD_REPLY:
+ command = "REPLY";
+ break;
+ case CLVMD_CMD_VERSION:
+ command = "VERSION";
+ break;
+ case CLVMD_CMD_GOAWAY:
+ command = "GOAWAY";
+ break;
+ case CLVMD_CMD_LOCK:
+ command = "LOCK";
+ break;
+ case CLVMD_CMD_UNLOCK:
+ command = "UNLOCK";
+ break;
+ case CLVMD_CMD_LOCK_QUERY:
+ command = "LOCK_QUERY";
+ break;
+ case CLVMD_CMD_RESTART:
+ command = "RESTART";
+ break;
+ default:
+ command = "unknown";
+ break;
+ }
+
+ sprintf(buf, "%s (0x%x)", command, cmdl);
+
+ return buf;
+}
+
+static void remove_lockfile(void)
+{
+ if (unlink(CLVMD_PIDFILE))
+ log_sys_error("unlink", CLVMD_PIDFILE);
+}
+
+/*
+ * clvmd require dm-ioctl capability for operation
+ */
+static void check_permissions(void)
+{
+ if (getuid() || geteuid()) {
+ log_error("Cannot run as a non-root user.");
+
+ /*
+ * Fail cleanly here if not run as root, instead of failing
+ * later when attempting a root-only operation
+ * Preferred exit code from an initscript for this.
+ */
+ exit(4);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int local_sock;
+ struct local_client *newfd;
+ struct utsname nodeinfo;
+ struct lvm_startup_params lvm_params;
+ signed char opt;
+ int cmd_timeout = DEFAULT_CMD_TIMEOUT;
+ int start_timeout = 0;
+ if_type_t cluster_iface = IF_AUTO;
+ sigset_t ss;
+ int using_gulm = 0;
+ int debug_opt = 0;
+ int clusterwide_opt = 0;
+ mode_t old_mask;
+
+ /* Deal with command-line arguments */
+ opterr = 0;
+ optind = 0;
+ while ((opt = getopt(argc, argv, "?vVhd::t:RST:CI:E:")) != EOF) {
+ switch (opt) {
+ case 'h':
+ usage(argv[0], stdout);
+ exit(0);
+
+ case '?':
+ usage(argv[0], stderr);
+ exit(0);
+
+ case 'R':
+ check_permissions();
+ return refresh_clvmd(1)==1?0:1;
+
+ case 'S':
+ check_permissions();
+ return restart_clvmd(clusterwide_opt)==1?0:1;
+
+ case 'C':
+ clusterwide_opt = 1;
+ break;
+
+ case 'd':
+ debug_opt = 1;
+ if (optarg)
+ debug = atoi(optarg);
+ else
+ debug = DEBUG_STDERR;
+ break;
+
+ case 't':
+ cmd_timeout = atoi(optarg);
+ if (!cmd_timeout) {
+ fprintf(stderr, "command timeout is invalid\n");
+ usage(argv[0], stderr);
+ exit(1);
+ }
+ break;
+ case 'I':
+ cluster_iface = parse_cluster_interface(optarg);
+ break;
+ case 'T':
+ start_timeout = atoi(optarg);
+ if (start_timeout <= 0) {
+ fprintf(stderr, "startup timeout is invalid\n");
+ usage(argv[0], stderr);
+ exit(1);
+ }
+ break;
+
+ case 'V':
+ printf("Cluster LVM daemon version: %s\n", LVM_VERSION);
+ printf("Protocol version: %d.%d.%d\n",
+ CLVMD_MAJOR_VERSION, CLVMD_MINOR_VERSION,
+ CLVMD_PATCH_VERSION);
+ exit(0);
+ break;
+
+ }
+ }
+
+ check_permissions();
+
+ /* Setting debug options on an existing clvmd */
+ if (debug_opt && !check_local_clvmd()) {
+
+ /* Sending to stderr makes no sense for a detached daemon */
+ if (debug == DEBUG_STDERR)
+ debug = DEBUG_SYSLOG;
+ return debug_clvmd(debug, clusterwide_opt)==1?0:1;
+ }
+
+ /*
+ * Switch to C locale to avoid reading large locale-archive file
+ * used by some glibc (on some distributions it takes over 100MB).
+ * Daemon currently needs to use mlockall().
+ */
+ if (setenv("LANG", "C", 1))
+ perror("Cannot set LANG to C");
+
+ /* Fork into the background (unless requested not to) */
+ if (debug != DEBUG_STDERR) {
+ be_daemon(start_timeout);
+ }
+
+ dm_prepare_selinux_context(DEFAULT_RUN_DIR, S_IFDIR);
+ old_mask = umask(0077);
+ if (dm_create_dir(DEFAULT_RUN_DIR) == 0) {
+ DEBUGLOG("clvmd: unable to create %s directory\n",
+ DEFAULT_RUN_DIR);
+ umask(old_mask);
+ exit(1);
+ }
+ umask(old_mask);
+
+ /* Create pidfile */
+ (void) dm_prepare_selinux_context(CLVMD_PIDFILE, S_IFREG);
+ if (dm_create_lockfile(CLVMD_PIDFILE) == 0) {
+ DEBUGLOG("clvmd: unable to create lockfile\n");
+ exit(1);
+ }
+ (void) dm_prepare_selinux_context(NULL, 0);
+
+ atexit(remove_lockfile);
+
+ DEBUGLOG("CLVMD started\n");
+
+ /* Open the Unix socket we listen for commands on.
+ We do this before opening the cluster socket so that
+ potential clients will block rather than error if we are running
+ but the cluster is not ready yet */
+ local_sock = open_local_sock();
+ if (local_sock < 0) {
+ child_init_signal_and_exit(DFAIL_LOCAL_SOCK);
+ /* NOTREACHED */
+ }
+
+ /* Set up signal handlers, USR1 is for cluster change notifications (in cman)
+ USR2 causes child threads to exit.
+ HUP causes gulm version to re-read nodes list from CCS.
+ PIPE should be ignored */
+ signal(SIGUSR2, sigusr2_handler);
+ signal(SIGHUP, sighup_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ /* Block SIGUSR2/SIGINT/SIGTERM in process */
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGUSR2);
+ sigaddset(&ss, SIGINT);
+ sigaddset(&ss, SIGTERM);
+ sigprocmask(SIG_BLOCK, &ss, NULL);
+
+ /* Initialise the LVM thread variables */
+ dm_list_init(&lvm_cmd_head);
+ pthread_mutex_init(&lvm_thread_mutex, NULL);
+ pthread_cond_init(&lvm_thread_cond, NULL);
+ pthread_mutex_init(&lvm_start_mutex, NULL);
+ init_lvhash();
+
+ /* Start the cluster interface */
+ if (cluster_iface == IF_AUTO)
+ cluster_iface = get_cluster_type();
+
+#ifdef USE_CMAN
+ if ((cluster_iface == IF_AUTO || cluster_iface == IF_CMAN) && (clops = init_cman_cluster())) {
+ max_csid_len = CMAN_MAX_CSID_LEN;
+ max_cluster_message = CMAN_MAX_CLUSTER_MESSAGE;
+ max_cluster_member_name_len = CMAN_MAX_NODENAME_LEN;
+ syslog(LOG_NOTICE, "Cluster LVM daemon started - connected to CMAN");
+ }
+#endif
+#ifdef USE_GULM
+ if (!clops)
+ if ((cluster_iface == IF_AUTO || cluster_iface == IF_GULM) && (clops = init_gulm_cluster())) {
+ max_csid_len = GULM_MAX_CSID_LEN;
+ max_cluster_message = GULM_MAX_CLUSTER_MESSAGE;
+ max_cluster_member_name_len = GULM_MAX_CLUSTER_MEMBER_NAME_LEN;
+ using_gulm = 1;
+ syslog(LOG_NOTICE, "Cluster LVM daemon started - connected to GULM");
+ }
+#endif
+#ifdef USE_COROSYNC
+ if (!clops)
+ if (((cluster_iface == IF_AUTO || cluster_iface == IF_COROSYNC) && (clops = init_corosync_cluster()))) {
+ max_csid_len = COROSYNC_CSID_LEN;
+ max_cluster_message = COROSYNC_MAX_CLUSTER_MESSAGE;
+ max_cluster_member_name_len = COROSYNC_MAX_CLUSTER_MEMBER_NAME_LEN;
+ syslog(LOG_NOTICE, "Cluster LVM daemon started - connected to Corosync");
+ }
+#endif
+#ifdef USE_OPENAIS
+ if (!clops)
+ if ((cluster_iface == IF_AUTO || cluster_iface == IF_OPENAIS) && (clops = init_openais_cluster())) {
+ max_csid_len = OPENAIS_CSID_LEN;
+ max_cluster_message = OPENAIS_MAX_CLUSTER_MESSAGE;
+ max_cluster_member_name_len = OPENAIS_MAX_CLUSTER_MEMBER_NAME_LEN;
+ syslog(LOG_NOTICE, "Cluster LVM daemon started - connected to OpenAIS");
+ }
+#endif
+#ifdef USE_SINGLENODE
+ if (!clops)
+ if (cluster_iface == IF_SINGLENODE && (clops = init_singlenode_cluster())) {
+ max_csid_len = SINGLENODE_CSID_LEN;
+ max_cluster_message = SINGLENODE_MAX_CLUSTER_MESSAGE;
+ max_cluster_member_name_len = MAX_CLUSTER_MEMBER_NAME_LEN;
+ syslog(LOG_NOTICE, "Cluster LVM daemon started - running in single-node mode");
+ }
+#endif
+
+ if (!clops) {
+ DEBUGLOG("Can't initialise cluster interface\n");
+ log_error("Can't initialise cluster interface\n");
+ child_init_signal_and_exit(DFAIL_CLUSTER_IF);
+ /* NOTREACHED */
+ }
+ DEBUGLOG("Cluster ready, doing some more initialisation\n");
+
+ /* Save our CSID */
+ uname(&nodeinfo);
+ clops->get_our_csid(our_csid);
+
+ /* Initialise the FD list head */
+ local_client_head.fd = clops->get_main_cluster_fd();
+ local_client_head.type = CLUSTER_MAIN_SOCK;
+ local_client_head.callback = clops->cluster_fd_callback;
+
+ /* Add the local socket to the list */
+ newfd = malloc(sizeof(struct local_client));
+ if (!newfd) {
+ child_init_signal_and_exit(DFAIL_MALLOC);
+ /* NOTREACHED */
+ }
+
+ newfd->fd = local_sock;
+ newfd->removeme = 0;
+ newfd->type = LOCAL_RENDEZVOUS;
+ newfd->callback = local_rendezvous_callback;
+ newfd->next = local_client_head.next;
+ local_client_head.next = newfd;
+
+ /* This needs to be started after cluster initialisation
+ as it may need to take out locks */
+ DEBUGLOG("starting LVM thread\n");
+
+ /* Don't let anyone else to do work until we are started */
+ pthread_mutex_lock(&lvm_start_mutex);
+ lvm_params.using_gulm = using_gulm;
+ lvm_params.argv = argv;
+ pthread_create(&lvm_thread, NULL, (lvm_pthread_fn_t*)lvm_thread_fn,
+ (void *)&lvm_params);
+
+ /* Tell the rest of the cluster our version number */
+ /* CMAN can do this immediately, gulm needs to wait until
+ the core initialisation has finished and the node list
+ has been gathered */
+ if (clops->cluster_init_completed)
+ clops->cluster_init_completed();
+
+ DEBUGLOG("clvmd ready for work\n");
+ child_init_signal(SUCCESS);
+
+ /* Try to shutdown neatly */
+ signal(SIGTERM, sigterm_handler);
+ signal(SIGINT, sigterm_handler);
+
+ /* Do some work */
+ main_loop(local_sock, cmd_timeout);
+
+ close_local_sock(local_sock);
+ destroy_lvm();
+
+ return 0;
+}
+
+/* Called when the GuLM cluster layer has completed initialisation.
+ We send the version message */
+void clvmd_cluster_init_completed()
+{
+ send_version_message();
+}
+
+/* Data on a connected socket */
+static int local_sock_callback(struct local_client *thisfd, char *buf, int len,
+ const char *csid,
+ struct local_client **new_client)
+{
+ *new_client = NULL;
+ return read_from_local_sock(thisfd);
+}
+
+/* Data on a connected socket */
+static int local_rendezvous_callback(struct local_client *thisfd, char *buf,
+ int len, const char *csid,
+ struct local_client **new_client)
+{
+ /* Someone connected to our local socket, accept it. */
+
+ struct sockaddr_un socka;
+ struct local_client *newfd;
+ socklen_t sl = sizeof(socka);
+ int client_fd = accept(thisfd->fd, (struct sockaddr *) &socka, &sl);
+
+ if (client_fd == -1 && errno == EINTR)
+ return 1;
+
+ if (client_fd >= 0) {
+ newfd = malloc(sizeof(struct local_client));
+ if (!newfd) {
+ close(client_fd);
+ return 1;
+ }
+
+ if (fcntl(client_fd, F_SETFD, 1))
+ DEBUGLOG("setting CLOEXEC on client fd failed: %s\n", strerror(errno));
+
+ newfd->fd = client_fd;
+ newfd->type = LOCAL_SOCK;
+ newfd->xid = 0;
+ newfd->removeme = 0;
+ newfd->callback = local_sock_callback;
+ newfd->bits.localsock.replies = NULL;
+ newfd->bits.localsock.expected_replies = 0;
+ newfd->bits.localsock.cmd = NULL;
+ newfd->bits.localsock.in_progress = FALSE;
+ newfd->bits.localsock.sent_out = FALSE;
+ newfd->bits.localsock.threadid = 0;
+ newfd->bits.localsock.finished = 0;
+ newfd->bits.localsock.pipe_client = NULL;
+ newfd->bits.localsock.private = NULL;
+ newfd->bits.localsock.all_success = 1;
+ DEBUGLOG("Got new connection on fd %d\n", newfd->fd);
+ *new_client = newfd;
+ }
+ return 1;
+}
+
+static int local_pipe_callback(struct local_client *thisfd, char *buf,
+ int maxlen, const char *csid,
+ struct local_client **new_client)
+{
+ int len;
+ char buffer[PIPE_BUF];
+ struct local_client *sock_client = thisfd->bits.pipe.client;
+ int status = -1; /* in error by default */
+
+ len = read(thisfd->fd, buffer, sizeof(int));
+ if (len == -1 && errno == EINTR)
+ return 1;
+
+ if (len == sizeof(int)) {
+ memcpy(&status, buffer, sizeof(int));
+ }
+
+ DEBUGLOG("read on PIPE %d: %d bytes: status: %d\n",
+ thisfd->fd, len, status);
+
+ /* EOF on pipe or an error, close it */
+ if (len <= 0) {
+ int jstat;
+ void *ret = &status;
+ close(thisfd->fd);
+
+ /* Clear out the cross-link */
+ if (thisfd->bits.pipe.client != NULL)
+ thisfd->bits.pipe.client->bits.localsock.pipe_client =
+ NULL;
+
+ /* Reap child thread */
+ if (thisfd->bits.pipe.threadid) {
+ jstat = pthread_join(thisfd->bits.pipe.threadid, &ret);
+ thisfd->bits.pipe.threadid = 0;
+ if (thisfd->bits.pipe.client != NULL)
+ thisfd->bits.pipe.client->bits.localsock.
+ threadid = 0;
+ }
+ return -1;
+ } else {
+ DEBUGLOG("background routine status was %d, sock_client=%p\n",
+ status, sock_client);
+ /* But has the client gone away ?? */
+ if (sock_client == NULL) {
+ DEBUGLOG
+ ("Got PIPE response for dead client, ignoring it\n");
+ } else {
+ /* If error then just return that code */
+ if (status)
+ send_local_reply(sock_client, status,
+ sock_client->fd);
+ else {
+ if (sock_client->bits.localsock.state ==
+ POST_COMMAND) {
+ send_local_reply(sock_client, 0,
+ sock_client->fd);
+ } else // PRE_COMMAND finished.
+ {
+ if (
+ (status =
+ distribute_command(sock_client)) !=
+ 0) send_local_reply(sock_client,
+ EFBIG,
+ sock_client->
+ fd);
+ }
+ }
+ }
+ }
+ return len;
+}
+
+/* If a noed is up, look for it in the reply array, if it's not there then
+ add one with "ETIMEDOUT".
+ NOTE: This won't race with real replies because they happen in the same thread.
+*/
+static void timedout_callback(struct local_client *client, const char *csid,
+ int node_up)
+{
+ if (node_up) {
+ struct node_reply *reply;
+ char nodename[max_cluster_member_name_len];
+
+ clops->name_from_csid(csid, nodename);
+ DEBUGLOG("Checking for a reply from %s\n", nodename);
+ pthread_mutex_lock(&client->bits.localsock.reply_mutex);
+
+ reply = client->bits.localsock.replies;
+ while (reply && strcmp(reply->node, nodename) != 0) {
+ reply = reply->next;
+ }
+
+ pthread_mutex_unlock(&client->bits.localsock.reply_mutex);
+
+ if (!reply) {
+ DEBUGLOG("Node %s timed-out\n", nodename);
+ add_reply_to_list(client, ETIMEDOUT, csid,
+ "Command timed out", 18);
+ }
+ }
+}
+
+/* Called when the request has timed out on at least one node. We fill in
+ the remaining node entries with ETIMEDOUT and return.
+
+ By the time we get here the node that caused
+ the timeout could have gone down, in which case we will never get the expected
+ number of replies that triggers the post command so we need to do it here
+*/
+static void request_timed_out(struct local_client *client)
+{
+ DEBUGLOG("Request timed-out. padding\n");
+ clops->cluster_do_node_callback(client, timedout_callback);
+
+ if (client->bits.localsock.num_replies !=
+ client->bits.localsock.expected_replies) {
+ /* Post-process the command */
+ if (client->bits.localsock.threadid) {
+ pthread_mutex_lock(&client->bits.localsock.mutex);
+ client->bits.localsock.state = POST_COMMAND;
+ pthread_cond_signal(&client->bits.localsock.cond);
+ pthread_mutex_unlock(&client->bits.localsock.mutex);
+ }
+ }
+}
+
+/* This is where the real work happens */
+static void main_loop(int local_sock, int cmd_timeout)
+{
+ DEBUGLOG("Using timeout of %d seconds\n", cmd_timeout);
+
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGINT);
+ sigaddset(&ss, SIGTERM);
+ pthread_sigmask(SIG_UNBLOCK, &ss, NULL);
+ /* Main loop */
+ while (!quit) {
+ fd_set in;
+ int select_status;
+ struct local_client *thisfd;
+ struct timeval tv = { cmd_timeout, 0 };
+ int quorate = clops->is_quorate();
+
+ /* Wait on the cluster FD and all local sockets/pipes */
+ local_client_head.fd = clops->get_main_cluster_fd();
+ FD_ZERO(&in);
+ for (thisfd = &local_client_head; thisfd != NULL;
+ thisfd = thisfd->next) {
+
+ if (thisfd->removeme)
+ continue;
+
+ /* if the cluster is not quorate then don't listen for new requests */
+ if ((thisfd->type != LOCAL_RENDEZVOUS &&
+ thisfd->type != LOCAL_SOCK) || quorate)
+ FD_SET(thisfd->fd, &in);
+ }
+
+ select_status = select(FD_SETSIZE, &in, NULL, NULL, &tv);
+
+ if (reread_config) {
+ int saved_errno = errno;
+
+ reread_config = 0;
+ if (clops->reread_config)
+ clops->reread_config();
+ errno = saved_errno;
+ }
+
+ if (select_status > 0) {
+ struct local_client *lastfd = NULL;
+ char csid[MAX_CSID_LEN];
+ char buf[max_cluster_message];
+
+ for (thisfd = &local_client_head; thisfd != NULL;
+ thisfd = thisfd->next) {
+
+ if (thisfd->removeme) {
+ struct local_client *free_fd;
+ lastfd->next = thisfd->next;
+ free_fd = thisfd;
+ thisfd = lastfd;
+
+ DEBUGLOG("removeme set for fd %d\n", free_fd->fd);
+
+ /* Queue cleanup, this also frees the client struct */
+ add_to_lvmqueue(free_fd, NULL, 0, NULL);
+ break;
+ }
+
+ if (FD_ISSET(thisfd->fd, &in)) {
+ struct local_client *newfd = NULL;
+ int ret;
+
+ /* Do callback */
+ ret =
+ thisfd->callback(thisfd, buf,
+ sizeof(buf), csid,
+ &newfd);
+ /* Ignore EAGAIN */
+ if (ret < 0 && (errno == EAGAIN ||
+ errno == EINTR)) continue;
+
+ /* Got error or EOF: Remove it from the list safely */
+ if (ret <= 0) {
+ struct local_client *free_fd;
+ int type = thisfd->type;
+
+ /* If the cluster socket shuts down, so do we */
+ if (type == CLUSTER_MAIN_SOCK ||
+ type == CLUSTER_INTERNAL)
+ goto closedown;
+
+ DEBUGLOG("ret == %d, errno = %d. removing client\n",
+ ret, errno);
+ lastfd->next = thisfd->next;
+ free_fd = thisfd;
+ thisfd = lastfd;
+ safe_close(&(free_fd->fd));
+
+ /* Queue cleanup, this also frees the client struct */
+ add_to_lvmqueue(free_fd, NULL, 0, NULL);
+ break;
+ }
+
+ /* New client...simply add it to the list */
+ if (newfd) {
+ newfd->next = thisfd->next;
+ thisfd->next = newfd;
+ break;
+ }
+ }
+ lastfd = thisfd;
+ }
+ }
+
+ /* Select timed out. Check for clients that have been waiting too long for a response */
+ if (select_status == 0) {
+ time_t the_time = time(NULL);
+
+ for (thisfd = &local_client_head; thisfd != NULL;
+ thisfd = thisfd->next) {
+ if (thisfd->type == LOCAL_SOCK
+ && thisfd->bits.localsock.sent_out
+ && thisfd->bits.localsock.sent_time +
+ cmd_timeout < the_time
+ && thisfd->bits.localsock.
+ expected_replies !=
+ thisfd->bits.localsock.num_replies) {
+ /* Send timed out message + replies we already have */
+ DEBUGLOG
+ ("Request timed-out (send: %ld, now: %ld)\n",
+ thisfd->bits.localsock.sent_time,
+ the_time);
+
+ thisfd->bits.localsock.all_success = 0;
+
+ request_timed_out(thisfd);
+ }
+ }
+ }
+ if (select_status < 0) {
+ if (errno == EINTR)
+ continue;
+
+#ifdef DEBUG
+ perror("select error");
+ exit(-1);
+#endif
+ }
+ }
+
+ closedown:
+ clops->cluster_closedown();
+}
+
+static __attribute__ ((noreturn)) void wait_for_child(int c_pipe, int timeout)
+{
+ int child_status;
+ int sstat;
+ fd_set fds;
+ struct timeval tv = {timeout, 0};
+
+ FD_ZERO(&fds);
+ FD_SET(c_pipe, &fds);
+
+ sstat = select(c_pipe+1, &fds, NULL, NULL, timeout? &tv: NULL);
+ if (sstat == 0) {
+ fprintf(stderr, "clvmd startup timed out\n");
+ exit(DFAIL_TIMEOUT);
+ }
+ if (sstat == 1) {
+ if (read(c_pipe, &child_status, sizeof(child_status)) !=
+ sizeof(child_status)) {
+
+ fprintf(stderr, "clvmd failed in initialisation\n");
+ exit(DFAIL_INIT);
+ }
+ else {
+ switch (child_status) {
+ case SUCCESS:
+ break;
+ case DFAIL_INIT:
+ fprintf(stderr, "clvmd failed in initialisation\n");
+ break;
+ case DFAIL_LOCAL_SOCK:
+ fprintf(stderr, "clvmd could not create local socket\n");
+ fprintf(stderr, "Another clvmd is probably already running\n");
+ break;
+ case DFAIL_CLUSTER_IF:
+ fprintf(stderr, "clvmd could not connect to cluster manager\n");
+ fprintf(stderr, "Consult syslog for more information\n");
+ break;
+ case DFAIL_MALLOC:
+ fprintf(stderr, "clvmd failed, not enough memory\n");
+ break;
+ default:
+ fprintf(stderr, "clvmd failed, error was %d\n", child_status);
+ break;
+ }
+ exit(child_status);
+ }
+ }
+ fprintf(stderr, "clvmd startup, select failed: %s\n", strerror(errno));
+ exit(DFAIL_INIT);
+}
+
+/*
+ * Fork into the background and detach from our parent process.
+ * In the interests of user-friendliness we wait for the daemon
+ * to complete initialisation before returning its status
+ * the the user.
+ */
+static void be_daemon(int timeout)
+{
+ int devnull = open("/dev/null", O_RDWR);
+ if (devnull == -1) {
+ perror("Can't open /dev/null");
+ exit(3);
+ }
+
+ pipe(child_pipe);
+
+ switch (fork()) {
+ case -1:
+ perror("clvmd: can't fork");
+ exit(2);
+
+ case 0: /* Child */
+ close(child_pipe[0]);
+ break;
+
+ default: /* Parent */
+ close(child_pipe[1]);
+ wait_for_child(child_pipe[0], timeout);
+ }
+
+ /* Detach ourself from the calling environment */
+ if (close(0) || close(1) || close(2)) {
+ perror("Error closing terminal FDs");
+ exit(4);
+ }
+ setsid();
+
+ if (dup2(devnull, 0) < 0 || dup2(devnull, 1) < 0
+ || dup2(devnull, 2) < 0) {
+ perror("Error setting terminal FDs to /dev/null");
+ log_error("Error setting terminal FDs to /dev/null: %m");
+ exit(5);
+ }
+ if (chdir("/")) {
+ log_error("Error setting current directory to /: %m");
+ exit(6);
+ }
+
+}
+
+/* Called when we have a read from the local socket.
+ was in the main loop but it's grown up and is a big girl now */
+static int read_from_local_sock(struct local_client *thisfd)
+{
+ int len;
+ int argslen;
+ int missing_len;
+ char buffer[PIPE_BUF];
+
+ len = read(thisfd->fd, buffer, sizeof(buffer));
+ if (len == -1 && errno == EINTR)
+ return 1;
+
+ DEBUGLOG("Read on local socket %d, len = %d\n", thisfd->fd, len);
+
+ /* EOF or error on socket */
+ if (len <= 0) {
+ int *status;
+ int jstat;
+
+ DEBUGLOG("EOF on local socket: inprogress=%d\n",
+ thisfd->bits.localsock.in_progress);
+
+ thisfd->bits.localsock.finished = 1;
+
+ /* If the client went away in mid command then tidy up */
+ if (thisfd->bits.localsock.in_progress) {
+ pthread_kill(thisfd->bits.localsock.threadid, SIGUSR2);
+ pthread_mutex_lock(&thisfd->bits.localsock.mutex);
+ thisfd->bits.localsock.state = POST_COMMAND;
+ pthread_cond_signal(&thisfd->bits.localsock.cond);
+ pthread_mutex_unlock(&thisfd->bits.localsock.mutex);
+
+ /* Free any unsent buffers */
+ free_reply(thisfd);
+ }
+
+ /* Kill the subthread & free resources */
+ if (thisfd->bits.localsock.threadid) {
+ DEBUGLOG("Waiting for child thread\n");
+ pthread_mutex_lock(&thisfd->bits.localsock.mutex);
+ thisfd->bits.localsock.state = PRE_COMMAND;
+ pthread_cond_signal(&thisfd->bits.localsock.cond);
+ pthread_mutex_unlock(&thisfd->bits.localsock.mutex);
+
+ jstat =
+ pthread_join(thisfd->bits.localsock.threadid,
+ (void **) &status);
+ DEBUGLOG("Joined child thread\n");
+
+ thisfd->bits.localsock.threadid = 0;
+ pthread_cond_destroy(&thisfd->bits.localsock.cond);
+ pthread_mutex_destroy(&thisfd->bits.localsock.mutex);
+
+ /* Remove the pipe client */
+ if (thisfd->bits.localsock.pipe_client != NULL) {
+ struct local_client *newfd;
+ struct local_client *lastfd = NULL;
+ struct local_client *free_fd = NULL;
+
+ close(thisfd->bits.localsock.pipe_client->fd); /* Close pipe */
+ close(thisfd->bits.localsock.pipe);
+
+ /* Remove pipe client */
+ for (newfd = &local_client_head; newfd != NULL;
+ newfd = newfd->next) {
+ if (thisfd->bits.localsock.
+ pipe_client == newfd) {
+ thisfd->bits.localsock.
+ pipe_client = NULL;
+
+ lastfd->next = newfd->next;
+ free_fd = newfd;
+ newfd->next = lastfd;
+ free(free_fd);
+ break;
+ }
+ lastfd = newfd;
+ }
+ }
+ }
+
+ /* Free the command buffer */
+ free(thisfd->bits.localsock.cmd);
+
+ /* Clear out the cross-link */
+ if (thisfd->bits.localsock.pipe_client != NULL)
+ thisfd->bits.localsock.pipe_client->bits.pipe.client =
+ NULL;
+
+ safe_close(&(thisfd->fd));
+ return 0;
+ } else {
+ int comms_pipe[2];
+ struct local_client *newfd;
+ char csid[MAX_CSID_LEN];
+ struct clvm_header *inheader;
+ int status;
+
+ inheader = (struct clvm_header *) buffer;
+
+ /* Fill in the client ID */
+ inheader->clientid = htonl(thisfd->fd);
+
+ /* If we are already busy then return an error */
+ if (thisfd->bits.localsock.in_progress) {
+ struct clvm_header reply;
+ reply.cmd = CLVMD_CMD_REPLY;
+ reply.status = EBUSY;
+ reply.arglen = 0;
+ reply.flags = 0;
+ send_message(&reply, sizeof(reply), our_csid,
+ thisfd->fd,
+ "Error sending EBUSY reply to local user");
+ return len;
+ }
+
+ /* Free any old buffer space */
+ free(thisfd->bits.localsock.cmd);
+
+ /* See if we have the whole message */
+ argslen =
+ len - strlen(inheader->node) - sizeof(struct clvm_header);
+ missing_len = inheader->arglen - argslen;
+
+ if (missing_len < 0)
+ missing_len = 0;
+
+ /* Save the message */
+ thisfd->bits.localsock.cmd = malloc(len + missing_len);
+
+ if (!thisfd->bits.localsock.cmd) {
+ struct clvm_header reply;
+ reply.cmd = CLVMD_CMD_REPLY;
+ reply.status = ENOMEM;
+ reply.arglen = 0;
+ reply.flags = 0;
+ send_message(&reply, sizeof(reply), our_csid,
+ thisfd->fd,
+ "Error sending ENOMEM reply to local user");
+ return 0;
+ }
+ memcpy(thisfd->bits.localsock.cmd, buffer, len);
+ thisfd->bits.localsock.cmd_len = len + missing_len;
+ inheader = (struct clvm_header *) thisfd->bits.localsock.cmd;
+
+ /* If we don't have the full message then read the rest now */
+ if (missing_len) {
+ char *argptr =
+ inheader->node + strlen(inheader->node) + 1;
+
+ while (missing_len > 0 && len >= 0) {
+ DEBUGLOG
+ ("got %d bytes, need another %d (total %d)\n",
+ argslen, missing_len, inheader->arglen);
+ len = read(thisfd->fd, argptr + argslen,
+ missing_len);
+ if (len >= 0) {
+ missing_len -= len;
+ argslen += len;
+ }
+ }
+ }
+
+ /* Initialise and lock the mutex so the subthread will wait after
+ finishing the PRE routine */
+ if (!thisfd->bits.localsock.threadid) {
+ pthread_mutex_init(&thisfd->bits.localsock.mutex, NULL);
+ pthread_cond_init(&thisfd->bits.localsock.cond, NULL);
+ pthread_mutex_init(&thisfd->bits.localsock.reply_mutex, NULL);
+ }
+
+ /* Only run the command if all the cluster nodes are running CLVMD */
+ if (((inheader->flags & CLVMD_FLAG_LOCAL) == 0) &&
+ (check_all_clvmds_running(thisfd) == -1)) {
+ thisfd->bits.localsock.expected_replies = 0;
+ thisfd->bits.localsock.num_replies = 0;
+ send_local_reply(thisfd, EHOSTDOWN, thisfd->fd);
+ return len;
+ }
+
+ /* Check the node name for validity */
+ if (inheader->node[0] && clops->csid_from_name(csid, inheader->node)) {
+ /* Error, node is not in the cluster */
+ struct clvm_header reply;
+ DEBUGLOG("Unknown node: '%s'\n", inheader->node);
+
+ reply.cmd = CLVMD_CMD_REPLY;
+ reply.status = ENOENT;
+ reply.flags = 0;
+ reply.arglen = 0;
+ send_message(&reply, sizeof(reply), our_csid,
+ thisfd->fd,
+ "Error sending ENOENT reply to local user");
+ thisfd->bits.localsock.expected_replies = 0;
+ thisfd->bits.localsock.num_replies = 0;
+ thisfd->bits.localsock.in_progress = FALSE;
+ thisfd->bits.localsock.sent_out = FALSE;
+ return len;
+ }
+
+ /* If we already have a subthread then just signal it to start */
+ if (thisfd->bits.localsock.threadid) {
+ pthread_mutex_lock(&thisfd->bits.localsock.mutex);
+ thisfd->bits.localsock.state = PRE_COMMAND;
+ pthread_cond_signal(&thisfd->bits.localsock.cond);
+ pthread_mutex_unlock(&thisfd->bits.localsock.mutex);
+ return len;
+ }
+
+ /* Create a pipe and add the reading end to our FD list */
+ pipe(comms_pipe);
+ newfd = malloc(sizeof(struct local_client));
+ if (!newfd) {
+ struct clvm_header reply;
+ close(comms_pipe[0]);
+ close(comms_pipe[1]);
+
+ reply.cmd = CLVMD_CMD_REPLY;
+ reply.status = ENOMEM;
+ reply.arglen = 0;
+ reply.flags = 0;
+ send_message(&reply, sizeof(reply), our_csid,
+ thisfd->fd,
+ "Error sending ENOMEM reply to local user");
+ return len;
+ }
+ DEBUGLOG("creating pipe, [%d, %d]\n", comms_pipe[0],
+ comms_pipe[1]);
+
+ if (fcntl(comms_pipe[0], F_SETFD, 1))
+ DEBUGLOG("setting CLOEXEC on pipe[0] failed: %s\n", strerror(errno));
+ if (fcntl(comms_pipe[1], F_SETFD, 1))
+ DEBUGLOG("setting CLOEXEC on pipe[1] failed: %s\n", strerror(errno));
+
+ newfd->fd = comms_pipe[0];
+ newfd->removeme = 0;
+ newfd->type = THREAD_PIPE;
+ newfd->callback = local_pipe_callback;
+ newfd->next = thisfd->next;
+ newfd->bits.pipe.client = thisfd;
+ newfd->bits.pipe.threadid = 0;
+ thisfd->next = newfd;
+
+ /* Store a cross link to the pipe */
+ thisfd->bits.localsock.pipe_client = newfd;
+
+ thisfd->bits.localsock.pipe = comms_pipe[1];
+
+ /* Make sure the thread has a copy of it's own ID */
+ newfd->bits.pipe.threadid = thisfd->bits.localsock.threadid;
+
+ /* Run the pre routine */
+ thisfd->bits.localsock.in_progress = TRUE;
+ thisfd->bits.localsock.state = PRE_COMMAND;
+ DEBUGLOG("Creating pre&post thread\n");
+ status = pthread_create(&thisfd->bits.localsock.threadid, NULL,
+ pre_and_post_thread, thisfd);
+ DEBUGLOG("Created pre&post thread, state = %d\n", status);
+ }
+ return len;
+}
+
+/* Add a file descriptor from the cluster or comms interface to
+ our list of FDs for select
+*/
+int add_client(struct local_client *new_client)
+{
+ new_client->next = local_client_head.next;
+ local_client_head.next = new_client;
+
+ return 0;
+}
+
+/* Called when the pre-command has completed successfully - we
+ now execute the real command on all the requested nodes */
+static int distribute_command(struct local_client *thisfd)
+{
+ struct clvm_header *inheader =
+ (struct clvm_header *) thisfd->bits.localsock.cmd;
+ int len = thisfd->bits.localsock.cmd_len;
+
+ thisfd->xid = global_xid++;
+ DEBUGLOG("distribute command: XID = %d\n", thisfd->xid);
+
+ /* Forward it to other nodes in the cluster if needed */
+ if (!(inheader->flags & CLVMD_FLAG_LOCAL)) {
+ /* if node is empty then do it on the whole cluster */
+ if (inheader->node[0] == '\0') {
+ thisfd->bits.localsock.expected_replies =
+ clops->get_num_nodes();
+ thisfd->bits.localsock.num_replies = 0;
+ thisfd->bits.localsock.sent_time = time(NULL);
+ thisfd->bits.localsock.in_progress = TRUE;
+ thisfd->bits.localsock.sent_out = TRUE;
+
+ /* Do it here first */
+ add_to_lvmqueue(thisfd, inheader, len, NULL);
+
+ DEBUGLOG("Sending message to all cluster nodes\n");
+ inheader->xid = thisfd->xid;
+ send_message(inheader, len, NULL, -1,
+ "Error forwarding message to cluster");
+ } else {
+ /* Do it on a single node */
+ char csid[MAX_CSID_LEN];
+
+ if (clops->csid_from_name(csid, inheader->node)) {
+ /* This has already been checked so should not happen */
+ return 0;
+ } else {
+ /* OK, found a node... */
+ thisfd->bits.localsock.expected_replies = 1;
+ thisfd->bits.localsock.num_replies = 0;
+ thisfd->bits.localsock.in_progress = TRUE;
+
+ /* Are we the requested node ?? */
+ if (memcmp(csid, our_csid, max_csid_len) == 0) {
+ DEBUGLOG("Doing command on local node only\n");
+ add_to_lvmqueue(thisfd, inheader, len, NULL);
+ } else {
+ DEBUGLOG("Sending message to single node: %s\n",
+ inheader->node);
+ inheader->xid = thisfd->xid;
+ send_message(inheader, len,
+ csid, -1,
+ "Error forwarding message to cluster node");
+ }
+ }
+ }
+ } else {
+ /* Local explicitly requested, ignore nodes */
+ thisfd->bits.localsock.in_progress = TRUE;
+ thisfd->bits.localsock.expected_replies = 1;
+ thisfd->bits.localsock.num_replies = 0;
+ add_to_lvmqueue(thisfd, inheader, len, NULL);
+ }
+ return 0;
+}
+
+/* Process a command from a remote node and return the result */
+static void process_remote_command(struct clvm_header *msg, int msglen, int fd,
+ const char *csid)
+{
+ char *replyargs;
+ char nodename[max_cluster_member_name_len];
+ int replylen = 0;
+ int buflen = max_cluster_message - sizeof(struct clvm_header) - 1;
+ int status;
+ int msg_malloced = 0;
+
+ /* Get the node name as we /may/ need it later */
+ clops->name_from_csid(csid, nodename);
+
+ DEBUGLOG("process_remote_command %s for clientid 0x%x XID %d on node %s\n",
+ decode_cmd(msg->cmd), msg->clientid, msg->xid, nodename);
+
+ /* Check for GOAWAY and sulk */
+ if (msg->cmd == CLVMD_CMD_GOAWAY) {
+
+ DEBUGLOG("Told to go away by %s\n", nodename);
+ log_error("Told to go away by %s\n", nodename);
+ exit(99);
+ }
+
+ /* Version check is internal - don't bother exposing it in
+ clvmd-command.c */
+ if (msg->cmd == CLVMD_CMD_VERSION) {
+ int version_nums[3];
+ char node[256];
+
+ memcpy(version_nums, msg->args, sizeof(version_nums));
+
+ clops->name_from_csid(csid, node);
+ DEBUGLOG("Remote node %s is version %d.%d.%d\n",
+ node,
+ ntohl(version_nums[0]),
+ ntohl(version_nums[1]), ntohl(version_nums[2]));
+
+ if (ntohl(version_nums[0]) != CLVMD_MAJOR_VERSION) {
+ struct clvm_header byebyemsg;
+ DEBUGLOG
+ ("Telling node %s to go away because of incompatible version number\n",
+ node);
+ log_notice
+ ("Telling node %s to go away because of incompatible version number %d.%d.%d\n",
+ node, ntohl(version_nums[0]),
+ ntohl(version_nums[1]), ntohl(version_nums[2]));
+
+ byebyemsg.cmd = CLVMD_CMD_GOAWAY;
+ byebyemsg.status = 0;
+ byebyemsg.flags = 0;
+ byebyemsg.arglen = 0;
+ byebyemsg.clientid = 0;
+ clops->cluster_send_message(&byebyemsg, sizeof(byebyemsg),
+ our_csid,
+ "Error Sending GOAWAY message");
+ } else {
+ clops->add_up_node(csid);
+ }
+ return;
+ }
+
+ /* Allocate a default reply buffer */
+ replyargs = malloc(max_cluster_message - sizeof(struct clvm_header));
+
+ if (replyargs != NULL) {
+ /* Run the command */
+ status =
+ do_command(NULL, msg, msglen, &replyargs, buflen,
+ &replylen);
+ } else {
+ status = ENOMEM;
+ }
+
+ /* If it wasn't a reply, then reply */
+ if (msg->cmd != CLVMD_CMD_REPLY) {
+ char *aggreply;
+
+ aggreply =
+ realloc(replyargs, replylen + sizeof(struct clvm_header));
+ if (aggreply) {
+ struct clvm_header *agghead =
+ (struct clvm_header *) aggreply;
+
+ replyargs = aggreply;
+ /* Move it up so there's room for a header in front of the data */
+ memmove(aggreply + offsetof(struct clvm_header, args),
+ replyargs, replylen);
+
+ agghead->xid = msg->xid;
+ agghead->cmd = CLVMD_CMD_REPLY;
+ agghead->status = status;
+ agghead->flags = 0;
+ agghead->clientid = msg->clientid;
+ agghead->arglen = replylen;
+ agghead->node[0] = '\0';
+ send_message(aggreply,
+ sizeof(struct clvm_header) +
+ replylen, csid, fd,
+ "Error sending command reply");
+ } else {
+ struct clvm_header head;
+
+ DEBUGLOG("Error attempting to realloc return buffer\n");
+ /* Return a failure response */
+ head.cmd = CLVMD_CMD_REPLY;
+ head.status = ENOMEM;
+ head.flags = 0;
+ head.clientid = msg->clientid;
+ head.arglen = 0;
+ head.node[0] = '\0';
+ send_message(&head, sizeof(struct clvm_header), csid,
+ fd, "Error sending ENOMEM command reply");
+ return;
+ }
+ }
+
+ /* Free buffer if it was malloced */
+ if (msg_malloced) {
+ free(msg);
+ }
+ free(replyargs);
+}
+
+/* Add a reply to a command to the list of replies for this client.
+ If we have got a full set then send them to the waiting client down the local
+ socket */
+static void add_reply_to_list(struct local_client *client, int status,
+ const char *csid, const char *buf, int len)
+{
+ struct node_reply *reply;
+
+ pthread_mutex_lock(&client->bits.localsock.reply_mutex);
+
+ /* Add it to the list of replies */
+ reply = malloc(sizeof(struct node_reply));
+ if (reply) {
+ reply->status = status;
+ clops->name_from_csid(csid, reply->node);
+ DEBUGLOG("Reply from node %s: %d bytes\n", reply->node, len);
+
+ if (len > 0) {
+ reply->replymsg = malloc(len);
+ if (!reply->replymsg) {
+ reply->status = ENOMEM;
+ } else {
+ memcpy(reply->replymsg, buf, len);
+ }
+ } else {
+ reply->replymsg = NULL;
+ }
+ /* Hook it onto the reply chain */
+ reply->next = client->bits.localsock.replies;
+ client->bits.localsock.replies = reply;
+ } else {
+ /* It's all gone horribly wrong... */
+ pthread_mutex_unlock(&client->bits.localsock.reply_mutex);
+ send_local_reply(client, ENOMEM, client->fd);
+ return;
+ }
+ DEBUGLOG("Got %d replies, expecting: %d\n",
+ client->bits.localsock.num_replies + 1,
+ client->bits.localsock.expected_replies);
+
+ /* If we have the whole lot then do the post-process */
+ if (++client->bits.localsock.num_replies ==
+ client->bits.localsock.expected_replies) {
+ /* Post-process the command */
+ if (client->bits.localsock.threadid) {
+ pthread_mutex_lock(&client->bits.localsock.mutex);
+ client->bits.localsock.state = POST_COMMAND;
+ pthread_cond_signal(&client->bits.localsock.cond);
+ pthread_mutex_unlock(&client->bits.localsock.mutex);
+ }
+ }
+ pthread_mutex_unlock(&client->bits.localsock.reply_mutex);
+}
+
+/* This is the thread that runs the PRE and post commands for a particular connection */
+static __attribute__ ((noreturn)) void *pre_and_post_thread(void *arg)
+{
+ struct local_client *client = (struct local_client *) arg;
+ int status;
+ int write_status;
+ sigset_t ss;
+ int pipe_fd = client->bits.localsock.pipe;
+
+ DEBUGLOG("in sub thread: client = %p\n", client);
+ pthread_mutex_lock(&client->bits.localsock.mutex);
+
+ /* Don't start until the LVM thread is ready */
+ pthread_mutex_lock(&lvm_start_mutex);
+ pthread_mutex_unlock(&lvm_start_mutex);
+ DEBUGLOG("Sub thread ready for work.\n");
+
+ /* Ignore SIGUSR1 (handled by master process) but enable
+ SIGUSR2 (kills subthreads) */
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGUSR1);
+ pthread_sigmask(SIG_BLOCK, &ss, NULL);
+
+ sigdelset(&ss, SIGUSR1);
+ sigaddset(&ss, SIGUSR2);
+ pthread_sigmask(SIG_UNBLOCK, &ss, NULL);
+
+ /* Loop around doing PRE and POST functions until the client goes away */
+ while (!client->bits.localsock.finished) {
+ /* Execute the code */
+ status = do_pre_command(client);
+
+ if (status)
+ client->bits.localsock.all_success = 0;
+
+ DEBUGLOG("Writing status %d down pipe %d\n", status, pipe_fd);
+
+ /* Tell the parent process we have finished this bit */
+ do {
+ write_status = write(pipe_fd, &status, sizeof(int));
+ if (write_status == sizeof(int))
+ break;
+ if (write_status < 0 &&
+ (errno == EINTR || errno == EAGAIN))
+ continue;
+ log_error("Error sending to pipe: %m\n");
+ break;
+ } while(1);
+
+ if (status) {
+ client->bits.localsock.state = POST_COMMAND;
+ goto next_pre;
+ }
+
+ /* We may need to wait for the condition variable before running the post command */
+ DEBUGLOG("Waiting to do post command - state = %d\n",
+ client->bits.localsock.state);
+
+ if (client->bits.localsock.state != POST_COMMAND &&
+ !client->bits.localsock.finished) {
+ pthread_cond_wait(&client->bits.localsock.cond,
+ &client->bits.localsock.mutex);
+ }
+
+ DEBUGLOG("Got post command condition...\n");
+
+ /* POST function must always run, even if the client aborts */
+ status = 0;
+ do_post_command(client);
+
+ do {
+ write_status = write(pipe_fd, &status, sizeof(int));
+ if (write_status == sizeof(int))
+ break;
+ if (write_status < 0 &&
+ (errno == EINTR || errno == EAGAIN))
+ continue;
+ log_error("Error sending to pipe: %m\n");
+ break;
+ } while(1);
+next_pre:
+ DEBUGLOG("Waiting for next pre command\n");
+
+ if (client->bits.localsock.state != PRE_COMMAND &&
+ !client->bits.localsock.finished) {
+ pthread_cond_wait(&client->bits.localsock.cond,
+ &client->bits.localsock.mutex);
+ }
+
+ DEBUGLOG("Got pre command condition...\n");
+ }
+ pthread_mutex_unlock(&client->bits.localsock.mutex);
+ DEBUGLOG("Subthread finished\n");
+ pthread_exit((void *) 0);
+}
+
+/* Process a command on the local node and store the result */
+static int process_local_command(struct clvm_header *msg, int msglen,
+ struct local_client *client,
+ unsigned short xid)
+{
+ char *replybuf = malloc(max_cluster_message);
+ int buflen = max_cluster_message - sizeof(struct clvm_header) - 1;
+ int replylen = 0;
+ int status;
+
+ DEBUGLOG("process_local_command: %s msg=%p, msglen =%d, client=%p\n",
+ decode_cmd(msg->cmd), msg, msglen, client);
+
+ if (replybuf == NULL)
+ return -1;
+
+ status = do_command(client, msg, msglen, &replybuf, buflen, &replylen);
+
+ if (status)
+ client->bits.localsock.all_success = 0;
+
+ /* If we took too long then discard the reply */
+ if (xid == client->xid) {
+ add_reply_to_list(client, status, our_csid, replybuf, replylen);
+ } else {
+ DEBUGLOG
+ ("Local command took too long, discarding xid %d, current is %d\n",
+ xid, client->xid);
+ }
+
+ free(replybuf);
+ return status;
+}
+
+static int process_reply(const struct clvm_header *msg, int msglen, const char *csid)
+{
+ struct local_client *client = NULL;
+
+ client = find_client(msg->clientid);
+ if (!client) {
+ DEBUGLOG("Got message for unknown client 0x%x\n",
+ msg->clientid);
+ log_error("Got message for unknown client 0x%x\n",
+ msg->clientid);
+ return -1;
+ }
+
+ if (msg->status)
+ client->bits.localsock.all_success = 0;
+
+ /* Gather replies together for this client id */
+ if (msg->xid == client->xid) {
+ add_reply_to_list(client, msg->status, csid, msg->args,
+ msg->arglen);
+ } else {
+ DEBUGLOG("Discarding reply with old XID %d, current = %d\n",
+ msg->xid, client->xid);
+ }
+ return 0;
+}
+
+/* Send an aggregated reply back to the client */
+static void send_local_reply(struct local_client *client, int status, int fd)
+{
+ struct clvm_header *clientreply;
+ struct node_reply *thisreply = client->bits.localsock.replies;
+ char *replybuf;
+ char *ptr;
+ int message_len = 0;
+
+ DEBUGLOG("Send local reply\n");
+
+ /* Work out the total size of the reply */
+ while (thisreply) {
+ if (thisreply->replymsg)
+ message_len += strlen(thisreply->replymsg) + 1;
+ else
+ message_len++;
+
+ message_len += strlen(thisreply->node) + 1 + sizeof(int);
+
+ thisreply = thisreply->next;
+ }
+
+ /* Add in the size of our header */
+ message_len = message_len + sizeof(struct clvm_header) + 1;
+ replybuf = malloc(message_len);
+
+ clientreply = (struct clvm_header *) replybuf;
+ clientreply->status = status;
+ clientreply->cmd = CLVMD_CMD_REPLY;
+ clientreply->node[0] = '\0';
+ clientreply->flags = 0;
+
+ ptr = clientreply->args;
+
+ /* Add in all the replies, and free them as we go */
+ thisreply = client->bits.localsock.replies;
+ while (thisreply) {
+ struct node_reply *tempreply = thisreply;
+
+ strcpy(ptr, thisreply->node);
+ ptr += strlen(thisreply->node) + 1;
+
+ if (thisreply->status)
+ clientreply->flags |= CLVMD_FLAG_NODEERRS;
+
+ memcpy(ptr, &thisreply->status, sizeof(int));
+ ptr += sizeof(int);
+
+ if (thisreply->replymsg) {
+ strcpy(ptr, thisreply->replymsg);
+ ptr += strlen(thisreply->replymsg) + 1;
+ } else {
+ ptr[0] = '\0';
+ ptr++;
+ }
+ thisreply = thisreply->next;
+
+ free(tempreply->replymsg);
+ free(tempreply);
+ }
+
+ /* Terminate with an empty node name */
+ *ptr = '\0';
+
+ clientreply->arglen = ptr - clientreply->args + 1;
+
+ /* And send it */
+ send_message(replybuf, message_len, our_csid, fd,
+ "Error sending REPLY to client");
+ free(replybuf);
+
+ /* Reset comms variables */
+ client->bits.localsock.replies = NULL;
+ client->bits.localsock.expected_replies = 0;
+ client->bits.localsock.in_progress = FALSE;
+ client->bits.localsock.sent_out = FALSE;
+}
+
+/* Just free a reply chain baceuse it wasn't used. */
+static void free_reply(struct local_client *client)
+{
+ /* Add in all the replies, and free them as we go */
+ struct node_reply *thisreply = client->bits.localsock.replies;
+ while (thisreply) {
+ struct node_reply *tempreply = thisreply;
+
+ thisreply = thisreply->next;
+
+ free(tempreply->replymsg);
+ free(tempreply);
+ }
+ client->bits.localsock.replies = NULL;
+}
+
+/* Send our version number to the cluster */
+static void send_version_message()
+{
+ char message[sizeof(struct clvm_header) + sizeof(int) * 3];
+ struct clvm_header *msg = (struct clvm_header *) message;
+ int version_nums[3];
+
+ msg->cmd = CLVMD_CMD_VERSION;
+ msg->status = 0;
+ msg->flags = 0;
+ msg->clientid = 0;
+ msg->arglen = sizeof(version_nums);
+
+ version_nums[0] = htonl(CLVMD_MAJOR_VERSION);
+ version_nums[1] = htonl(CLVMD_MINOR_VERSION);
+ version_nums[2] = htonl(CLVMD_PATCH_VERSION);
+
+ memcpy(&msg->args, version_nums, sizeof(version_nums));
+
+ hton_clvm(msg);
+
+ clops->cluster_send_message(message, sizeof(message), NULL,
+ "Error Sending version number");
+}
+
+/* Send a message to either a local client or another server */
+static int send_message(void *buf, int msglen, const char *csid, int fd,
+ const char *errtext)
+{
+ int len = 0;
+ int saved_errno = 0;
+ struct timespec delay;
+ struct timespec remtime;
+
+ int retry_cnt = 0;
+
+ /* Send remote messages down the cluster socket */
+ if (csid == NULL || !ISLOCAL_CSID(csid)) {
+ hton_clvm((struct clvm_header *) buf);
+ return clops->cluster_send_message(buf, msglen, csid, errtext);
+ } else {
+ int ptr = 0;
+
+ /* Make sure it all goes */
+ do {
+ if (retry_cnt > MAX_RETRIES)
+ {
+ errno = saved_errno;
+ log_error("%s", errtext);
+ errno = saved_errno;
+ break;
+ }
+
+ len = write(fd, buf + ptr, msglen - ptr);
+
+ if (len <= 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN ||
+ errno == EIO ||
+ errno == ENOSPC) {
+ saved_errno = errno;
+ retry_cnt++;
+
+ delay.tv_sec = 0;
+ delay.tv_nsec = 100000;
+ remtime.tv_sec = 0;
+ remtime.tv_nsec = 0;
+ (void) nanosleep (&delay, &remtime);
+
+ continue;
+ }
+ log_error("%s", errtext);
+ break;
+ }
+ ptr += len;
+ } while (ptr < msglen);
+ }
+ return len;
+}
+
+static int process_work_item(struct lvm_thread_cmd *cmd)
+{
+ /* If msg is NULL then this is a cleanup request */
+ if (cmd->msg == NULL) {
+ DEBUGLOG("process_work_item: free fd %d\n", cmd->client->fd);
+ cmd_client_cleanup(cmd->client);
+ free(cmd->client);
+ return 0;
+ }
+
+ if (!cmd->remote) {
+ DEBUGLOG("process_work_item: local\n");
+ process_local_command(cmd->msg, cmd->msglen, cmd->client,
+ cmd->xid);
+ } else {
+ DEBUGLOG("process_work_item: remote\n");
+ process_remote_command(cmd->msg, cmd->msglen, cmd->client->fd,
+ cmd->csid);
+ }
+ return 0;
+}
+
+/*
+ * Routine that runs in the "LVM thread".
+ */
+static void lvm_thread_fn(void *arg)
+{
+ struct dm_list *cmdl, *tmp;
+ sigset_t ss;
+ struct lvm_startup_params *lvm_params = arg;
+
+ DEBUGLOG("LVM thread function started\n");
+
+ /* Ignore SIGUSR1 & 2 */
+ sigemptyset(&ss);
+ sigaddset(&ss, SIGUSR1);
+ sigaddset(&ss, SIGUSR2);
+ pthread_sigmask(SIG_BLOCK, &ss, NULL);
+
+ /* Initialise the interface to liblvm */
+ init_clvm(lvm_params->using_gulm, lvm_params->argv);
+
+ /* Allow others to get moving */
+ pthread_mutex_unlock(&lvm_start_mutex);
+
+ /* Now wait for some actual work */
+ for (;;) {
+ DEBUGLOG("LVM thread waiting for work\n");
+
+ pthread_mutex_lock(&lvm_thread_mutex);
+ if (dm_list_empty(&lvm_cmd_head))
+ pthread_cond_wait(&lvm_thread_cond, &lvm_thread_mutex);
+
+ dm_list_iterate_safe(cmdl, tmp, &lvm_cmd_head) {
+ struct lvm_thread_cmd *cmd;
+
+ cmd =
+ dm_list_struct_base(cmdl, struct lvm_thread_cmd, list);
+ dm_list_del(&cmd->list);
+ pthread_mutex_unlock(&lvm_thread_mutex);
+
+ process_work_item(cmd);
+ free(cmd->msg);
+ free(cmd);
+
+ pthread_mutex_lock(&lvm_thread_mutex);
+ }
+ pthread_mutex_unlock(&lvm_thread_mutex);
+ }
+}
+
+/* Pass down some work to the LVM thread */
+static int add_to_lvmqueue(struct local_client *client, struct clvm_header *msg,
+ int msglen, const char *csid)
+{
+ struct lvm_thread_cmd *cmd;
+
+ cmd = malloc(sizeof(struct lvm_thread_cmd));
+ if (!cmd)
+ return ENOMEM;
+
+ if (msglen) {
+ cmd->msg = malloc(msglen);
+ if (!cmd->msg) {
+ log_error("Unable to allocate buffer space\n");
+ free(cmd);
+ return -1;
+ }
+ memcpy(cmd->msg, msg, msglen);
+ }
+ else {
+ cmd->msg = NULL;
+ }
+ cmd->client = client;
+ cmd->msglen = msglen;
+ cmd->xid = client->xid;
+
+ if (csid) {
+ memcpy(cmd->csid, csid, max_csid_len);
+ cmd->remote = 1;
+ } else {
+ cmd->remote = 0;
+ }
+
+ DEBUGLOG
+ ("add_to_lvmqueue: cmd=%p. client=%p, msg=%p, len=%d, csid=%p, xid=%d\n",
+ cmd, client, msg, msglen, csid, cmd->xid);
+ pthread_mutex_lock(&lvm_thread_mutex);
+ dm_list_add(&lvm_cmd_head, &cmd->list);
+ pthread_cond_signal(&lvm_thread_cond);
+ pthread_mutex_unlock(&lvm_thread_mutex);
+
+ return 0;
+}
+
+/* Return 0 if we can talk to an existing clvmd */
+static int check_local_clvmd(void)
+{
+ int local_socket;
+ struct sockaddr_un sockaddr;
+ int ret = 0;
+
+ /* Open local socket */
+ if ((local_socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ return -1;
+ }
+
+ memset(&sockaddr, 0, sizeof(sockaddr));
+ memcpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(CLVMD_SOCKNAME));
+ sockaddr.sun_family = AF_UNIX;
+
+ if (connect(local_socket,(struct sockaddr *) &sockaddr,
+ sizeof(sockaddr))) {
+ ret = -1;
+ }
+
+ close(local_socket);
+ return ret;
+}
+
+static void close_local_sock(int local_socket)
+{
+ if (local_socket != -1 && close(local_socket))
+ stack;
+
+ if (CLVMD_SOCKNAME[0] != '\0' && unlink(CLVMD_SOCKNAME))
+ stack;
+}
+
+/* Open the local socket, that's the one we talk to libclvm down */
+static int open_local_sock()
+{
+ int local_socket = -1;
+ struct sockaddr_un sockaddr;
+ mode_t old_mask;
+
+ close_local_sock(local_socket);
+
+ (void) dm_prepare_selinux_context(CLVMD_SOCKNAME, S_IFSOCK);
+ old_mask = umask(0077);
+
+ /* Open local socket */
+ local_socket = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (local_socket < 0) {
+ log_error("Can't create local socket: %m");
+ goto error;
+ }
+
+ /* Set Close-on-exec & non-blocking */
+ if (fcntl(local_socket, F_SETFD, 1))
+ DEBUGLOG("setting CLOEXEC on local_socket failed: %s\n", strerror(errno));
+ fcntl(local_socket, F_SETFL, fcntl(local_socket, F_GETFL, 0) | O_NONBLOCK);
+
+ memset(&sockaddr, 0, sizeof(sockaddr));
+ memcpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(CLVMD_SOCKNAME));
+ sockaddr.sun_family = AF_UNIX;
+
+ if (bind(local_socket, (struct sockaddr *) &sockaddr, sizeof(sockaddr))) {
+ log_error("can't bind local socket: %m");
+ goto error;
+ }
+ if (listen(local_socket, 1) != 0) {
+ log_error("listen local: %m");
+ goto error;
+ }
+
+ umask(old_mask);
+ (void) dm_prepare_selinux_context(NULL, 0);
+ return local_socket;
+error:
+ close_local_sock(local_socket);
+ umask(old_mask);
+ (void) dm_prepare_selinux_context(NULL, 0);
+ return -1;
+}
+
+void process_message(struct local_client *client, const char *buf, int len,
+ const char *csid)
+{
+ struct clvm_header *inheader;
+
+ inheader = (struct clvm_header *) buf;
+ ntoh_clvm(inheader); /* Byteswap fields */
+ if (inheader->cmd == CLVMD_CMD_REPLY)
+ process_reply(inheader, len, csid);
+ else
+ add_to_lvmqueue(client, inheader, len, csid);
+}
+
+
+static void check_all_callback(struct local_client *client, const char *csid,
+ int node_up)
+{
+ if (!node_up)
+ add_reply_to_list(client, EHOSTDOWN, csid, "CLVMD not running",
+ 18);
+}
+
+/* Check to see if all CLVMDs are running (ie one on
+ every node in the cluster).
+ If not, returns -1 and prints out a list of errant nodes */
+static int check_all_clvmds_running(struct local_client *client)
+{
+ DEBUGLOG("check_all_clvmds_running\n");
+ return clops->cluster_do_node_callback(client, check_all_callback);
+}
+
+/* Return a local_client struct given a client ID.
+ client IDs are in network byte order */
+static struct local_client *find_client(int clientid)
+{
+ struct local_client *thisfd;
+ for (thisfd = &local_client_head; thisfd != NULL; thisfd = thisfd->next) {
+ if (thisfd->fd == ntohl(clientid))
+ return thisfd;
+ }
+ return NULL;
+}
+
+/* Byte-swapping routines for the header so we
+ work in a heterogeneous environment */
+static void hton_clvm(struct clvm_header *hdr)
+{
+ hdr->status = htonl(hdr->status);
+ hdr->arglen = htonl(hdr->arglen);
+ hdr->xid = htons(hdr->xid);
+ /* Don't swap clientid as it's only a token as far as
+ remote nodes are concerned */
+}
+
+static void ntoh_clvm(struct clvm_header *hdr)
+{
+ hdr->status = ntohl(hdr->status);
+ hdr->arglen = ntohl(hdr->arglen);
+ hdr->xid = ntohs(hdr->xid);
+}
+
+/* Handler for SIGUSR2 - sent to kill subthreads */
+static void sigusr2_handler(int sig)
+{
+ DEBUGLOG("SIGUSR2 received\n");
+ return;
+}
+
+static void sigterm_handler(int sig)
+{
+ DEBUGLOG("SIGTERM received\n");
+ quit = 1;
+ return;
+}
+
+static void sighup_handler(int sig)
+{
+ DEBUGLOG("got SIGHUP\n");
+ reread_config = 1;
+}
+
+int sync_lock(const char *resource, int mode, int flags, int *lockid)
+{
+ return clops->sync_lock(resource, mode, flags, lockid);
+}
+
+int sync_unlock(const char *resource, int lockid)
+{
+ return clops->sync_unlock(resource, lockid);
+}
+
+static if_type_t parse_cluster_interface(char *ifname)
+{
+ if_type_t iface = IF_AUTO;
+
+ if (!strcmp(ifname, "auto"))
+ iface = IF_AUTO;
+ if (!strcmp(ifname, "cman"))
+ iface = IF_CMAN;
+ if (!strcmp(ifname, "gulm"))
+ iface = IF_GULM;
+ if (!strcmp(ifname, "openais"))
+ iface = IF_OPENAIS;
+ if (!strcmp(ifname, "corosync"))
+ iface = IF_COROSYNC;
+ if (!strcmp(ifname, "singlenode"))
+ iface = IF_SINGLENODE;
+
+ return iface;
+}
+
+/*
+ * Try and find a cluster system in corosync's objdb, if it is running. This is
+ * only called if the command-line option is not present, and if it fails
+ * we still try the interfaces in order.
+ */
+static if_type_t get_cluster_type()
+{
+#ifdef HAVE_COROSYNC_CONFDB_H
+ confdb_handle_t handle;
+ if_type_t type = IF_AUTO;
+ int result;
+ char buf[255];
+ size_t namelen = sizeof(buf);
+ hdb_handle_t cluster_handle;
+ hdb_handle_t clvmd_handle;
+ confdb_callbacks_t callbacks = {
+ .confdb_key_change_notify_fn = NULL,
+ .confdb_object_create_change_notify_fn = NULL,
+ .confdb_object_delete_change_notify_fn = NULL
+ };
+
+ result = confdb_initialize (&handle, &callbacks);
+ if (result != CS_OK)
+ return type;
+
+ result = confdb_object_find_start(handle, OBJECT_PARENT_HANDLE);
+ if (result != CS_OK)
+ goto out;
+
+ result = confdb_object_find(handle, OBJECT_PARENT_HANDLE, (void *)"cluster", strlen("cluster"), &cluster_handle);
+ if (result != CS_OK)
+ goto out;
+
+ result = confdb_object_find_start(handle, cluster_handle);
+ if (result != CS_OK)
+ goto out;
+
+ result = confdb_object_find(handle, cluster_handle, (void *)"clvmd", strlen("clvmd"), &clvmd_handle);
+ if (result != CS_OK)
+ goto out;
+
+ result = confdb_key_get(handle, clvmd_handle, (void *)"interface", strlen("interface"), buf, &namelen);
+ if (result != CS_OK)
+ goto out;
+
+ buf[namelen] = '\0';
+ type = parse_cluster_interface(buf);
+ DEBUGLOG("got interface type '%s' from confdb\n", buf);
+out:
+ confdb_finalize(handle);
+ return type;
+#else
+ return IF_AUTO;
+#endif
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CLVMD_H
+#define _CLVMD_H
+
+#define CLVMD_MAJOR_VERSION 0
+#define CLVMD_MINOR_VERSION 2
+#define CLVMD_PATCH_VERSION 1
+
+/* Default time (in seconds) we will wait for all remote commands to execute
+ before declaring them dead */
+#define DEFAULT_CMD_TIMEOUT 60
+
+/* One of these for each reply we get from command execution on a node */
+struct node_reply {
+ char node[MAX_CLUSTER_MEMBER_NAME_LEN];
+ char *replymsg;
+ int status;
+ struct node_reply *next;
+};
+
+typedef enum {DEBUG_OFF, DEBUG_STDERR, DEBUG_SYSLOG} debug_t;
+
+/*
+ * These exist for the use of local sockets only when we are
+ * collecting responses from all cluster nodes
+ */
+struct localsock_bits {
+ struct node_reply *replies;
+ int num_replies;
+ int expected_replies;
+ time_t sent_time; /* So we can check for timeouts */
+ int in_progress; /* Only execute one cmd at a time per client */
+ int sent_out; /* Flag to indicate that a command was sent
+ to remote nodes */
+ void *private; /* Private area for command processor use */
+ void *cmd; /* Whole command as passed down local socket */
+ int cmd_len; /* Length of above */
+ int pipe; /* Pipe to send PRE completion status down */
+ int finished; /* Flag to tell subthread to exit */
+ int all_success; /* Set to 0 if any node (or the pre_command)
+ failed */
+ struct local_client *pipe_client;
+ pthread_t threadid;
+ enum { PRE_COMMAND, POST_COMMAND, QUIT } state;
+ pthread_mutex_t mutex; /* Main thread and worker synchronisation */
+ pthread_cond_t cond;
+
+ pthread_mutex_t reply_mutex; /* Protect reply structure */
+};
+
+/* Entries for PIPE clients */
+struct pipe_bits {
+ struct local_client *client; /* Actual (localsock) client */
+ pthread_t threadid; /* Our own copy of the thread id */
+};
+
+/* Entries for Network socket clients */
+struct netsock_bits {
+ void *private;
+ int flags;
+};
+
+typedef int (*fd_callback_t) (struct local_client * fd, char *buf, int len,
+ const char *csid,
+ struct local_client ** new_client);
+
+/* One of these for each fd we are listening on */
+struct local_client {
+ int fd;
+ enum { CLUSTER_MAIN_SOCK, CLUSTER_DATA_SOCK, LOCAL_RENDEZVOUS,
+ LOCAL_SOCK, THREAD_PIPE, CLUSTER_INTERNAL } type;
+ struct local_client *next;
+ unsigned short xid;
+ fd_callback_t callback;
+ uint8_t removeme;
+
+ union {
+ struct localsock_bits localsock;
+ struct pipe_bits pipe;
+ struct netsock_bits net;
+ } bits;
+};
+
+#define DEBUGLOG(fmt, args...) debuglog(fmt, ## args);
+
+#ifndef max
+#define max(a,b) ((a)>(b)?(a):(b))
+#endif
+
+/* The real command processor is in clvmd-command.c */
+extern int do_command(struct local_client *client, struct clvm_header *msg,
+ int msglen, char **buf, int buflen, int *retlen);
+
+/* Pre and post command routines are called only on the local node */
+extern int do_pre_command(struct local_client *client);
+extern int do_post_command(struct local_client *client);
+extern void cmd_client_cleanup(struct local_client *client);
+extern int add_client(struct local_client *new_client);
+
+extern void clvmd_cluster_init_completed(void);
+extern void process_message(struct local_client *client, const char *buf,
+ int len, const char *csid);
+extern void debuglog(const char *fmt, ... )
+ __attribute__ ((format(printf, 1, 2)));
+
+int sync_lock(const char *resource, int mode, int flags, int *lockid);
+int sync_unlock(const char *resource, int lockid);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+
+#include "lvm-types.h"
+#include "clvm.h"
+#include "clvmd-comms.h"
+#include "clvmd.h"
+#include "lvm-functions.h"
+
+/* LVM2 headers */
+#include "toolcontext.h"
+#include "lvmcache.h"
+#include "lvm-globals.h"
+#include "activate.h"
+#include "archiver.h"
+#include "memlock.h"
+
+#include <syslog.h>
+
+static struct cmd_context *cmd = NULL;
+static struct dm_hash_table *lv_hash = NULL;
+static pthread_mutex_t lv_hash_lock;
+static pthread_mutex_t lvm_lock;
+static char last_error[1024];
+
+struct lv_info {
+ int lock_id;
+ int lock_mode;
+};
+
+static const char *decode_full_locking_cmd(uint32_t cmdl)
+{
+ static char buf[128];
+ const char *type;
+ const char *scope;
+ const char *command;
+
+ switch (cmdl & LCK_TYPE_MASK) {
+ case LCK_NULL:
+ type = "NULL";
+ break;
+ case LCK_READ:
+ type = "READ";
+ break;
+ case LCK_PREAD:
+ type = "PREAD";
+ break;
+ case LCK_WRITE:
+ type = "WRITE";
+ break;
+ case LCK_EXCL:
+ type = "EXCL";
+ break;
+ case LCK_UNLOCK:
+ type = "UNLOCK";
+ break;
+ default:
+ type = "unknown";
+ break;
+ }
+
+ switch (cmdl & LCK_SCOPE_MASK) {
+ case LCK_VG:
+ scope = "VG";
+ command = "LCK_VG";
+ break;
+ case LCK_LV:
+ scope = "LV";
+ switch (cmdl & LCK_MASK) {
+ case LCK_LV_EXCLUSIVE & LCK_MASK:
+ command = "LCK_LV_EXCLUSIVE";
+ break;
+ case LCK_LV_SUSPEND & LCK_MASK:
+ command = "LCK_LV_SUSPEND";
+ break;
+ case LCK_LV_RESUME & LCK_MASK:
+ command = "LCK_LV_RESUME";
+ break;
+ case LCK_LV_ACTIVATE & LCK_MASK:
+ command = "LCK_LV_ACTIVATE";
+ break;
+ case LCK_LV_DEACTIVATE & LCK_MASK:
+ command = "LCK_LV_DEACTIVATE";
+ break;
+ default:
+ command = "unknown";
+ break;
+ }
+ break;
+ default:
+ scope = "unknown";
+ command = "unknown";
+ break;
+ }
+
+ sprintf(buf, "0x%x %s (%s|%s%s%s%s%s%s)", cmdl, command, type, scope,
+ cmdl & LCK_NONBLOCK ? "|NONBLOCK" : "",
+ cmdl & LCK_HOLD ? "|HOLD" : "",
+ cmdl & LCK_LOCAL ? "|LOCAL" : "",
+ cmdl & LCK_CLUSTER_VG ? "|CLUSTER_VG" : "",
+ cmdl & LCK_CACHE ? "|CACHE" : "");
+
+ return buf;
+}
+
+/*
+ * Only processes 8 bits: excludes LCK_CACHE.
+ */
+static const char *decode_locking_cmd(unsigned char cmdl)
+{
+ return decode_full_locking_cmd((uint32_t) cmdl);
+}
+
+static const char *decode_flags(unsigned char flags)
+{
+ static char buf[128];
+ int len;
+
+ len = sprintf(buf, "0x%x ( %s%s%s%s%s)", flags,
+ flags & LCK_PARTIAL_MODE ? "PARTIAL_MODE|" : "",
+ flags & LCK_MIRROR_NOSYNC_MODE ? "MIRROR_NOSYNC|" : "",
+ flags & LCK_DMEVENTD_MONITOR_MODE ? "DMEVENTD_MONITOR|" : "",
+ flags & LCK_ORIGIN_ONLY_MODE ? "ORIGIN_ONLY|" : "",
+ flags & LCK_CONVERT ? "CONVERT|" : "");
+
+ if (len > 1)
+ buf[len - 2] = ' ';
+ else
+ buf[0] = '\0';
+
+ return buf;
+}
+
+char *get_last_lvm_error()
+{
+ return last_error;
+}
+
+/*
+ * Hash lock info helpers
+ */
+static struct lv_info *lookup_info(const char *resource)
+{
+ struct lv_info *lvi;
+
+ pthread_mutex_lock(&lv_hash_lock);
+ lvi = dm_hash_lookup(lv_hash, resource);
+ pthread_mutex_unlock(&lv_hash_lock);
+
+ return lvi;
+}
+
+static void insert_info(const char *resource, struct lv_info *lvi)
+{
+ pthread_mutex_lock(&lv_hash_lock);
+ dm_hash_insert(lv_hash, resource, lvi);
+ pthread_mutex_unlock(&lv_hash_lock);
+}
+
+static void remove_info(const char *resource)
+{
+ pthread_mutex_lock(&lv_hash_lock);
+ dm_hash_remove(lv_hash, resource);
+ pthread_mutex_unlock(&lv_hash_lock);
+}
+
+/*
+ * Return the mode a lock is currently held at (or -1 if not held)
+ */
+static int get_current_lock(char *resource)
+{
+ struct lv_info *lvi;
+
+ if ((lvi = lookup_info(resource)))
+ return lvi->lock_mode;
+
+ return -1;
+}
+
+
+void init_lvhash()
+{
+ /* Create hash table for keeping LV locks & status */
+ lv_hash = dm_hash_create(100);
+ pthread_mutex_init(&lv_hash_lock, NULL);
+ pthread_mutex_init(&lvm_lock, NULL);
+}
+
+/* Called at shutdown to tidy the lockspace */
+void destroy_lvhash()
+{
+ struct dm_hash_node *v;
+ struct lv_info *lvi;
+ char *resource;
+ int status;
+
+ pthread_mutex_lock(&lv_hash_lock);
+
+ dm_hash_iterate(v, lv_hash) {
+ lvi = dm_hash_get_data(lv_hash, v);
+ resource = dm_hash_get_key(lv_hash, v);
+
+ if ((status = sync_unlock(resource, lvi->lock_id)))
+ DEBUGLOG("unlock_all. unlock failed(%d): %s\n",
+ status, strerror(errno));
+ free(lvi);
+ }
+
+ dm_hash_destroy(lv_hash);
+ lv_hash = NULL;
+
+ pthread_mutex_unlock(&lv_hash_lock);
+}
+
+/* Gets a real lock and keeps the info in the hash table */
+static int hold_lock(char *resource, int mode, int flags)
+{
+ int status;
+ int saved_errno;
+ struct lv_info *lvi;
+
+ /* Mask off invalid options */
+ flags &= LCKF_NOQUEUE | LCKF_CONVERT;
+
+ lvi = lookup_info(resource);
+
+ if (lvi && lvi->lock_mode == mode) {
+ DEBUGLOG("hold_lock, lock mode %d already held\n", mode);
+ return 0;
+ }
+
+ /* Only allow explicit conversions */
+ if (lvi && !(flags & LCKF_CONVERT)) {
+ errno = EBUSY;
+ return -1;
+ }
+ if (lvi) {
+ /* Already exists - convert it */
+ status =
+ sync_lock(resource, mode, flags, &lvi->lock_id);
+ saved_errno = errno;
+ if (!status)
+ lvi->lock_mode = mode;
+
+ if (status) {
+ DEBUGLOG("hold_lock. convert to %d failed: %s\n", mode,
+ strerror(errno));
+ }
+ errno = saved_errno;
+ } else {
+ lvi = malloc(sizeof(struct lv_info));
+ if (!lvi)
+ return -1;
+
+ lvi->lock_mode = mode;
+ status = sync_lock(resource, mode, flags & ~LCKF_CONVERT, &lvi->lock_id);
+ saved_errno = errno;
+ if (status) {
+ free(lvi);
+ DEBUGLOG("hold_lock. lock at %d failed: %s\n", mode,
+ strerror(errno));
+ } else
+ insert_info(resource, lvi);
+
+ errno = saved_errno;
+ }
+ return status;
+}
+
+/* Unlock and remove it from the hash table */
+static int hold_unlock(char *resource)
+{
+ struct lv_info *lvi;
+ int status;
+ int saved_errno;
+
+ if (!(lvi = lookup_info(resource))) {
+ DEBUGLOG("hold_unlock, lock not already held\n");
+ return 0;
+ }
+
+ status = sync_unlock(resource, lvi->lock_id);
+ saved_errno = errno;
+ if (!status) {
+ remove_info(resource);
+ free(lvi);
+ } else {
+ DEBUGLOG("hold_unlock. unlock failed(%d): %s\n", status,
+ strerror(errno));
+ }
+
+ errno = saved_errno;
+ return status;
+}
+
+/* Watch the return codes here.
+ liblvm API functions return 1(true) for success, 0(false) for failure and don't set errno.
+ libdlm API functions return 0 for success, -1 for failure and do set errno.
+ These functions here return 0 for success or >0 for failure (where the retcode is errno)
+*/
+
+/* Activate LV exclusive or non-exclusive */
+static int do_activate_lv(char *resource, unsigned char lock_flags, int mode)
+{
+ int oldmode;
+ int status;
+ int activate_lv;
+ int exclusive = 0;
+ struct lvinfo lvi;
+
+ /* Is it already open ? */
+ oldmode = get_current_lock(resource);
+ if (oldmode == mode && (lock_flags & LCK_CLUSTER_VG)) {
+ DEBUGLOG("do_activate_lv, lock already held at %d\n", oldmode);
+ return 0; /* Nothing to do */
+ }
+
+ /* Does the config file want us to activate this LV ? */
+ if (!lv_activation_filter(cmd, resource, &activate_lv))
+ return EIO;
+
+ if (!activate_lv)
+ return 0; /* Success, we did nothing! */
+
+ /* Do we need to activate exclusively? */
+ if ((activate_lv == 2) || (mode == LCK_EXCL)) {
+ exclusive = 1;
+ mode = LCK_EXCL;
+ }
+
+ /*
+ * Try to get the lock if it's a clustered volume group.
+ * Use lock conversion only if requested, to prevent implicit conversion
+ * of exclusive lock to shared one during activation.
+ */
+ if (lock_flags & LCK_CLUSTER_VG) {
+ status = hold_lock(resource, mode, LCKF_NOQUEUE | (lock_flags & LCK_CONVERT ? LCKF_CONVERT:0));
+ if (status) {
+ /* Return an LVM-sensible error for this.
+ * Forcing EIO makes the upper level return this text
+ * rather than the strerror text for EAGAIN.
+ */
+ if (errno == EAGAIN) {
+ sprintf(last_error, "Volume is busy on another node");
+ errno = EIO;
+ }
+ return errno;
+ }
+ }
+
+ /* If it's suspended then resume it */
+ if (!lv_info_by_lvid(cmd, resource, 0, &lvi, 0, 0))
+ goto error;
+
+ if (lvi.suspended) {
+ memlock_inc(cmd);
+ if (!lv_resume(cmd, resource, 0)) {
+ memlock_dec(cmd);
+ goto error;
+ }
+ }
+
+ /* Now activate it */
+ if (!lv_activate(cmd, resource, exclusive))
+ goto error;
+
+ return 0;
+
+error:
+ if (oldmode == -1 || oldmode != mode)
+ (void)hold_unlock(resource);
+ return EIO;
+}
+
+/* Resume the LV if it was active */
+static int do_resume_lv(char *resource, unsigned char lock_flags)
+{
+ int oldmode;
+
+ /* Is it open ? */
+ oldmode = get_current_lock(resource);
+ if (oldmode == -1 && (lock_flags & LCK_CLUSTER_VG)) {
+ DEBUGLOG("do_resume_lv, lock not already held\n");
+ return 0; /* We don't need to do anything */
+ }
+
+ if (!lv_resume_if_active(cmd, resource, (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0))
+ return EIO;
+
+ return 0;
+}
+
+/* Suspend the device if active */
+static int do_suspend_lv(char *resource, unsigned char lock_flags)
+{
+ int oldmode;
+ struct lvinfo lvi;
+ unsigned origin_only = (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0;
+
+ /* Is it open ? */
+ oldmode = get_current_lock(resource);
+ if (oldmode == -1 && (lock_flags & LCK_CLUSTER_VG)) {
+ DEBUGLOG("do_suspend_lv, lock not already held\n");
+ return 0; /* Not active, so it's OK */
+ }
+
+ /* Only suspend it if it exists */
+ if (!lv_info_by_lvid(cmd, resource, origin_only, &lvi, 0, 0))
+ return EIO;
+
+ if (lvi.exists && !lv_suspend_if_active(cmd, resource, origin_only))
+ return EIO;
+
+ return 0;
+}
+
+static int do_deactivate_lv(char *resource, unsigned char lock_flags)
+{
+ int oldmode;
+ int status;
+
+ /* Is it open ? */
+ oldmode = get_current_lock(resource);
+ if (oldmode == -1 && (lock_flags & LCK_CLUSTER_VG)) {
+ DEBUGLOG("do_deactivate_lock, lock not already held\n");
+ return 0; /* We don't need to do anything */
+ }
+
+ if (!lv_deactivate(cmd, resource))
+ return EIO;
+
+ if (lock_flags & LCK_CLUSTER_VG) {
+ status = hold_unlock(resource);
+ if (status)
+ return errno;
+ }
+
+ return 0;
+}
+
+const char *do_lock_query(char *resource)
+{
+ int mode;
+ const char *type = NULL;
+
+ mode = get_current_lock(resource);
+ switch (mode) {
+ case LCK_NULL: type = "NL"; break;
+ case LCK_READ: type = "CR"; break;
+ case LCK_PREAD:type = "PR"; break;
+ case LCK_WRITE:type = "PW"; break;
+ case LCK_EXCL: type = "EX"; break;
+ }
+
+ DEBUGLOG("do_lock_query: resource '%s', mode %i (%s)\n", resource, mode, type ?: "?");
+
+ return type;
+}
+
+/* This is the LOCK_LV part that happens on all nodes in the cluster -
+ it is responsible for the interaction with device-mapper and LVM */
+int do_lock_lv(unsigned char command, unsigned char lock_flags, char *resource)
+{
+ int status = 0;
+
+ DEBUGLOG("do_lock_lv: resource '%s', cmd = %s, flags = %s, memlock = %d\n",
+ resource, decode_locking_cmd(command), decode_flags(lock_flags), memlock());
+
+ if (!cmd->config_valid || config_files_changed(cmd)) {
+ /* Reinitialise various settings inc. logging, filters */
+ if (do_refresh_cache()) {
+ log_error("Updated config file invalid. Aborting.");
+ return EINVAL;
+ }
+ }
+
+ pthread_mutex_lock(&lvm_lock);
+ if (lock_flags & LCK_MIRROR_NOSYNC_MODE)
+ init_mirror_in_sync(1);
+
+ if (lock_flags & LCK_DMEVENTD_MONITOR_MODE)
+ init_dmeventd_monitor(1);
+ else
+ init_dmeventd_monitor(0);
+
+ cmd->partial_activation = (lock_flags & LCK_PARTIAL_MODE) ? 1 : 0;
+
+ /* clvmd should never try to read suspended device */
+ init_ignore_suspended_devices(1);
+
+ switch (command & LCK_MASK) {
+ case LCK_LV_EXCLUSIVE:
+ status = do_activate_lv(resource, lock_flags, LCK_EXCL);
+ break;
+
+ case LCK_LV_SUSPEND:
+ status = do_suspend_lv(resource, lock_flags);
+ break;
+
+ case LCK_UNLOCK:
+ case LCK_LV_RESUME: /* if active */
+ status = do_resume_lv(resource, lock_flags);
+ break;
+
+ case LCK_LV_ACTIVATE:
+ status = do_activate_lv(resource, lock_flags, LCK_READ);
+ break;
+
+ case LCK_LV_DEACTIVATE:
+ status = do_deactivate_lv(resource, lock_flags);
+ break;
+
+ default:
+ DEBUGLOG("Invalid LV command 0x%x\n", command);
+ status = EINVAL;
+ break;
+ }
+
+ if (lock_flags & LCK_MIRROR_NOSYNC_MODE)
+ init_mirror_in_sync(0);
+
+ cmd->partial_activation = 0;
+
+ /* clean the pool for another command */
+ dm_pool_empty(cmd->mem);
+ pthread_mutex_unlock(&lvm_lock);
+
+ DEBUGLOG("Command return is %d, memlock is %d\n", status, memlock());
+ return status;
+}
+
+/* Functions to do on the local node only BEFORE the cluster-wide stuff above happens */
+int pre_lock_lv(unsigned char command, unsigned char lock_flags, char *resource)
+{
+ /* Nearly all the stuff happens cluster-wide. Apart from SUSPEND. Here we get the
+ lock out on this node (because we are the node modifying the metadata)
+ before suspending cluster-wide.
+ LCKF_CONVERT is used always, local node is going to modify metadata
+ */
+ if ((command & (LCK_SCOPE_MASK | LCK_TYPE_MASK)) == LCK_LV_SUSPEND &&
+ (lock_flags & LCK_CLUSTER_VG)) {
+ DEBUGLOG("pre_lock_lv: resource '%s', cmd = %s, flags = %s\n",
+ resource, decode_locking_cmd(command), decode_flags(lock_flags));
+
+ if (hold_lock(resource, LCK_WRITE, LCKF_NOQUEUE | LCKF_CONVERT))
+ return errno;
+ }
+ return 0;
+}
+
+/* Functions to do on the local node only AFTER the cluster-wide stuff above happens */
+int post_lock_lv(unsigned char command, unsigned char lock_flags,
+ char *resource)
+{
+ int status;
+ unsigned origin_only = (lock_flags & LCK_ORIGIN_ONLY_MODE) ? 1 : 0;
+
+ /* Opposite of above, done on resume after a metadata update */
+ if ((command & (LCK_SCOPE_MASK | LCK_TYPE_MASK)) == LCK_LV_RESUME &&
+ (lock_flags & LCK_CLUSTER_VG)) {
+ int oldmode;
+
+ DEBUGLOG
+ ("post_lock_lv: resource '%s', cmd = %s, flags = %s\n",
+ resource, decode_locking_cmd(command), decode_flags(lock_flags));
+
+ /* If the lock state is PW then restore it to what it was */
+ oldmode = get_current_lock(resource);
+ if (oldmode == LCK_WRITE) {
+ struct lvinfo lvi;
+
+ pthread_mutex_lock(&lvm_lock);
+ status = lv_info_by_lvid(cmd, resource, origin_only, &lvi, 0, 0);
+ pthread_mutex_unlock(&lvm_lock);
+ if (!status)
+ return EIO;
+
+ if (lvi.exists) {
+ if (hold_lock(resource, LCK_READ, LCKF_CONVERT))
+ return errno;
+ } else if (hold_unlock(resource))
+ return errno;
+ }
+ }
+ return 0;
+}
+
+/* Check if a VG is in use by LVM1 so we don't stomp on it */
+int do_check_lvm1(const char *vgname)
+{
+ int status;
+
+ status = check_lvm1_vg_inactive(cmd, vgname);
+
+ return status == 1 ? 0 : EBUSY;
+}
+
+int do_refresh_cache()
+{
+ DEBUGLOG("Refreshing context\n");
+ log_notice("Refreshing context");
+
+ pthread_mutex_lock(&lvm_lock);
+
+ if (!refresh_toolcontext(cmd)) {
+ pthread_mutex_unlock(&lvm_lock);
+ return -1;
+ }
+
+ init_full_scan_done(0);
+ init_ignore_suspended_devices(1);
+ lvmcache_label_scan(cmd, 2);
+ dm_pool_empty(cmd->mem);
+
+ pthread_mutex_unlock(&lvm_lock);
+
+ return 0;
+}
+
+
+/* Only called at gulm startup. Drop any leftover VG or P_orphan locks
+ that might be hanging around if we died for any reason
+*/
+static void drop_vg_locks(void)
+{
+ char vg[128];
+ char line[255];
+ FILE *vgs =
+ popen
+ (LVM_PATH " pvs --config 'log{command_names=0 prefix=\"\"}' --nolocking --noheadings -o vg_name", "r");
+
+ sync_unlock("P_" VG_ORPHANS, LCK_EXCL);
+ sync_unlock("P_" VG_GLOBAL, LCK_EXCL);
+
+ if (!vgs)
+ return;
+
+ while (fgets(line, sizeof(line), vgs)) {
+ char *vgend;
+ char *vgstart;
+
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = '\0';
+
+ vgstart = line + strspn(line, " ");
+ vgend = vgstart + strcspn(vgstart, " ");
+ *vgend = '\0';
+
+ if (strncmp(vgstart, "WARNING:", 8) == 0)
+ continue;
+
+ sprintf(vg, "V_%s", vgstart);
+ sync_unlock(vg, LCK_EXCL);
+
+ }
+ if (fclose(vgs))
+ DEBUGLOG("vgs fclose failed: %s\n", strerror(errno));
+}
+
+/*
+ * Handle VG lock - drop metadata or update lvmcache state
+ */
+void do_lock_vg(unsigned char command, unsigned char lock_flags, char *resource)
+{
+ uint32_t lock_cmd = command;
+ char *vgname = resource + 2;
+
+ lock_cmd &= (LCK_SCOPE_MASK | LCK_TYPE_MASK | LCK_HOLD);
+
+ /*
+ * Check if LCK_CACHE should be set. All P_ locks except # are cache related.
+ */
+ if (strncmp(resource, "P_#", 3) && !strncmp(resource, "P_", 2))
+ lock_cmd |= LCK_CACHE;
+
+ DEBUGLOG("do_lock_vg: resource '%s', cmd = %s, flags = %s, memlock = %d\n",
+ resource, decode_full_locking_cmd(lock_cmd), decode_flags(lock_flags), memlock());
+
+ /* P_#global causes a full cache refresh */
+ if (!strcmp(resource, "P_" VG_GLOBAL)) {
+ do_refresh_cache();
+ return;
+ }
+
+ pthread_mutex_lock(&lvm_lock);
+ switch (lock_cmd) {
+ case LCK_VG_COMMIT:
+ DEBUGLOG("vg_commit notification for VG %s\n", vgname);
+ lvmcache_commit_metadata(vgname);
+ break;
+ case LCK_VG_REVERT:
+ DEBUGLOG("vg_revert notification for VG %s\n", vgname);
+ lvmcache_drop_metadata(vgname, 1);
+ break;
+ case LCK_VG_DROP_CACHE:
+ default:
+ DEBUGLOG("Invalidating cached metadata for VG %s\n", vgname);
+ lvmcache_drop_metadata(vgname, 0);
+ }
+ pthread_mutex_unlock(&lvm_lock);
+}
+
+/*
+ * Compare the uuid with the list of exclusive locks that clvmd
+ * held before it was restarted, so we can get the right kind
+ * of lock now we are restarting.
+ */
+static int was_ex_lock(char *uuid, char **argv)
+{
+ int optnum = 0;
+ char *opt = argv[optnum];
+
+ while (opt) {
+ if (strcmp(opt, "-E") == 0) {
+ opt = argv[++optnum];
+ if (opt && (strcmp(opt, uuid) == 0)) {
+ DEBUGLOG("Lock %s is exclusive\n", uuid);
+ return 1;
+ }
+ }
+ opt = argv[++optnum];
+ }
+ return 0;
+}
+
+/*
+ * Ideally, clvmd should be started before any LVs are active
+ * but this may not be the case...
+ * I suppose this also comes in handy if clvmd crashes, not that it would!
+ */
+static void *get_initial_state(char **argv)
+{
+ int lock_mode;
+ char lv[64], vg[64], flags[25], vg_flags[25];
+ char uuid[65];
+ char line[255];
+ FILE *lvs =
+ popen
+ (LVM_PATH " lvs --config 'log{command_names=0 prefix=\"\"}' --nolocking --noheadings -o vg_uuid,lv_uuid,lv_attr,vg_attr",
+ "r");
+
+ if (!lvs)
+ return NULL;
+
+ while (fgets(line, sizeof(line), lvs)) {
+ if (sscanf(line, "%s %s %s %s\n", vg, lv, flags, vg_flags) == 4) {
+
+ /* States: s:suspended a:active S:dropped snapshot I:invalid snapshot */
+ if (strlen(vg) == 38 && /* is is a valid UUID ? */
+ (flags[4] == 'a' || flags[4] == 's') && /* is it active or suspended? */
+ vg_flags[5] == 'c') { /* is it clustered ? */
+ /* Convert hyphen-separated UUIDs into one */
+ memcpy(&uuid[0], &vg[0], 6);
+ memcpy(&uuid[6], &vg[7], 4);
+ memcpy(&uuid[10], &vg[12], 4);
+ memcpy(&uuid[14], &vg[17], 4);
+ memcpy(&uuid[18], &vg[22], 4);
+ memcpy(&uuid[22], &vg[27], 4);
+ memcpy(&uuid[26], &vg[32], 6);
+ memcpy(&uuid[32], &lv[0], 6);
+ memcpy(&uuid[38], &lv[7], 4);
+ memcpy(&uuid[42], &lv[12], 4);
+ memcpy(&uuid[46], &lv[17], 4);
+ memcpy(&uuid[50], &lv[22], 4);
+ memcpy(&uuid[54], &lv[27], 4);
+ memcpy(&uuid[58], &lv[32], 6);
+ uuid[64] = '\0';
+
+ lock_mode = LCK_READ;
+
+ /* Look for this lock in the list of EX locks
+ we were passed on the command-line */
+ if (was_ex_lock(uuid, argv))
+ lock_mode = LCK_EXCL;
+
+ DEBUGLOG("getting initial lock for %s\n", uuid);
+ hold_lock(uuid, lock_mode, LCKF_NOQUEUE);
+ }
+ }
+ }
+ if (fclose(lvs))
+ DEBUGLOG("lvs fclose failed: %s\n", strerror(errno));
+ return NULL;
+}
+
+static void lvm2_log_fn(int level, const char *file, int line, int dm_errno,
+ const char *message)
+{
+
+ /* Send messages to the normal LVM2 logging system too,
+ so we get debug output when it's asked for.
+ We need to NULL the function ptr otherwise it will just call
+ back into here! */
+ init_log_fn(NULL);
+ print_log(level, file, line, dm_errno, "%s", message);
+ init_log_fn(lvm2_log_fn);
+
+ /*
+ * Ignore non-error messages, but store the latest one for returning
+ * to the user.
+ */
+ if (level != _LOG_ERR && level != _LOG_FATAL)
+ return;
+
+ strncpy(last_error, message, sizeof(last_error));
+ last_error[sizeof(last_error)-1] = '\0';
+}
+
+/* This checks some basic cluster-LVM configuration stuff */
+static void check_config(void)
+{
+ int locking_type;
+
+ locking_type = find_config_tree_int(cmd, "global/locking_type", 1);
+
+ if (locking_type == 3) /* compiled-in cluster support */
+ return;
+
+ if (locking_type == 2) { /* External library, check name */
+ const char *libname;
+
+ libname = find_config_tree_str(cmd, "global/locking_library",
+ "");
+ if (strstr(libname, "liblvm2clusterlock.so"))
+ return;
+
+ log_error("Incorrect LVM locking library specified in lvm.conf, cluster operations may not work.");
+ return;
+ }
+ log_error("locking_type not set correctly in lvm.conf, cluster operations will not work.");
+}
+
+/* Backups up the LVM metadata if it's changed */
+void lvm_do_backup(const char *vgname)
+{
+ struct volume_group * vg;
+ int consistent = 0;
+
+ DEBUGLOG("Triggering backup of VG metadata for %s.\n", vgname);
+
+ pthread_mutex_lock(&lvm_lock);
+
+ vg = vg_read_internal(cmd, vgname, NULL /*vgid*/, 1, &consistent);
+
+ if (vg && consistent)
+ check_current_backup(vg);
+ else
+ log_error("Error backing up metadata, can't find VG for group %s", vgname);
+
+ free_vg(vg);
+ dm_pool_empty(cmd->mem);
+
+ pthread_mutex_unlock(&lvm_lock);
+}
+
+struct dm_hash_node *get_next_excl_lock(struct dm_hash_node *v, char **name)
+{
+ struct lv_info *lvi;
+
+ *name = NULL;
+ if (!v)
+ v = dm_hash_get_first(lv_hash);
+
+ do {
+ if (v) {
+ lvi = dm_hash_get_data(lv_hash, v);
+ DEBUGLOG("Looking for EX locks. found %x mode %d\n", lvi->lock_id, lvi->lock_mode);
+
+ if (lvi->lock_mode == LCK_EXCL) {
+ *name = dm_hash_get_key(lv_hash, v);
+ }
+ v = dm_hash_get_next(lv_hash, v);
+ }
+ } while (v && !*name);
+
+ if (*name)
+ DEBUGLOG("returning EXclusive UUID %s\n", *name);
+ return v;
+}
+
+/* Called to initialise the LVM context of the daemon */
+int init_clvm(int using_gulm, char **argv)
+{
+ if (!(cmd = create_toolcontext(1, NULL))) {
+ log_error("Failed to allocate command context");
+ return 0;
+ }
+
+ if (stored_errno()) {
+ destroy_toolcontext(cmd);
+ return 0;
+ }
+
+ /* Use LOG_DAEMON for syslog messages instead of LOG_USER */
+ init_syslog(LOG_DAEMON);
+ openlog("clvmd", LOG_PID, LOG_DAEMON);
+ cmd->cmd_line = "clvmd";
+
+ /* Check lvm.conf is setup for cluster-LVM */
+ check_config();
+ init_ignore_suspended_devices(1);
+
+ /* Remove any non-LV locks that may have been left around */
+ if (using_gulm)
+ drop_vg_locks();
+
+ get_initial_state(argv);
+
+ /* Trap log messages so we can pass them back to the user */
+ init_log_fn(lvm2_log_fn);
+ memlock_inc_daemon(cmd);
+
+ return 1;
+}
+
+void destroy_lvm(void)
+{
+ if (cmd) {
+ memlock_dec_daemon(cmd);
+ destroy_toolcontext(cmd);
+ }
+ cmd = NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Functions in lvm-functions.c */
+
+#ifndef _LVM_FUNCTIONS_H
+#define _LVM_FUNCTIONS_H
+
+extern int pre_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
+ char *resource);
+extern int do_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
+ char *resource);
+extern const char *do_lock_query(char *resource);
+extern int post_lock_lv(unsigned char lock_cmd, unsigned char lock_flags,
+ char *resource);
+extern int do_check_lvm1(const char *vgname);
+extern int do_refresh_cache(void);
+extern int init_clvm(int using_gulm, char **argv);
+extern void destroy_lvm(void);
+extern void init_lvhash(void);
+extern void destroy_lvhash(void);
+extern void lvm_do_backup(const char *vgname);
+extern char *get_last_lvm_error(void);
+extern void do_lock_vg(unsigned char command, unsigned char lock_flags,
+ char *resource);
+extern struct dm_hash_node *get_next_excl_lock(struct dm_hash_node *v, char **name);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Send a command to a running clvmd from the command-line
+ */
+
+#include "clvmd-common.h"
+
+#include "clvm.h"
+#include "refresh_clvmd.h"
+
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+typedef struct lvm_response {
+ char node[255];
+ char *response;
+ int status;
+ int len;
+} lvm_response_t;
+
+/*
+ * This gets stuck at the start of memory we allocate so we
+ * can sanity-check it at deallocation time
+ */
+#define LVM_SIGNATURE 0x434C564D
+
+static int _clvmd_sock = -1;
+
+/* Open connection to the clvm daemon */
+static int _open_local_sock(void)
+{
+ int local_socket;
+ struct sockaddr_un sockaddr;
+
+ /* Open local socket */
+ if ((local_socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ fprintf(stderr, "Local socket creation failed: %s", strerror(errno));
+ return -1;
+ }
+
+ memset(&sockaddr, 0, sizeof(sockaddr));
+ memcpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(CLVMD_SOCKNAME));
+
+ sockaddr.sun_family = AF_UNIX;
+
+ if (connect(local_socket,(struct sockaddr *) &sockaddr,
+ sizeof(sockaddr))) {
+ int saved_errno = errno;
+
+ fprintf(stderr, "connect() failed on local socket: %s\n",
+ strerror(errno));
+ if (close(local_socket))
+ return -1;
+
+ errno = saved_errno;
+ return -1;
+ }
+
+ return local_socket;
+}
+
+/* Send a request and return the status */
+static int _send_request(const char *inbuf, int inlen, char **retbuf, int no_response)
+{
+ char outbuf[PIPE_BUF];
+ struct clvm_header *outheader = (struct clvm_header *) outbuf;
+ int len;
+ int off;
+ int buflen;
+ int err;
+
+ /* Send it to CLVMD */
+ rewrite:
+ if ( (err = write(_clvmd_sock, inbuf, inlen)) != inlen) {
+ if (err == -1 && errno == EINTR)
+ goto rewrite;
+ fprintf(stderr, "Error writing data to clvmd: %s", strerror(errno));
+ return 0;
+ }
+ if (no_response)
+ return 1;
+
+ /* Get the response */
+ reread:
+ if ((len = read(_clvmd_sock, outbuf, sizeof(struct clvm_header))) < 0) {
+ if (errno == EINTR)
+ goto reread;
+ fprintf(stderr, "Error reading data from clvmd: %s", strerror(errno));
+ return 0;
+ }
+
+ if (len == 0) {
+ fprintf(stderr, "EOF reading CLVMD");
+ errno = ENOTCONN;
+ return 0;
+ }
+
+ /* Allocate buffer */
+ buflen = len + outheader->arglen;
+ *retbuf = dm_malloc(buflen);
+ if (!*retbuf) {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ /* Copy the header */
+ memcpy(*retbuf, outbuf, len);
+ outheader = (struct clvm_header *) *retbuf;
+
+ /* Read the returned values */
+ off = 1; /* we've already read the first byte */
+ while (off <= outheader->arglen && len > 0) {
+ len = read(_clvmd_sock, outheader->args + off,
+ buflen - off - offsetof(struct clvm_header, args));
+ if (len > 0)
+ off += len;
+ }
+
+ /* Was it an error ? */
+ if (outheader->status != 0) {
+ errno = outheader->status;
+
+ /* Only return an error here if there are no node-specific
+ errors present in the message that might have more detail */
+ if (!(outheader->flags & CLVMD_FLAG_NODEERRS)) {
+ fprintf(stderr, "cluster request failed: %s\n", strerror(errno));
+ return 0;
+ }
+
+ }
+
+ return 1;
+}
+
+/* Build the structure header and parse-out wildcard node names */
+static void _build_header(struct clvm_header *head, int cmd, const char *node,
+ int len)
+{
+ head->cmd = cmd;
+ head->status = 0;
+ head->flags = 0;
+ head->clientid = 0;
+ head->arglen = len;
+
+ if (node) {
+ /*
+ * Allow a couple of special node names:
+ * "*" for all nodes,
+ * "." for the local node only
+ */
+ if (strcmp(node, "*") == 0) {
+ head->node[0] = '\0';
+ } else if (strcmp(node, ".") == 0) {
+ head->node[0] = '\0';
+ head->flags = CLVMD_FLAG_LOCAL;
+ } else
+ strcpy(head->node, node);
+ } else
+ head->node[0] = '\0';
+}
+
+/*
+ * Send a message to a(or all) node(s) in the cluster and wait for replies
+ */
+static int _cluster_request(char cmd, const char *node, void *data, int len,
+ lvm_response_t ** response, int *num, int no_response)
+{
+ char outbuf[sizeof(struct clvm_header) + len + strlen(node) + 1];
+ char *inptr;
+ char *retbuf = NULL;
+ int status;
+ int i;
+ int num_responses = 0;
+ struct clvm_header *head = (struct clvm_header *) outbuf;
+ lvm_response_t *rarray;
+
+ *num = 0;
+
+ if (_clvmd_sock == -1)
+ _clvmd_sock = _open_local_sock();
+
+ if (_clvmd_sock == -1)
+ return 0;
+
+ _build_header(head, cmd, node, len);
+ memcpy(head->node + strlen(head->node) + 1, data, len);
+
+ status = _send_request(outbuf, sizeof(struct clvm_header) +
+ strlen(head->node) + len, &retbuf, no_response);
+ if (!status || no_response)
+ goto out;
+
+ /* Count the number of responses we got */
+ head = (struct clvm_header *) retbuf;
+ inptr = head->args;
+ while (inptr[0]) {
+ num_responses++;
+ inptr += strlen(inptr) + 1;
+ inptr += sizeof(int);
+ inptr += strlen(inptr) + 1;
+ }
+
+ /*
+ * Allocate response array.
+ * With an extra pair of INTs on the front to sanity
+ * check the pointer when we are given it back to free
+ */
+ *response = dm_malloc(sizeof(lvm_response_t) * num_responses +
+ sizeof(int) * 2);
+ if (!*response) {
+ errno = ENOMEM;
+ status = 0;
+ goto out;
+ }
+
+ rarray = *response;
+
+ /* Unpack the response into an lvm_response_t array */
+ inptr = head->args;
+ i = 0;
+ while (inptr[0]) {
+ strcpy(rarray[i].node, inptr);
+ inptr += strlen(inptr) + 1;
+
+ memcpy(&rarray[i].status, inptr, sizeof(int));
+ inptr += sizeof(int);
+
+ rarray[i].response = dm_malloc(strlen(inptr) + 1);
+ if (rarray[i].response == NULL) {
+ /* Free up everything else and return error */
+ int j;
+ for (j = 0; j < i; j++)
+ dm_free(rarray[i].response);
+ free(*response);
+ errno = ENOMEM;
+ status = -1;
+ goto out;
+ }
+
+ strcpy(rarray[i].response, inptr);
+ rarray[i].len = strlen(inptr);
+ inptr += strlen(inptr) + 1;
+ i++;
+ }
+ *num = num_responses;
+ *response = rarray;
+
+ out:
+ if (retbuf)
+ dm_free(retbuf);
+
+ return status;
+}
+
+/* Free reply array */
+static int _cluster_free_request(lvm_response_t * response, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ dm_free(response[i].response);
+ }
+
+ dm_free(response);
+
+ return 1;
+}
+
+int refresh_clvmd(int all_nodes)
+{
+ int num_responses;
+ char args[1]; // No args really.
+ lvm_response_t *response = NULL;
+ int saved_errno;
+ int status;
+ int i;
+
+ status = _cluster_request(CLVMD_CMD_REFRESH, all_nodes?"*":".", args, 0, &response, &num_responses, 0);
+
+ /* If any nodes were down then display them and return an error */
+ for (i = 0; i < num_responses; i++) {
+ if (response[i].status == EHOSTDOWN) {
+ fprintf(stderr, "clvmd not running on node %s",
+ response[i].node);
+ status = 0;
+ errno = response[i].status;
+ } else if (response[i].status) {
+ fprintf(stderr, "Error resetting node %s: %s",
+ response[i].node,
+ response[i].response[0] ?
+ response[i].response :
+ strerror(response[i].status));
+ status = 0;
+ errno = response[i].status;
+ }
+ }
+
+ saved_errno = errno;
+ _cluster_free_request(response, num_responses);
+ errno = saved_errno;
+
+ return status;
+}
+
+int restart_clvmd(int all_nodes)
+{
+ int dummy, status;
+
+ status = _cluster_request(CLVMD_CMD_RESTART, all_nodes?"*":".", NULL, 0, NULL, &dummy, 1);
+
+ /*
+ * FIXME: we cannot receive response, clvmd re-exec before it.
+ * but also should not close socket too early (the whole rq is dropped then).
+ * FIXME: This should be handled this way:
+ * - client waits for RESTART ack (and socket close)
+ * - server restarts
+ * - client checks that server is ready again (VERSION command?)
+ */
+ usleep(500000);
+
+ return status;
+}
+
+int debug_clvmd(int level, int clusterwide)
+{
+ int num_responses;
+ char args[1];
+ const char *nodes;
+ lvm_response_t *response = NULL;
+ int saved_errno;
+ int status;
+ int i;
+
+ args[0] = level;
+ if (clusterwide)
+ nodes = "*";
+ else
+ nodes = ".";
+
+ status = _cluster_request(CLVMD_CMD_SET_DEBUG, nodes, args, 1, &response, &num_responses, 0);
+
+ /* If any nodes were down then display them and return an error */
+ for (i = 0; i < num_responses; i++) {
+ if (response[i].status == EHOSTDOWN) {
+ fprintf(stderr, "clvmd not running on node %s",
+ response[i].node);
+ status = 0;
+ errno = response[i].status;
+ } else if (response[i].status) {
+ fprintf(stderr, "Error setting debug on node %s: %s",
+ response[i].node,
+ response[i].response[0] ?
+ response[i].response :
+ strerror(response[i].status));
+ status = 0;
+ errno = response[i].status;
+ }
+ }
+
+ saved_errno = errno;
+ _cluster_free_request(response, num_responses);
+ errno = saved_errno;
+
+ return status;
+}
--- /dev/null
+/*
+ * Copyright (C) 2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+int refresh_clvmd(int all_nodes);
+int restart_clvmd(int all_nodes);
+int debug_clvmd(int level, int clusterwide);
+
--- /dev/null
+/*
+ * Copyright (C) 2002-2003 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This provides the inter-clvmd communications for a system without CMAN.
+ * There is a listening TCP socket which accepts new connections in the
+ * normal way.
+ * It can also make outgoing connnections to the other clvmd nodes.
+ */
+
+#include "clvmd-common.h"
+
+#include <pthread.h>
+#include <sys/utsname.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <errno.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <assert.h>
+
+#include "clvm.h"
+#include "clvmd-comms.h"
+#include "clvmd.h"
+#include "clvmd-gulm.h"
+
+#define DEFAULT_TCP_PORT 21064
+
+static int listen_fd = -1;
+static int tcp_port;
+struct dm_hash_table *sock_hash;
+
+static int get_our_ip_address(char *addr, int *family);
+static int read_from_tcpsock(struct local_client *fd, char *buf, int len, char *csid,
+ struct local_client **new_client);
+
+/* Called by init_cluster() to open up the listening socket */
+int init_comms(unsigned short port)
+{
+ struct sockaddr_in6 addr;
+
+ sock_hash = dm_hash_create(100);
+ tcp_port = port ? : DEFAULT_TCP_PORT;
+
+ listen_fd = socket(AF_INET6, SOCK_STREAM, 0);
+
+ if (listen_fd < 0)
+ {
+ return -1;
+ }
+ else
+ {
+ int one = 1;
+ setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
+ setsockopt(listen_fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(int));
+ }
+
+ memset(&addr, 0, sizeof(addr)); // Bind to INADDR_ANY
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(tcp_port);
+
+ if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ {
+ DEBUGLOG("Can't bind to port: %s\n", strerror(errno));
+ syslog(LOG_ERR, "Can't bind to port %d, is clvmd already running ?", tcp_port);
+ close(listen_fd);
+ return -1;
+ }
+
+ listen(listen_fd, 5);
+
+ /* Set Close-on-exec */
+ fcntl(listen_fd, F_SETFD, 1);
+
+ return 0;
+}
+
+void tcp_remove_client(const char *c_csid)
+{
+ struct local_client *client;
+ char csid[GULM_MAX_CSID_LEN];
+ unsigned int i;
+ memcpy(csid, c_csid, sizeof csid);
+ DEBUGLOG("tcp_remove_client\n");
+
+ /* Don't actually close the socket here - that's the
+ job of clvmd.c whch will do the job when it notices the
+ other end has gone. We just need to remove the client(s) from
+ the hash table so we don't try to use it for sending any more */
+ for (i = 0; i < 2; i++)
+ {
+ client = dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
+ if (client)
+ {
+ dm_hash_remove_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
+ client->removeme = 1;
+ close(client->fd);
+ }
+ /* Look for a mangled one too, on the 2nd iteration. */
+ csid[0] ^= 0x80;
+ }
+}
+
+int alloc_client(int fd, const char *c_csid, struct local_client **new_client)
+{
+ struct local_client *client;
+ char csid[GULM_MAX_CSID_LEN];
+ memcpy(csid, c_csid, sizeof csid);
+
+ DEBUGLOG("alloc_client %d csid = %s\n", fd, print_csid(csid));
+
+ /* Create a local_client and return it */
+ client = malloc(sizeof(struct local_client));
+ if (!client)
+ {
+ DEBUGLOG("malloc failed\n");
+ return -1;
+ }
+
+ memset(client, 0, sizeof(struct local_client));
+ client->fd = fd;
+ client->type = CLUSTER_DATA_SOCK;
+ client->callback = read_from_tcpsock;
+ if (new_client)
+ *new_client = client;
+
+ /* Add to our list of node sockets */
+ if (dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN))
+ {
+ DEBUGLOG("alloc_client mangling CSID for second connection\n");
+ /* This is a duplicate connection but we can't close it because
+ the other end may already have started sending.
+ So, we mangle the IP address and keep it, all sending will
+ go out of the main FD
+ */
+ csid[0] ^= 0x80;
+ client->bits.net.flags = 1; /* indicate mangled CSID */
+
+ /* If it still exists then kill the connection as we should only
+ ever have one incoming connection from each node */
+ if (dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN))
+ {
+ DEBUGLOG("Multiple incoming connections from node\n");
+ syslog(LOG_ERR, " Bogus incoming connection from %d.%d.%d.%d\n", csid[0],csid[1],csid[2],csid[3]);
+
+ free(client);
+ errno = ECONNREFUSED;
+ return -1;
+ }
+ }
+ dm_hash_insert_binary(sock_hash, csid, GULM_MAX_CSID_LEN, client);
+
+ return 0;
+}
+
+int get_main_gulm_cluster_fd()
+{
+ return listen_fd;
+}
+
+
+/* Read on main comms (listen) socket, accept it */
+int cluster_fd_gulm_callback(struct local_client *fd, char *buf, int len, const char *csid,
+ struct local_client **new_client)
+{
+ int newfd;
+ struct sockaddr_in6 addr;
+ socklen_t addrlen = sizeof(addr);
+ int status;
+ char name[GULM_MAX_CLUSTER_MEMBER_NAME_LEN];
+
+ DEBUGLOG("cluster_fd_callback\n");
+ *new_client = NULL;
+ newfd = accept(listen_fd, (struct sockaddr *)&addr, &addrlen);
+
+ DEBUGLOG("cluster_fd_callback, newfd=%d (errno=%d)\n", newfd, errno);
+ if (!newfd)
+ {
+ syslog(LOG_ERR, "error in accept: %m");
+ errno = EAGAIN;
+ return -1; /* Don't return an error or clvmd will close the listening FD */
+ }
+
+ /* Check that the client is a member of the cluster
+ and reject if not.
+ */
+ if (gulm_name_from_csid((char *)&addr.sin6_addr, name) < 0)
+ {
+ syslog(LOG_ERR, "Got connect from non-cluster node %s\n",
+ print_csid((char *)&addr.sin6_addr));
+ DEBUGLOG("Got connect from non-cluster node %s\n",
+ print_csid((char *)&addr.sin6_addr));
+ close(newfd);
+
+ errno = EAGAIN;
+ return -1;
+ }
+
+ status = alloc_client(newfd, (char *)&addr.sin6_addr, new_client);
+ if (status)
+ {
+ DEBUGLOG("cluster_fd_callback, alloc_client failed, status = %d\n", status);
+ close(newfd);
+ /* See above... */
+ errno = EAGAIN;
+ return -1;
+ }
+ DEBUGLOG("cluster_fd_callback, returning %d, %p\n", newfd, *new_client);
+ return newfd;
+}
+
+/* Try to get at least 'len' bytes from the socket */
+static int really_read(int fd, char *buf, int len)
+{
+ int got, offset;
+
+ got = offset = 0;
+
+ do {
+ got = read(fd, buf+offset, len-offset);
+ DEBUGLOG("really_read. got %d bytes\n", got);
+ offset += got;
+ } while (got > 0 && offset < len);
+
+ if (got < 0)
+ return got;
+ else
+ return offset;
+}
+
+
+static int read_from_tcpsock(struct local_client *client, char *buf, int len, char *csid,
+ struct local_client **new_client)
+{
+ struct sockaddr_in6 addr;
+ socklen_t slen = sizeof(addr);
+ struct clvm_header *header = (struct clvm_header *)buf;
+ int status;
+ uint32_t arglen;
+
+ DEBUGLOG("read_from_tcpsock fd %d\n", client->fd);
+ *new_client = NULL;
+
+ /* Get "csid" */
+ getpeername(client->fd, (struct sockaddr *)&addr, &slen);
+ memcpy(csid, &addr.sin6_addr, GULM_MAX_CSID_LEN);
+
+ /* Read just the header first, then get the rest if there is any.
+ * Stream sockets, sigh.
+ */
+ status = really_read(client->fd, buf, sizeof(struct clvm_header));
+ if (status > 0)
+ {
+ int status2;
+
+ arglen = ntohl(header->arglen);
+
+ /* Get the rest */
+ if (arglen && arglen < GULM_MAX_CLUSTER_MESSAGE)
+ {
+ status2 = really_read(client->fd, buf+status, arglen);
+ if (status2 > 0)
+ status += status2;
+ else
+ status = status2;
+ }
+ }
+
+ DEBUGLOG("read_from_tcpsock, status = %d(errno = %d)\n", status, errno);
+
+ /* Remove it from the hash table if there's an error, clvmd will
+ remove the socket from its lists and free the client struct */
+ if (status == 0 ||
+ (status < 0 && errno != EAGAIN && errno != EINTR))
+ {
+ char remcsid[GULM_MAX_CSID_LEN];
+
+ memcpy(remcsid, csid, GULM_MAX_CSID_LEN);
+ close(client->fd);
+
+ /* If the csid was mangled, then make sure we remove the right entry */
+ if (client->bits.net.flags)
+ remcsid[0] ^= 0x80;
+ dm_hash_remove_binary(sock_hash, remcsid, GULM_MAX_CSID_LEN);
+
+ /* Tell cluster manager layer */
+ add_down_node(remcsid);
+ }
+ else {
+ gulm_add_up_node(csid);
+ /* Send it back to clvmd */
+ process_message(client, buf, status, csid);
+ }
+ return status;
+}
+
+int gulm_connect_csid(const char *csid, struct local_client **newclient)
+{
+ int fd;
+ struct sockaddr_in6 addr;
+ int status;
+ int one = 1;
+
+ DEBUGLOG("Connecting socket\n");
+ fd = socket(PF_INET6, SOCK_STREAM, 0);
+
+ if (fd < 0)
+ {
+ syslog(LOG_ERR, "Unable to create new socket: %m");
+ return -1;
+ }
+
+ addr.sin6_family = AF_INET6;
+ memcpy(&addr.sin6_addr, csid, GULM_MAX_CSID_LEN);
+ addr.sin6_port = htons(tcp_port);
+
+ DEBUGLOG("Connecting socket %d\n", fd);
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in6)) < 0)
+ {
+ /* "Connection refused" is "normal" because clvmd may not yet be running
+ * on that node.
+ */
+ if (errno != ECONNREFUSED)
+ {
+ syslog(LOG_ERR, "Unable to connect to remote node: %m");
+ }
+ DEBUGLOG("Unable to connect to remote node: %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ /* Set Close-on-exec */
+ fcntl(fd, F_SETFD, 1);
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(int));
+
+ status = alloc_client(fd, csid, newclient);
+ if (status)
+ close(fd);
+ else
+ add_client(*newclient);
+
+ /* If we can connect to it, it must be running a clvmd */
+ gulm_add_up_node(csid);
+ return status;
+}
+
+/* Send a message to a known CSID */
+static int tcp_send_message(void *buf, int msglen, const char *csid, const char *errtext)
+{
+ int status;
+ struct local_client *client;
+ char ourcsid[GULM_MAX_CSID_LEN];
+
+ assert(csid);
+
+ DEBUGLOG("tcp_send_message, csid = %s, msglen = %d\n", print_csid(csid), msglen);
+
+ /* Don't connect to ourself */
+ get_our_gulm_csid(ourcsid);
+ if (memcmp(csid, ourcsid, GULM_MAX_CSID_LEN) == 0)
+ return msglen;
+
+ client = dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
+ if (!client)
+ {
+ status = gulm_connect_csid(csid, &client);
+ if (status)
+ return -1;
+ }
+ DEBUGLOG("tcp_send_message, fd = %d\n", client->fd);
+
+ return write(client->fd, buf, msglen);
+}
+
+
+int gulm_cluster_send_message(void *buf, int msglen, const char *csid, const char *errtext)
+{
+ int status=0;
+
+ DEBUGLOG("cluster send message, csid = %p, msglen = %d\n", csid, msglen);
+
+ /* If csid is NULL then send to all known (not just connected) nodes */
+ if (!csid)
+ {
+ void *context = NULL;
+ char loop_csid[GULM_MAX_CSID_LEN];
+
+ /* Loop round all gulm-known nodes */
+ while (get_next_node_csid(&context, loop_csid))
+ {
+ status = tcp_send_message(buf, msglen, loop_csid, errtext);
+ if (status == 0 ||
+ (status < 0 && (errno == EAGAIN || errno == EINTR)))
+ break;
+ }
+ }
+ else
+ {
+
+ status = tcp_send_message(buf, msglen, csid, errtext);
+ }
+ return status;
+}
+
+/* To get our own IP address we get the locally bound address of the
+ socket that's talking to GULM in the assumption(eek) that it will
+ be on the "right" network in a multi-homed system */
+static int get_our_ip_address(char *addr, int *family)
+{
+ struct utsname info;
+
+ uname(&info);
+ get_ip_address(info.nodename, addr);
+
+ return 0;
+}
+
+/* Public version of above for those that don't care what protocol
+ we're using */
+void get_our_gulm_csid(char *csid)
+{
+ static char our_csid[GULM_MAX_CSID_LEN];
+ static int got_csid = 0;
+
+ if (!got_csid)
+ {
+ int family;
+
+ memset(our_csid, 0, sizeof(our_csid));
+ if (get_our_ip_address(our_csid, &family))
+ {
+ got_csid = 1;
+ }
+ }
+ memcpy(csid, our_csid, GULM_MAX_CSID_LEN);
+}
+
+static void map_v4_to_v6(struct in_addr *ip4, struct in6_addr *ip6)
+{
+ ip6->s6_addr32[0] = 0;
+ ip6->s6_addr32[1] = 0;
+ ip6->s6_addr32[2] = htonl(0xffff);
+ ip6->s6_addr32[3] = ip4->s_addr;
+}
+
+/* Get someone else's IP address from DNS */
+int get_ip_address(const char *node, char *addr)
+{
+ struct hostent *he;
+
+ memset(addr, 0, GULM_MAX_CSID_LEN);
+
+ // TODO: what do we do about multi-homed hosts ???
+ // CCSs ip_interfaces solved this but some bugger removed it.
+
+ /* Try IPv6 first. The man page for gethostbyname implies that
+ it will lookup ip6 & ip4 names, but it seems not to */
+ he = gethostbyname2(node, AF_INET6);
+ if (he)
+ {
+ memcpy(addr, he->h_addr_list[0],
+ he->h_length);
+ }
+ else
+ {
+ he = gethostbyname2(node, AF_INET);
+ if (!he)
+ return -1;
+ map_v4_to_v6((struct in_addr *)he->h_addr_list[0], (struct in6_addr *)addr);
+ }
+
+ return 0;
+}
+
+char *print_csid(const char *csid)
+{
+ static char buf[128];
+ int *icsid = (int *)csid;
+
+ sprintf(buf, "[%x.%x.%x.%x]",
+ icsid[0],icsid[1],icsid[2],icsid[3]);
+
+ return buf;
+}
--- /dev/null
+#include <netinet/in.h>
+
+#define GULM_MAX_CLUSTER_MESSAGE 1600
+#define GULM_MAX_CSID_LEN sizeof(struct in6_addr)
+#define GULM_MAX_CLUSTER_MEMBER_NAME_LEN 128
+
+extern int init_comms(unsigned short);
+extern char *print_csid(const char *);
+int get_main_gulm_cluster_fd(void);
+int cluster_fd_gulm_callback(struct local_client *fd, char *buf, int len, const char *csid, struct local_client **new_client);
+int gulm_cluster_send_message(void *buf, int msglen, const char *csid, const char *errtext);
+void get_our_gulm_csid(char *csid);
+int gulm_connect_csid(const char *csid, struct local_client **newclient);
--- /dev/null
+#
+# Copyright (C) 2009-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+CPG_LIBS = @CPG_LIBS@
+CPG_CFLAGS = @CPG_CFLAGS@
+SACKPT_LIBS = @SACKPT_LIBS@
+SACKPT_CFLAGS = @SACKPT_CFLAGS@
+
+SOURCES = clogd.c cluster.c compat.c functions.c link_mon.c local.c logging.c
+
+TARGETS = cmirrord
+
+include $(top_builddir)/make.tmpl
+
+LIBS += -ldevmapper
+LMLIBS += $(CPG_LIBS) $(SACKPT_LIBS)
+CFLAGS += $(CPG_CFLAGS) $(SACKPT_CFLAGS)
+
+cmirrord: $(OBJECTS) $(top_builddir)/lib/liblvm-internal.a
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) \
+ $(LVMLIBS) $(LMLIBS) $(LIBS)
+
+install: $(TARGETS)
+ $(INSTALL_PROGRAM) -D cmirrord $(usrsbindir)/cmirrord
--- /dev/null
+/*
+ * Copyright (C) 2004-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "logging.h"
+#include "common.h"
+#include "functions.h"
+#include "link_mon.h"
+#include "local.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+static volatile sig_atomic_t exit_now = 0;
+/* FIXME Review signal handling. Should be volatile sig_atomic_t */
+static sigset_t signal_mask;
+static volatile sig_atomic_t signal_received;
+
+static void process_signals(void);
+static void daemonize(void);
+static void init_all(void);
+static void cleanup_all(void);
+
+int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+{
+ daemonize();
+
+ init_all();
+
+ /* Parent can now exit, we're ready to handle requests */
+ kill(getppid(), SIGTERM);
+
+ LOG_PRINT("Starting cmirrord:");
+ LOG_PRINT(" Built: "__DATE__" "__TIME__"\n");
+ LOG_DBG(" Compiled with debugging.");
+
+ while (!exit_now) {
+ links_monitor();
+
+ links_issue_callbacks();
+
+ process_signals();
+ }
+ exit(EXIT_SUCCESS);
+}
+
+/*
+ * parent_exit_handler: exit the parent
+ * @sig: the signal
+ *
+ */
+static void parent_exit_handler(int sig __attribute__((unused)))
+{
+ exit_now = 1;
+}
+
+static void sig_handler(int sig)
+{
+ /* FIXME Races - don't touch signal_mask here. */
+ sigaddset(&signal_mask, sig);
+ signal_received = 1;
+}
+
+static void process_signal(int sig){
+ int r = 0;
+
+ switch(sig) {
+ case SIGINT:
+ case SIGQUIT:
+ case SIGTERM:
+ case SIGHUP:
+ r += log_status();
+ break;
+ case SIGUSR1:
+ case SIGUSR2:
+ log_debug();
+ /*local_debug();*/
+ cluster_debug();
+ return;
+ default:
+ LOG_PRINT("Unknown signal received... ignoring");
+ return;
+ }
+
+ if (!r) {
+ LOG_DBG("No current cluster logs... safe to exit.");
+ cleanup_all();
+ exit(EXIT_SUCCESS);
+ }
+
+ LOG_ERROR("Cluster logs exist. Refusing to exit.");
+}
+
+static void process_signals(void)
+{
+ int x;
+
+ if (!signal_received)
+ return;
+
+ signal_received = 0;
+
+ for (x = 1; x < _NSIG; x++) {
+ if (sigismember(&signal_mask, x)) {
+ sigdelset(&signal_mask, x);
+ process_signal(x);
+ }
+ }
+}
+
+static void remove_lockfile(void)
+{
+ unlink(CMIRRORD_PIDFILE);
+}
+
+/*
+ * daemonize
+ *
+ * Performs the steps necessary to become a daemon.
+ */
+static void daemonize(void)
+{
+ int pid;
+ int status;
+
+ signal(SIGTERM, &parent_exit_handler);
+
+ pid = fork();
+
+ if (pid < 0) {
+ LOG_ERROR("Unable to fork()");
+ exit(EXIT_FAILURE);
+ }
+
+ if (pid) {
+ /* Parent waits here for child to get going */
+ while (!waitpid(pid, &status, WNOHANG) && !exit_now);
+ if (exit_now)
+ exit(EXIT_SUCCESS);
+
+ switch (WEXITSTATUS(status)) {
+ case EXIT_LOCKFILE:
+ LOG_ERROR("Failed to create lockfile");
+ LOG_ERROR("Process already running?");
+ break;
+ case EXIT_KERNEL_SOCKET:
+ LOG_ERROR("Unable to create netlink socket");
+ break;
+ case EXIT_KERNEL_BIND:
+ LOG_ERROR("Unable to bind to netlink socket");
+ break;
+ case EXIT_KERNEL_SETSOCKOPT:
+ LOG_ERROR("Unable to setsockopt on netlink socket");
+ break;
+ case EXIT_CLUSTER_CKPT_INIT:
+ LOG_ERROR("Unable to initialize checkpoint service");
+ LOG_ERROR("Has the cluster infrastructure been started?");
+ break;
+ case EXIT_FAILURE:
+ LOG_ERROR("Failed to start: Generic error");
+ break;
+ default:
+ LOG_ERROR("Failed to start: Unknown error");
+ break;
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ setsid();
+ chdir("/");
+ umask(0);
+
+ close(0); close(1); close(2);
+ open("/dev/null", O_RDONLY); /* reopen stdin */
+ open("/dev/null", O_WRONLY); /* reopen stdout */
+ open("/dev/null", O_WRONLY); /* reopen stderr */
+
+ LOG_OPEN("cmirrord", LOG_PID, LOG_DAEMON);
+
+ (void) dm_prepare_selinux_context(CMIRRORD_PIDFILE, S_IFREG);
+ if (dm_create_lockfile(CMIRRORD_PIDFILE) == 0)
+ exit(EXIT_LOCKFILE);
+ (void) dm_prepare_selinux_context(NULL, 0);
+
+ atexit(remove_lockfile);
+
+ /* FIXME Replace with sigaction. (deprecated) */
+ signal(SIGINT, &sig_handler);
+ signal(SIGQUIT, &sig_handler);
+ signal(SIGTERM, &sig_handler);
+ signal(SIGHUP, &sig_handler);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGUSR1, &sig_handler);
+ signal(SIGUSR2, &sig_handler);
+ sigemptyset(&signal_mask);
+ signal_received = 0;
+}
+
+/*
+ * init_all
+ *
+ * Initialize modules. Exit on failure.
+ */
+static void init_all(void)
+{
+ int r;
+
+ if ((r = init_local()) ||
+ (r = init_cluster())) {
+ exit(r);
+ }
+}
+
+/*
+ * cleanup_all
+ *
+ * Clean up before exiting
+ */
+static void cleanup_all(void)
+{
+ cleanup_local();
+ cleanup_cluster();
+}
--- /dev/null
+/*
+ * Copyright (C) 2004-2009 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "logging.h"
+#include "cluster.h"
+#include "common.h"
+#include "compat.h"
+#include "functions.h"
+#include "link_mon.h"
+#include "local.h"
+#include "xlate.h"
+
+#include <corosync/cpg.h>
+#include <errno.h>
+#include <openais/saAis.h>
+#include <openais/saCkpt.h>
+#include <signal.h>
+#include <unistd.h>
+
+/* Open AIS error codes */
+#define str_ais_error(x) \
+ ((x) == SA_AIS_OK) ? "SA_AIS_OK" : \
+ ((x) == SA_AIS_ERR_LIBRARY) ? "SA_AIS_ERR_LIBRARY" : \
+ ((x) == SA_AIS_ERR_VERSION) ? "SA_AIS_ERR_VERSION" : \
+ ((x) == SA_AIS_ERR_INIT) ? "SA_AIS_ERR_INIT" : \
+ ((x) == SA_AIS_ERR_TIMEOUT) ? "SA_AIS_ERR_TIMEOUT" : \
+ ((x) == SA_AIS_ERR_TRY_AGAIN) ? "SA_AIS_ERR_TRY_AGAIN" : \
+ ((x) == SA_AIS_ERR_INVALID_PARAM) ? "SA_AIS_ERR_INVALID_PARAM" : \
+ ((x) == SA_AIS_ERR_NO_MEMORY) ? "SA_AIS_ERR_NO_MEMORY" : \
+ ((x) == SA_AIS_ERR_BAD_HANDLE) ? "SA_AIS_ERR_BAD_HANDLE" : \
+ ((x) == SA_AIS_ERR_BUSY) ? "SA_AIS_ERR_BUSY" : \
+ ((x) == SA_AIS_ERR_ACCESS) ? "SA_AIS_ERR_ACCESS" : \
+ ((x) == SA_AIS_ERR_NOT_EXIST) ? "SA_AIS_ERR_NOT_EXIST" : \
+ ((x) == SA_AIS_ERR_NAME_TOO_LONG) ? "SA_AIS_ERR_NAME_TOO_LONG" : \
+ ((x) == SA_AIS_ERR_EXIST) ? "SA_AIS_ERR_EXIST" : \
+ ((x) == SA_AIS_ERR_NO_SPACE) ? "SA_AIS_ERR_NO_SPACE" : \
+ ((x) == SA_AIS_ERR_INTERRUPT) ? "SA_AIS_ERR_INTERRUPT" : \
+ ((x) == SA_AIS_ERR_NAME_NOT_FOUND) ? "SA_AIS_ERR_NAME_NOT_FOUND" : \
+ ((x) == SA_AIS_ERR_NO_RESOURCES) ? "SA_AIS_ERR_NO_RESOURCES" : \
+ ((x) == SA_AIS_ERR_NOT_SUPPORTED) ? "SA_AIS_ERR_NOT_SUPPORTED" : \
+ ((x) == SA_AIS_ERR_BAD_OPERATION) ? "SA_AIS_ERR_BAD_OPERATION" : \
+ ((x) == SA_AIS_ERR_FAILED_OPERATION) ? "SA_AIS_ERR_FAILED_OPERATION" : \
+ ((x) == SA_AIS_ERR_MESSAGE_ERROR) ? "SA_AIS_ERR_MESSAGE_ERROR" : \
+ ((x) == SA_AIS_ERR_QUEUE_FULL) ? "SA_AIS_ERR_QUEUE_FULL" : \
+ ((x) == SA_AIS_ERR_QUEUE_NOT_AVAILABLE) ? "SA_AIS_ERR_QUEUE_NOT_AVAILABLE" : \
+ ((x) == SA_AIS_ERR_BAD_FLAGS) ? "SA_AIS_ERR_BAD_FLAGS" : \
+ ((x) == SA_AIS_ERR_TOO_BIG) ? "SA_AIS_ERR_TOO_BIG" : \
+ ((x) == SA_AIS_ERR_NO_SECTIONS) ? "SA_AIS_ERR_NO_SECTIONS" : \
+ "ais_error_unknown"
+
+#define _RQ_TYPE(x) \
+ ((x) == DM_ULOG_CHECKPOINT_READY) ? "DM_ULOG_CHECKPOINT_READY": \
+ ((x) == DM_ULOG_MEMBER_JOIN) ? "DM_ULOG_MEMBER_JOIN": \
+ RQ_TYPE((x) & ~DM_ULOG_RESPONSE)
+
+static uint32_t my_cluster_id = 0xDEAD;
+static SaCkptHandleT ckpt_handle = 0;
+static SaCkptCallbacksT callbacks = { 0, 0 };
+static SaVersionT version = { 'B', 1, 1 };
+
+#define DEBUGGING_HISTORY 100
+//static char debugging[DEBUGGING_HISTORY][128];
+//static int idx = 0;
+#define LOG_SPRINT(cc, f, arg...) do { \
+ cc->idx++; \
+ cc->idx = cc->idx % DEBUGGING_HISTORY; \
+ sprintf(cc->debugging[cc->idx], f, ## arg); \
+ } while (0)
+
+static int log_resp_rec = 0;
+
+struct checkpoint_data {
+ uint32_t requester;
+ char uuid[CPG_MAX_NAME_LENGTH];
+
+ int bitmap_size; /* in bytes */
+ char *sync_bits;
+ char *clean_bits;
+ char *recovering_region;
+ struct checkpoint_data *next;
+};
+
+#define INVALID 0
+#define VALID 1
+#define LEAVING 2
+
+#define MAX_CHECKPOINT_REQUESTERS 10
+struct clog_cpg {
+ struct dm_list list;
+
+ uint32_t lowest_id;
+ cpg_handle_t handle;
+ struct cpg_name name;
+ uint64_t luid;
+
+ /* Are we the first, or have we received checkpoint? */
+ int state;
+ int cpg_state; /* FIXME: debugging */
+ int free_me;
+ int delay;
+ int resend_requests;
+ struct dm_list startup_list;
+ struct dm_list working_list;
+
+ int checkpoints_needed;
+ uint32_t checkpoint_requesters[MAX_CHECKPOINT_REQUESTERS];
+ struct checkpoint_data *checkpoint_list;
+ int idx;
+ char debugging[DEBUGGING_HISTORY][128];
+};
+
+static struct dm_list clog_cpg_list;
+
+/*
+ * cluster_send
+ * @rq
+ *
+ * Returns: 0 on success, -Exxx on error
+ */
+int cluster_send(struct clog_request *rq)
+{
+ int r;
+ int count=0;
+ int found = 0;
+ struct iovec iov;
+ struct clog_cpg *entry;
+
+ dm_list_iterate_items(entry, &clog_cpg_list)
+ if (!strncmp(entry->name.value, rq->u_rq.uuid,
+ CPG_MAX_NAME_LENGTH)) {
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ rq->u_rq.error = -ENOENT;
+ return -ENOENT;
+ }
+
+ /*
+ * Once the request heads for the cluster, the luid looses
+ * all its meaning.
+ */
+ rq->u_rq.luid = 0;
+
+ iov.iov_base = rq;
+ iov.iov_len = sizeof(struct clog_request) + rq->u_rq.data_size;
+
+ rq->u.version[0] = xlate64(CLOG_TFR_VERSION);
+ rq->u.version[1] = CLOG_TFR_VERSION;
+
+ r = clog_request_to_network(rq);
+ if (r < 0)
+ /* FIXME: Better error code for byteswap failure? */
+ return -EINVAL;
+
+ if (entry->cpg_state != VALID)
+ return -EINVAL;
+
+ do {
+ r = cpg_mcast_joined(entry->handle, CPG_TYPE_AGREED, &iov, 1);
+ if (r != SA_AIS_ERR_TRY_AGAIN)
+ break;
+ count++;
+ if (count < 10)
+ LOG_PRINT("[%s] Retry #%d of cpg_mcast_joined: %s",
+ SHORT_UUID(rq->u_rq.uuid), count,
+ str_ais_error(r));
+ else if ((count < 100) && !(count % 10))
+ LOG_ERROR("[%s] Retry #%d of cpg_mcast_joined: %s",
+ SHORT_UUID(rq->u_rq.uuid), count,
+ str_ais_error(r));
+ else if ((count < 1000) && !(count % 100))
+ LOG_ERROR("[%s] Retry #%d of cpg_mcast_joined: %s",
+ SHORT_UUID(rq->u_rq.uuid), count,
+ str_ais_error(r));
+ else if ((count < 10000) && !(count % 1000))
+ LOG_ERROR("[%s] Retry #%d of cpg_mcast_joined: %s - "
+ "OpenAIS not handling the load?",
+ SHORT_UUID(rq->u_rq.uuid), count,
+ str_ais_error(r));
+ usleep(1000);
+ } while (1);
+
+ if (r == CPG_OK)
+ return 0;
+
+ /* error codes found in openais/cpg.h */
+ LOG_ERROR("cpg_mcast_joined error: %s", str_ais_error(r));
+
+ rq->u_rq.error = -EBADE;
+ return -EBADE;
+}
+
+static struct clog_request *get_matching_rq(struct clog_request *rq,
+ struct dm_list *l)
+{
+ struct clog_request *match, *n;
+
+ dm_list_iterate_items_gen_safe(match, n, l, u.list)
+ if (match->u_rq.seq == rq->u_rq.seq) {
+ dm_list_del(&match->u.list);
+ return match;
+ }
+
+ return NULL;
+}
+
+static char rq_buffer[DM_ULOG_REQUEST_SIZE];
+static int handle_cluster_request(struct clog_cpg *entry __attribute__((unused)),
+ struct clog_request *rq, int server)
+{
+ int r = 0;
+ struct clog_request *tmp = (struct clog_request *)rq_buffer;
+
+ /*
+ * We need a separate dm_ulog_request struct, one that can carry
+ * a return payload. Otherwise, the memory address after
+ * rq will be altered - leading to problems
+ */
+ memset(rq_buffer, 0, sizeof(rq_buffer));
+ memcpy(tmp, rq, sizeof(struct clog_request) + rq->u_rq.data_size);
+
+ /*
+ * With resumes, we only handle our own.
+ * Resume is a special case that requires
+ * local action (to set up CPG), followed by
+ * a cluster action to co-ordinate reading
+ * the disk and checkpointing
+ */
+ if (tmp->u_rq.request_type == DM_ULOG_RESUME) {
+ if (tmp->originator == my_cluster_id) {
+ r = do_request(tmp, server);
+
+ r = kernel_send(&tmp->u_rq);
+ if (r < 0)
+ LOG_ERROR("Failed to send resume response to kernel");
+ }
+ return r;
+ }
+
+ r = do_request(tmp, server);
+
+ if (server &&
+ (tmp->u_rq.request_type != DM_ULOG_CLEAR_REGION) &&
+ (tmp->u_rq.request_type != DM_ULOG_POSTSUSPEND)) {
+ tmp->u_rq.request_type |= DM_ULOG_RESPONSE;
+
+ /*
+ * Errors from previous functions are in the rq struct.
+ */
+ r = cluster_send(tmp);
+ if (r < 0)
+ LOG_ERROR("cluster_send failed: %s", strerror(-r));
+ }
+
+ return r;
+}
+
+static int handle_cluster_response(struct clog_cpg *entry,
+ struct clog_request *rq)
+{
+ int r = 0;
+ struct clog_request *orig_rq;
+
+ /*
+ * If I didn't send it, then I don't care about the response
+ */
+ if (rq->originator != my_cluster_id)
+ return 0;
+
+ rq->u_rq.request_type &= ~DM_ULOG_RESPONSE;
+ orig_rq = get_matching_rq(rq, &entry->working_list);
+
+ if (!orig_rq) {
+ /* Unable to find match for response */
+
+ LOG_ERROR("[%s] No match for cluster response: %s:%u",
+ SHORT_UUID(rq->u_rq.uuid),
+ _RQ_TYPE(rq->u_rq.request_type),
+ rq->u_rq.seq);
+
+ LOG_ERROR("Current local list:");
+ if (dm_list_empty(&entry->working_list))
+ LOG_ERROR(" [none]");
+
+ dm_list_iterate_items_gen(orig_rq, &entry->working_list, u.list)
+ LOG_ERROR(" [%s] %s:%u",
+ SHORT_UUID(orig_rq->u_rq.uuid),
+ _RQ_TYPE(orig_rq->u_rq.request_type),
+ orig_rq->u_rq.seq);
+
+ return -EINVAL;
+ }
+
+ if (log_resp_rec > 0) {
+ LOG_COND(log_resend_requests,
+ "[%s] Response received to %s/#%u",
+ SHORT_UUID(rq->u_rq.uuid),
+ _RQ_TYPE(rq->u_rq.request_type),
+ rq->u_rq.seq);
+ log_resp_rec--;
+ }
+
+ /* FIXME: Ensure memcpy cannot explode */
+ memcpy(orig_rq, rq, sizeof(*rq) + rq->u_rq.data_size);
+
+ r = kernel_send(&orig_rq->u_rq);
+ if (r)
+ LOG_ERROR("Failed to send response to kernel");
+
+ free(orig_rq);
+ return r;
+}
+
+static struct clog_cpg *find_clog_cpg(cpg_handle_t handle)
+{
+ struct clog_cpg *match;
+
+ dm_list_iterate_items(match, &clog_cpg_list)
+ if (match->handle == handle)
+ return match;
+
+ return NULL;
+}
+
+/*
+ * prepare_checkpoint
+ * @entry: clog_cpg describing the log
+ * @cp_requester: nodeid requesting the checkpoint
+ *
+ * Creates and fills in a new checkpoint_data struct.
+ *
+ * Returns: checkpoint_data on success, NULL on error
+ */
+static struct checkpoint_data *prepare_checkpoint(struct clog_cpg *entry,
+ uint32_t cp_requester)
+{
+ int r;
+ struct checkpoint_data *new;
+
+ if (entry->state != VALID) {
+ /*
+ * We can't store bitmaps yet, because the log is not
+ * valid yet.
+ */
+ LOG_ERROR("Forced to refuse checkpoint for nodeid %u - log not valid yet",
+ cp_requester);
+ return NULL;
+ }
+
+ new = malloc(sizeof(*new));
+ if (!new) {
+ LOG_ERROR("Unable to create checkpoint data for %u",
+ cp_requester);
+ return NULL;
+ }
+ memset(new, 0, sizeof(*new));
+ new->requester = cp_requester;
+ strncpy(new->uuid, entry->name.value, entry->name.length);
+
+ new->bitmap_size = push_state(entry->name.value, entry->luid,
+ "clean_bits",
+ &new->clean_bits, cp_requester);
+ if (new->bitmap_size <= 0) {
+ LOG_ERROR("Failed to store clean_bits to checkpoint for node %u",
+ new->requester);
+ free(new);
+ return NULL;
+ }
+
+ new->bitmap_size = push_state(entry->name.value, entry->luid,
+ "sync_bits",
+ &new->sync_bits, cp_requester);
+ if (new->bitmap_size <= 0) {
+ LOG_ERROR("Failed to store sync_bits to checkpoint for node %u",
+ new->requester);
+ free(new->clean_bits);
+ free(new);
+ return NULL;
+ }
+
+ r = push_state(entry->name.value, entry->luid,
+ "recovering_region",
+ &new->recovering_region, cp_requester);
+ if (r <= 0) {
+ LOG_ERROR("Failed to store recovering_region to checkpoint for node %u",
+ new->requester);
+ free(new->sync_bits);
+ free(new->clean_bits);
+ free(new);
+ return NULL;
+ }
+ LOG_DBG("[%s] Checkpoint prepared for node %u:",
+ SHORT_UUID(new->uuid), new->requester);
+ LOG_DBG(" bitmap_size = %d", new->bitmap_size);
+
+ return new;
+}
+
+/*
+ * free_checkpoint
+ * @cp: the checkpoint_data struct to free
+ *
+ */
+static void free_checkpoint(struct checkpoint_data *cp)
+{
+ free(cp->recovering_region);
+ free(cp->sync_bits);
+ free(cp->clean_bits);
+ free(cp);
+}
+
+static int export_checkpoint(struct checkpoint_data *cp)
+{
+ SaCkptCheckpointCreationAttributesT attr;
+ SaCkptCheckpointHandleT h;
+ SaCkptSectionIdT section_id;
+ SaCkptSectionCreationAttributesT section_attr;
+ SaCkptCheckpointOpenFlagsT flags;
+ SaNameT name;
+ SaAisErrorT rv;
+ struct clog_request *rq;
+ int len, r = 0;
+ char buf[32];
+
+ LOG_DBG("Sending checkpointed data to %u", cp->requester);
+
+ len = snprintf((char *)(name.value), SA_MAX_NAME_LENGTH,
+ "bitmaps_%s_%u", SHORT_UUID(cp->uuid), cp->requester);
+ name.length = (SaUint16T)len;
+
+ len = (int)strlen(cp->recovering_region) + 1;
+
+ attr.creationFlags = SA_CKPT_WR_ALL_REPLICAS;
+ attr.checkpointSize = cp->bitmap_size * 2 + len;
+
+ attr.retentionDuration = SA_TIME_MAX;
+ attr.maxSections = 4; /* don't know why we need +1 */
+
+ attr.maxSectionSize = (cp->bitmap_size > len) ? cp->bitmap_size : len;
+ attr.maxSectionIdSize = 22;
+
+ flags = SA_CKPT_CHECKPOINT_READ |
+ SA_CKPT_CHECKPOINT_WRITE |
+ SA_CKPT_CHECKPOINT_CREATE;
+
+open_retry:
+ rv = saCkptCheckpointOpen(ckpt_handle, &name, &attr, flags, 0, &h);
+ if (rv == SA_AIS_ERR_TRY_AGAIN) {
+ LOG_ERROR("export_checkpoint: ckpt open retry");
+ usleep(1000);
+ goto open_retry;
+ }
+
+ if (rv == SA_AIS_ERR_EXIST) {
+ LOG_DBG("export_checkpoint: checkpoint already exists");
+ return -EEXIST;
+ }
+
+ if (rv != SA_AIS_OK) {
+ LOG_ERROR("[%s] Failed to open checkpoint for %u: %s",
+ SHORT_UUID(cp->uuid), cp->requester,
+ str_ais_error(rv));
+ return -EIO; /* FIXME: better error */
+ }
+
+ /*
+ * Add section for sync_bits
+ */
+ section_id.idLen = (SaUint16T)snprintf(buf, 32, "sync_bits");
+ section_id.id = (unsigned char *)buf;
+ section_attr.sectionId = §ion_id;
+ section_attr.expirationTime = SA_TIME_END;
+
+sync_create_retry:
+ rv = saCkptSectionCreate(h, §ion_attr,
+ cp->sync_bits, cp->bitmap_size);
+ if (rv == SA_AIS_ERR_TRY_AGAIN) {
+ LOG_ERROR("Sync checkpoint section create retry");
+ usleep(1000);
+ goto sync_create_retry;
+ }
+
+ if (rv == SA_AIS_ERR_EXIST) {
+ LOG_DBG("Sync checkpoint section already exists");
+ saCkptCheckpointClose(h);
+ return -EEXIST;
+ }
+
+ if (rv != SA_AIS_OK) {
+ LOG_ERROR("Sync checkpoint section creation failed: %s",
+ str_ais_error(rv));
+ saCkptCheckpointClose(h);
+ return -EIO; /* FIXME: better error */
+ }
+
+ /*
+ * Add section for clean_bits
+ */
+ section_id.idLen = snprintf(buf, 32, "clean_bits");
+ section_id.id = (unsigned char *)buf;
+ section_attr.sectionId = §ion_id;
+ section_attr.expirationTime = SA_TIME_END;
+
+clean_create_retry:
+ rv = saCkptSectionCreate(h, §ion_attr, cp->clean_bits, cp->bitmap_size);
+ if (rv == SA_AIS_ERR_TRY_AGAIN) {
+ LOG_ERROR("Clean checkpoint section create retry");
+ usleep(1000);
+ goto clean_create_retry;
+ }
+
+ if (rv == SA_AIS_ERR_EXIST) {
+ LOG_DBG("Clean checkpoint section already exists");
+ saCkptCheckpointClose(h);
+ return -EEXIST;
+ }
+
+ if (rv != SA_AIS_OK) {
+ LOG_ERROR("Clean checkpoint section creation failed: %s",
+ str_ais_error(rv));
+ saCkptCheckpointClose(h);
+ return -EIO; /* FIXME: better error */
+ }
+
+ /*
+ * Add section for recovering_region
+ */
+ section_id.idLen = snprintf(buf, 32, "recovering_region");
+ section_id.id = (unsigned char *)buf;
+ section_attr.sectionId = §ion_id;
+ section_attr.expirationTime = SA_TIME_END;
+
+rr_create_retry:
+ rv = saCkptSectionCreate(h, §ion_attr, cp->recovering_region,
+ strlen(cp->recovering_region) + 1);
+ if (rv == SA_AIS_ERR_TRY_AGAIN) {
+ LOG_ERROR("RR checkpoint section create retry");
+ usleep(1000);
+ goto rr_create_retry;
+ }
+
+ if (rv == SA_AIS_ERR_EXIST) {
+ LOG_DBG("RR checkpoint section already exists");
+ saCkptCheckpointClose(h);
+ return -EEXIST;
+ }
+
+ if (rv != SA_AIS_OK) {
+ LOG_ERROR("RR checkpoint section creation failed: %s",
+ str_ais_error(rv));
+ saCkptCheckpointClose(h);
+ return -EIO; /* FIXME: better error */
+ }
+
+ LOG_DBG("export_checkpoint: closing checkpoint");
+ saCkptCheckpointClose(h);
+
+ rq = malloc(DM_ULOG_REQUEST_SIZE);
+ if (!rq) {
+ LOG_ERROR("export_checkpoint: Unable to allocate transfer structs");
+ return -ENOMEM;
+ }
+ memset(rq, 0, sizeof(*rq));
+
+ dm_list_init(&rq->u.list);
+ rq->u_rq.request_type = DM_ULOG_CHECKPOINT_READY;
+ rq->originator = cp->requester; /* FIXME: hack to overload meaning of originator */
+ strncpy(rq->u_rq.uuid, cp->uuid, CPG_MAX_NAME_LENGTH);
+ rq->u_rq.seq = my_cluster_id;
+
+ r = cluster_send(rq);
+ if (r)
+ LOG_ERROR("Failed to send checkpoint ready notice: %s",
+ strerror(-r));
+
+ free(rq);
+ return 0;
+}
+
+static int import_checkpoint(struct clog_cpg *entry, int no_read)
+{
+ int rtn = 0;
+ SaCkptCheckpointHandleT h;
+ SaCkptSectionIterationHandleT itr;
+ SaCkptSectionDescriptorT desc;
+ SaCkptIOVectorElementT iov;
+ SaNameT name;
+ SaAisErrorT rv;
+ char *bitmap = NULL;
+ int len;
+
+ bitmap = malloc(1024*1024);
+ if (!bitmap)
+ return -ENOMEM;
+
+ len = snprintf((char *)(name.value), SA_MAX_NAME_LENGTH, "bitmaps_%s_%u",
+ SHORT_UUID(entry->name.value), my_cluster_id);
+ name.length = (SaUint16T)len;
+
+open_retry:
+ rv = saCkptCheckpointOpen(ckpt_handle, &name, NULL,
+ SA_CKPT_CHECKPOINT_READ, 0, &h);
+ if (rv == SA_AIS_ERR_TRY_AGAIN) {
+ LOG_ERROR("import_checkpoint: ckpt open retry");
+ usleep(1000);
+ goto open_retry;
+ }
+
+ if (rv != SA_AIS_OK) {
+ LOG_ERROR("[%s] Failed to open checkpoint: %s",
+ SHORT_UUID(entry->name.value), str_ais_error(rv));
+ return -EIO; /* FIXME: better error */
+ }
+
+unlink_retry:
+ rv = saCkptCheckpointUnlink(ckpt_handle, &name);
+ if (rv == SA_AIS_ERR_TRY_AGAIN) {
+ LOG_ERROR("import_checkpoint: ckpt unlink retry");
+ usleep(1000);
+ goto unlink_retry;
+ }
+
+ if (no_read) {
+ LOG_DBG("Checkpoint for this log already received");
+ goto no_read;
+ }
+
+init_retry:
+ rv = saCkptSectionIterationInitialize(h, SA_CKPT_SECTIONS_ANY,
+ SA_TIME_END, &itr);
+ if (rv == SA_AIS_ERR_TRY_AGAIN) {
+ LOG_ERROR("import_checkpoint: sync create retry");
+ usleep(1000);
+ goto init_retry;
+ }
+
+ if (rv != SA_AIS_OK) {
+ LOG_ERROR("[%s] Sync checkpoint section creation failed: %s",
+ SHORT_UUID(entry->name.value), str_ais_error(rv));
+ return -EIO; /* FIXME: better error */
+ }
+
+ len = 0;
+ while (1) {
+ rv = saCkptSectionIterationNext(itr, &desc);
+ if (rv == SA_AIS_OK)
+ len++;
+ else if ((rv == SA_AIS_ERR_NO_SECTIONS) && len)
+ break;
+ else if (rv != SA_AIS_ERR_TRY_AGAIN) {
+ LOG_ERROR("saCkptSectionIterationNext failure: %d", rv);
+ break;
+ }
+ }
+ saCkptSectionIterationFinalize(itr);
+ if (len != 3) {
+ LOG_ERROR("import_checkpoint: %d checkpoint sections found",
+ len);
+ usleep(1000);
+ goto init_retry;
+ }
+ saCkptSectionIterationInitialize(h, SA_CKPT_SECTIONS_ANY,
+ SA_TIME_END, &itr);
+
+ while (1) {
+ rv = saCkptSectionIterationNext(itr, &desc);
+ if (rv == SA_AIS_ERR_NO_SECTIONS)
+ break;
+
+ if (rv == SA_AIS_ERR_TRY_AGAIN) {
+ LOG_ERROR("import_checkpoint: ckpt iternext retry");
+ usleep(1000);
+ continue;
+ }
+
+ if (rv != SA_AIS_OK) {
+ LOG_ERROR("import_checkpoint: clean checkpoint section "
+ "creation failed: %s", str_ais_error(rv));
+ rtn = -EIO; /* FIXME: better error */
+ goto fail;
+ }
+
+ if (!desc.sectionSize) {
+ LOG_ERROR("Checkpoint section empty");
+ continue;
+ }
+
+ memset(bitmap, 0, sizeof(*bitmap));
+ iov.sectionId = desc.sectionId;
+ iov.dataBuffer = bitmap;
+ iov.dataSize = desc.sectionSize;
+ iov.dataOffset = 0;
+
+ read_retry:
+ rv = saCkptCheckpointRead(h, &iov, 1, NULL);
+ if (rv == SA_AIS_ERR_TRY_AGAIN) {
+ LOG_ERROR("ckpt read retry");
+ usleep(1000);
+ goto read_retry;
+ }
+
+ if (rv != SA_AIS_OK) {
+ LOG_ERROR("import_checkpoint: ckpt read error: %s",
+ str_ais_error(rv));
+ rtn = -EIO; /* FIXME: better error */
+ goto fail;
+ }
+
+ if (iov.readSize) {
+ if (pull_state(entry->name.value, entry->luid,
+ (char *)desc.sectionId.id, bitmap,
+ iov.readSize)) {
+ LOG_ERROR("Error loading state");
+ rtn = -EIO;
+ goto fail;
+ }
+ } else {
+ /* Need to request new checkpoint */
+ rtn = -EAGAIN;
+ goto fail;
+ }
+ }
+
+fail:
+ saCkptSectionIterationFinalize(itr);
+no_read:
+ saCkptCheckpointClose(h);
+
+ free(bitmap);
+ return rtn;
+}
+
+static void do_checkpoints(struct clog_cpg *entry, int leaving)
+{
+ struct checkpoint_data *cp;
+
+ for (cp = entry->checkpoint_list; cp;) {
+ /*
+ * FIXME: Check return code. Could send failure
+ * notice in rq in export_checkpoint function
+ * by setting rq->error
+ */
+ switch (export_checkpoint(cp)) {
+ case -EEXIST:
+ LOG_SPRINT(entry, "[%s] Checkpoint for %u already handled%s",
+ SHORT_UUID(entry->name.value), cp->requester,
+ (leaving) ? "(L)": "");
+ LOG_COND(log_checkpoint,
+ "[%s] Checkpoint for %u already handled%s",
+ SHORT_UUID(entry->name.value), cp->requester,
+ (leaving) ? "(L)": "");
+ entry->checkpoint_list = cp->next;
+ free_checkpoint(cp);
+ cp = entry->checkpoint_list;
+ break;
+ case 0:
+ LOG_SPRINT(entry, "[%s] Checkpoint data available for node %u%s",
+ SHORT_UUID(entry->name.value), cp->requester,
+ (leaving) ? "(L)": "");
+ LOG_COND(log_checkpoint,
+ "[%s] Checkpoint data available for node %u%s",
+ SHORT_UUID(entry->name.value), cp->requester,
+ (leaving) ? "(L)": "");
+ entry->checkpoint_list = cp->next;
+ free_checkpoint(cp);
+ cp = entry->checkpoint_list;
+ break;
+ default:
+ /* FIXME: Skipping will cause list corruption */
+ LOG_ERROR("[%s] Failed to export checkpoint for %u%s",
+ SHORT_UUID(entry->name.value), cp->requester,
+ (leaving) ? "(L)": "");
+ }
+ }
+}
+
+static int resend_requests(struct clog_cpg *entry)
+{
+ int r = 0;
+ struct clog_request *rq, *n;
+
+ if (!entry->resend_requests || entry->delay)
+ return 0;
+
+ if (entry->state != VALID)
+ return 0;
+
+ entry->resend_requests = 0;
+
+ dm_list_iterate_items_gen_safe(rq, n, &entry->working_list, u.list) {
+ dm_list_del(&rq->u.list);
+
+ if (strcmp(entry->name.value, rq->u_rq.uuid)) {
+ LOG_ERROR("[%s] Stray request from another log (%s)",
+ SHORT_UUID(entry->name.value),
+ SHORT_UUID(rq->u_rq.uuid));
+ free(rq);
+ continue;
+ }
+
+ switch (rq->u_rq.request_type) {
+ case DM_ULOG_SET_REGION_SYNC:
+ /*
+ * Some requests simply do not need to be resent.
+ * If it is a request that just changes log state,
+ * then it doesn't need to be resent (everyone makes
+ * updates).
+ */
+ LOG_COND(log_resend_requests,
+ "[%s] Skipping resend of %s/#%u...",
+ SHORT_UUID(entry->name.value),
+ _RQ_TYPE(rq->u_rq.request_type),
+ rq->u_rq.seq);
+ LOG_SPRINT(entry, "### No resend: [%s] %s/%u ###",
+ SHORT_UUID(entry->name.value),
+ _RQ_TYPE(rq->u_rq.request_type),
+ rq->u_rq.seq);
+
+ rq->u_rq.data_size = 0;
+ kernel_send(&rq->u_rq);
+
+ break;
+
+ default:
+ /*
+ * If an action or a response is required, then
+ * the request must be resent.
+ */
+ LOG_COND(log_resend_requests,
+ "[%s] Resending %s(#%u) due to new server(%u)",
+ SHORT_UUID(entry->name.value),
+ _RQ_TYPE(rq->u_rq.request_type),
+ rq->u_rq.seq, entry->lowest_id);
+ LOG_SPRINT(entry, "*** Resending: [%s] %s/%u ***",
+ SHORT_UUID(entry->name.value),
+ _RQ_TYPE(rq->u_rq.request_type),
+ rq->u_rq.seq);
+ r = cluster_send(rq);
+ if (r < 0)
+ LOG_ERROR("Failed resend");
+ }
+ free(rq);
+ }
+
+ return r;
+}
+
+static int do_cluster_work(void *data __attribute__((unused)))
+{
+ int r = SA_AIS_OK;
+ struct clog_cpg *entry, *tmp;
+
+ dm_list_iterate_items_safe(entry, tmp, &clog_cpg_list) {
+ r = cpg_dispatch(entry->handle, CPG_DISPATCH_ALL);
+ if (r != SA_AIS_OK)
+ LOG_ERROR("cpg_dispatch failed: %s", str_ais_error(r));
+
+ if (entry->free_me) {
+ free(entry);
+ continue;
+ }
+ do_checkpoints(entry, 0);
+
+ resend_requests(entry);
+ }
+
+ return (r == SA_AIS_OK) ? 0 : -1; /* FIXME: good error number? */
+}
+
+static int flush_startup_list(struct clog_cpg *entry)
+{
+ int r = 0;
+ int i_was_server;
+ struct clog_request *rq, *n;
+ struct checkpoint_data *new;
+
+ dm_list_iterate_items_gen_safe(rq, n, &entry->startup_list, u.list) {
+ dm_list_del(&rq->u.list);
+
+ if (rq->u_rq.request_type == DM_ULOG_MEMBER_JOIN) {
+ new = prepare_checkpoint(entry, rq->originator);
+ if (!new) {
+ /*
+ * FIXME: Need better error handling. Other nodes
+ * will be trying to send the checkpoint too, and we
+ * must continue processing the list; so report error
+ * but continue.
+ */
+ LOG_ERROR("Failed to prepare checkpoint for %u!!!",
+ rq->originator);
+ free(rq);
+ continue;
+ }
+ LOG_SPRINT(entry, "[%s] Checkpoint prepared for %u",
+ SHORT_UUID(entry->name.value), rq->originator);
+ LOG_COND(log_checkpoint, "[%s] Checkpoint prepared for %u",
+ SHORT_UUID(entry->name.value), rq->originator);
+ new->next = entry->checkpoint_list;
+ entry->checkpoint_list = new;
+ } else {
+ LOG_DBG("[%s] Processing delayed request: %s",
+ SHORT_UUID(rq->u_rq.uuid),
+ _RQ_TYPE(rq->u_rq.request_type));
+ i_was_server = (rq->pit_server == my_cluster_id) ? 1 : 0;
+ r = handle_cluster_request(entry, rq, i_was_server);
+
+ if (r)
+ /*
+ * FIXME: If we error out here, we will never get
+ * another opportunity to retry these requests
+ */
+ LOG_ERROR("Error while processing delayed CPG message");
+ }
+ free(rq);
+ }
+
+ return 0;
+}
+
+static void cpg_message_callback(cpg_handle_t handle, const struct cpg_name *gname __attribute__((unused)),
+ uint32_t nodeid, uint32_t pid __attribute__((unused)),
+ void *msg, size_t msg_len)
+{
+ int i;
+ int r = 0;
+ int i_am_server;
+ int response = 0;
+ struct clog_request *rq = msg;
+ struct clog_request *tmp_rq;
+ struct clog_cpg *match;
+
+ if (clog_request_from_network(rq, msg_len) < 0)
+ /* Error message comes from 'clog_request_from_network' */
+ return;
+
+ match = find_clog_cpg(handle);
+ if (!match) {
+ LOG_ERROR("Unable to find clog_cpg for cluster message");
+ return;
+ }
+
+ if ((nodeid == my_cluster_id) &&
+ !(rq->u_rq.request_type & DM_ULOG_RESPONSE) &&
+ (rq->u_rq.request_type != DM_ULOG_RESUME) &&
+ (rq->u_rq.request_type != DM_ULOG_CLEAR_REGION) &&
+ (rq->u_rq.request_type != DM_ULOG_CHECKPOINT_READY)) {
+ tmp_rq = malloc(DM_ULOG_REQUEST_SIZE);
+ if (!tmp_rq) {
+ /*
+ * FIXME: It may be possible to continue... but we
+ * would not be able to resend any messages that might
+ * be necessary during membership changes
+ */
+ LOG_ERROR("[%s] Unable to record request: -ENOMEM",
+ SHORT_UUID(rq->u_rq.uuid));
+ return;
+ }
+ memcpy(tmp_rq, rq, sizeof(*rq) + rq->u_rq.data_size);
+ dm_list_init(&tmp_rq->u.list);
+ dm_list_add( &match->working_list, &tmp_rq->u.list);
+ }
+
+ if (rq->u_rq.request_type == DM_ULOG_POSTSUSPEND) {
+ /*
+ * If the server (lowest_id) indicates it is leaving,
+ * then we must resend any outstanding requests. However,
+ * we do not want to resend them if the next server in
+ * line is in the process of leaving.
+ */
+ if (nodeid == my_cluster_id) {
+ LOG_COND(log_resend_requests, "[%s] I am leaving.1.....",
+ SHORT_UUID(rq->u_rq.uuid));
+ } else {
+ if (nodeid < my_cluster_id) {
+ if (nodeid == match->lowest_id) {
+ match->resend_requests = 1;
+ LOG_COND(log_resend_requests, "[%s] %u is leaving, resend required%s",
+ SHORT_UUID(rq->u_rq.uuid), nodeid,
+ (dm_list_empty(&match->working_list)) ? " -- working_list empty": "");
+
+ dm_list_iterate_items_gen(tmp_rq, &match->working_list, u.list)
+ LOG_COND(log_resend_requests,
+ "[%s] %s/%u",
+ SHORT_UUID(tmp_rq->u_rq.uuid),
+ _RQ_TYPE(tmp_rq->u_rq.request_type),
+ tmp_rq->u_rq.seq);
+ }
+
+ match->delay++;
+ LOG_COND(log_resend_requests, "[%s] %u is leaving, delay = %d",
+ SHORT_UUID(rq->u_rq.uuid), nodeid, match->delay);
+ }
+ rq->originator = nodeid; /* don't really need this, but nice for debug */
+ goto out;
+ }
+ }
+
+ /*
+ * We can receive messages after we do a cpg_leave but before we
+ * get our config callback. However, since we can't respond after
+ * leaving, we simply return.
+ */
+ if (match->state == LEAVING)
+ return;
+
+ i_am_server = (my_cluster_id == match->lowest_id) ? 1 : 0;
+
+ if (rq->u_rq.request_type == DM_ULOG_CHECKPOINT_READY) {
+ if (my_cluster_id == rq->originator) {
+ /* Redundant checkpoints ignored if match->valid */
+ LOG_SPRINT(match, "[%s] CHECKPOINT_READY notification from %u",
+ SHORT_UUID(rq->u_rq.uuid), nodeid);
+ if (import_checkpoint(match, (match->state != INVALID))) {
+ LOG_SPRINT(match,
+ "[%s] Failed to import checkpoint from %u",
+ SHORT_UUID(rq->u_rq.uuid), nodeid);
+ LOG_ERROR("[%s] Failed to import checkpoint from %u",
+ SHORT_UUID(rq->u_rq.uuid), nodeid);
+ kill(getpid(), SIGUSR1);
+ /* Could we retry? */
+ goto out;
+ } else if (match->state == INVALID) {
+ LOG_SPRINT(match,
+ "[%s] Checkpoint data received from %u. Log is now valid",
+ SHORT_UUID(match->name.value), nodeid);
+ LOG_COND(log_checkpoint,
+ "[%s] Checkpoint data received from %u. Log is now valid",
+ SHORT_UUID(match->name.value), nodeid);
+ match->state = VALID;
+
+ flush_startup_list(match);
+ } else {
+ LOG_SPRINT(match,
+ "[%s] Redundant checkpoint from %u ignored.",
+ SHORT_UUID(rq->u_rq.uuid), nodeid);
+ }
+ }
+ goto out;
+ }
+
+ if (rq->u_rq.request_type & DM_ULOG_RESPONSE) {
+ response = 1;
+ r = handle_cluster_response(match, rq);
+ } else {
+ rq->originator = nodeid;
+
+ if (match->state == LEAVING) {
+ LOG_ERROR("[%s] Ignoring %s from %u. Reason: I'm leaving",
+ SHORT_UUID(rq->u_rq.uuid), _RQ_TYPE(rq->u_rq.request_type),
+ rq->originator);
+ goto out;
+ }
+
+ if (match->state == INVALID) {
+ LOG_DBG("Log not valid yet, storing request");
+ tmp_rq = malloc(DM_ULOG_REQUEST_SIZE);
+ if (!tmp_rq) {
+ LOG_ERROR("cpg_message_callback: Unable to"
+ " allocate transfer structs");
+ r = -ENOMEM; /* FIXME: Better error #? */
+ goto out;
+ }
+
+ memcpy(tmp_rq, rq, sizeof(*rq) + rq->u_rq.data_size);
+ tmp_rq->pit_server = match->lowest_id;
+ dm_list_init(&tmp_rq->u.list);
+ dm_list_add(&match->startup_list, &tmp_rq->u.list);
+ goto out;
+ }
+
+ r = handle_cluster_request(match, rq, i_am_server);
+ }
+
+ /*
+ * If the log is now valid, we can queue the checkpoints
+ */
+ for (i = match->checkpoints_needed; i; ) {
+ struct checkpoint_data *new;
+
+ if (log_get_state(&rq->u_rq) != LOG_RESUMED) {
+ LOG_DBG("[%s] Withholding checkpoints until log is valid (%s from %u)",
+ SHORT_UUID(rq->u_rq.uuid), _RQ_TYPE(rq->u_rq.request_type), nodeid);
+ break;
+ }
+
+ i--;
+ new = prepare_checkpoint(match, match->checkpoint_requesters[i]);
+ if (!new) {
+ /* FIXME: Need better error handling */
+ LOG_ERROR("[%s] Failed to prepare checkpoint for %u!!!",
+ SHORT_UUID(rq->u_rq.uuid), match->checkpoint_requesters[i]);
+ break;
+ }
+ LOG_SPRINT(match, "[%s] Checkpoint prepared for %u* (%s)",
+ SHORT_UUID(rq->u_rq.uuid), match->checkpoint_requesters[i],
+ (log_get_state(&rq->u_rq) != LOG_RESUMED)? "LOG_RESUMED": "LOG_SUSPENDED");
+ LOG_COND(log_checkpoint, "[%s] Checkpoint prepared for %u*",
+ SHORT_UUID(rq->u_rq.uuid), match->checkpoint_requesters[i]);
+ match->checkpoints_needed--;
+
+ new->next = match->checkpoint_list;
+ match->checkpoint_list = new;
+ }
+
+out:
+ /* nothing happens after this point. It is just for debugging */
+ if (r) {
+ LOG_ERROR("[%s] Error while processing CPG message, %s: %s",
+ SHORT_UUID(rq->u_rq.uuid),
+ _RQ_TYPE(rq->u_rq.request_type & ~DM_ULOG_RESPONSE),
+ strerror(-r));
+ LOG_ERROR("[%s] Response : %s", SHORT_UUID(rq->u_rq.uuid),
+ (response) ? "YES" : "NO");
+ LOG_ERROR("[%s] Originator: %u",
+ SHORT_UUID(rq->u_rq.uuid), rq->originator);
+ if (response)
+ LOG_ERROR("[%s] Responder : %u",
+ SHORT_UUID(rq->u_rq.uuid), nodeid);
+
+ LOG_ERROR("HISTORY::");
+ for (i = 0; i < DEBUGGING_HISTORY; i++) {
+ match->idx++;
+ match->idx = match->idx % DEBUGGING_HISTORY;
+ if (match->debugging[match->idx][0] == '\0')
+ continue;
+ LOG_ERROR("%d:%d) %s", i, match->idx,
+ match->debugging[match->idx]);
+ }
+ } else if (!(rq->u_rq.request_type & DM_ULOG_RESPONSE) ||
+ (rq->originator == my_cluster_id)) {
+ if (!response)
+ LOG_SPRINT(match, "SEQ#=%u, UUID=%s, TYPE=%s, ORIG=%u, RESP=%s",
+ rq->u_rq.seq, SHORT_UUID(rq->u_rq.uuid),
+ _RQ_TYPE(rq->u_rq.request_type),
+ rq->originator, (response) ? "YES" : "NO");
+ else
+ LOG_SPRINT(match, "SEQ#=%u, UUID=%s, TYPE=%s, ORIG=%u, RESP=%s, RSPR=%u",
+ rq->u_rq.seq, SHORT_UUID(rq->u_rq.uuid),
+ _RQ_TYPE(rq->u_rq.request_type),
+ rq->originator, (response) ? "YES" : "NO",
+ nodeid);
+ }
+}
+
+static void cpg_join_callback(struct clog_cpg *match,
+ const struct cpg_address *joined,
+ const struct cpg_address *member_list,
+ size_t member_list_entries)
+{
+ unsigned i;
+ uint32_t my_pid = (uint32_t)getpid();
+ uint32_t lowest = match->lowest_id;
+ struct clog_request *rq;
+ char dbuf[32];
+
+ /* Assign my_cluster_id */
+ if ((my_cluster_id == 0xDEAD) && (joined->pid == my_pid))
+ my_cluster_id = joined->nodeid;
+
+ /* Am I the very first to join? */
+ if (member_list_entries == 1) {
+ match->lowest_id = joined->nodeid;
+ match->state = VALID;
+ }
+
+ /* If I am part of the joining list, I do not send checkpoints */
+ if (joined->nodeid == my_cluster_id)
+ goto out;
+
+ memset(dbuf, 0, sizeof(dbuf));
+ for (i = 0; i < member_list_entries - 1; i++)
+ sprintf(dbuf+strlen(dbuf), "%u-", member_list[i].nodeid);
+ sprintf(dbuf+strlen(dbuf), "(%u)", joined->nodeid);
+ LOG_COND(log_checkpoint, "[%s] Joining node, %u needs checkpoint [%s]",
+ SHORT_UUID(match->name.value), joined->nodeid, dbuf);
+
+ /*
+ * FIXME: remove checkpoint_requesters/checkpoints_needed, and use
+ * the startup_list interface exclusively
+ */
+ if (dm_list_empty(&match->startup_list) && (match->state == VALID) &&
+ (match->checkpoints_needed < MAX_CHECKPOINT_REQUESTERS)) {
+ match->checkpoint_requesters[match->checkpoints_needed++] = joined->nodeid;
+ goto out;
+ }
+
+ rq = malloc(DM_ULOG_REQUEST_SIZE);
+ if (!rq) {
+ LOG_ERROR("cpg_config_callback: "
+ "Unable to allocate transfer structs");
+ LOG_ERROR("cpg_config_callback: "
+ "Unable to perform checkpoint");
+ goto out;
+ }
+ rq->u_rq.request_type = DM_ULOG_MEMBER_JOIN;
+ rq->originator = joined->nodeid;
+ dm_list_init(&rq->u.list);
+ dm_list_add(&match->startup_list, &rq->u.list);
+
+out:
+ /* Find the lowest_id, i.e. the server */
+ match->lowest_id = member_list[0].nodeid;
+ for (i = 0; i < member_list_entries; i++)
+ if (match->lowest_id > member_list[i].nodeid)
+ match->lowest_id = member_list[i].nodeid;
+
+ if (lowest == 0xDEAD)
+ LOG_COND(log_membership_change, "[%s] Server change <none> -> %u (%u %s)",
+ SHORT_UUID(match->name.value), match->lowest_id,
+ joined->nodeid, (member_list_entries == 1) ?
+ "is first to join" : "joined");
+ else if (lowest != match->lowest_id)
+ LOG_COND(log_membership_change, "[%s] Server change %u -> %u (%u joined)",
+ SHORT_UUID(match->name.value), lowest,
+ match->lowest_id, joined->nodeid);
+ else
+ LOG_COND(log_membership_change, "[%s] Server unchanged at %u (%u joined)",
+ SHORT_UUID(match->name.value),
+ lowest, joined->nodeid);
+ LOG_SPRINT(match, "+++ UUID=%s %u join +++",
+ SHORT_UUID(match->name.value), joined->nodeid);
+}
+
+static void cpg_leave_callback(struct clog_cpg *match,
+ const struct cpg_address *left,
+ const struct cpg_address *member_list,
+ size_t member_list_entries)
+{
+ unsigned i;
+ int j, fd;
+ uint32_t lowest = match->lowest_id;
+ struct clog_request *rq, *n;
+ struct checkpoint_data *p_cp, *c_cp;
+
+ LOG_SPRINT(match, "--- UUID=%s %u left ---",
+ SHORT_UUID(match->name.value), left->nodeid);
+
+ /* Am I leaving? */
+ if (my_cluster_id == left->nodeid) {
+ LOG_DBG("Finalizing leave...");
+ dm_list_del(&match->list);
+
+ cpg_fd_get(match->handle, &fd);
+ links_unregister(fd);
+
+ cluster_postsuspend(match->name.value, match->luid);
+
+ dm_list_iterate_items_gen_safe(rq, n, &match->working_list, u.list) {
+ dm_list_del(&rq->u.list);
+
+ if (rq->u_rq.request_type == DM_ULOG_POSTSUSPEND)
+ kernel_send(&rq->u_rq);
+ free(rq);
+ }
+
+ cpg_finalize(match->handle);
+
+ match->free_me = 1;
+ match->lowest_id = 0xDEAD;
+ match->state = INVALID;
+ }
+
+ /* Remove any pending checkpoints for the leaving node. */
+ for (p_cp = NULL, c_cp = match->checkpoint_list;
+ c_cp && (c_cp->requester != left->nodeid);
+ p_cp = c_cp, c_cp = c_cp->next);
+ if (c_cp) {
+ if (p_cp)
+ p_cp->next = c_cp->next;
+ else
+ match->checkpoint_list = c_cp->next;
+
+ LOG_COND(log_checkpoint,
+ "[%s] Removing pending checkpoint (%u is leaving)",
+ SHORT_UUID(match->name.value), left->nodeid);
+ free_checkpoint(c_cp);
+ }
+ dm_list_iterate_items_gen_safe(rq, n, &match->startup_list, u.list) {
+ if ((rq->u_rq.request_type == DM_ULOG_MEMBER_JOIN) &&
+ (rq->originator == left->nodeid)) {
+ LOG_COND(log_checkpoint,
+ "[%s] Removing pending ckpt from startup list (%u is leaving)",
+ SHORT_UUID(match->name.value), left->nodeid);
+ dm_list_del(&rq->u.list);
+ free(rq);
+ }
+ }
+ for (i = 0, j = 0; i < match->checkpoints_needed; i++, j++) {
+ match->checkpoint_requesters[j] = match->checkpoint_requesters[i];
+ if (match->checkpoint_requesters[i] == left->nodeid) {
+ LOG_ERROR("[%s] Removing pending ckpt from needed list (%u is leaving)",
+ SHORT_UUID(match->name.value), left->nodeid);
+ j--;
+ }
+ }
+ match->checkpoints_needed = j;
+
+ if (left->nodeid < my_cluster_id) {
+ match->delay = (match->delay > 0) ? match->delay - 1 : 0;
+ if (!match->delay && dm_list_empty(&match->working_list))
+ match->resend_requests = 0;
+ LOG_COND(log_resend_requests, "[%s] %u has left, delay = %d%s",
+ SHORT_UUID(match->name.value), left->nodeid,
+ match->delay, (dm_list_empty(&match->working_list)) ?
+ " -- working_list empty": "");
+ }
+
+ /* Find the lowest_id, i.e. the server */
+ if (!member_list_entries) {
+ match->lowest_id = 0xDEAD;
+ LOG_COND(log_membership_change, "[%s] Server change %u -> <none> "
+ "(%u is last to leave)",
+ SHORT_UUID(match->name.value), left->nodeid,
+ left->nodeid);
+ return;
+ }
+
+ match->lowest_id = member_list[0].nodeid;
+ for (i = 0; i < member_list_entries; i++)
+ if (match->lowest_id > member_list[i].nodeid)
+ match->lowest_id = member_list[i].nodeid;
+
+ if (lowest != match->lowest_id) {
+ LOG_COND(log_membership_change, "[%s] Server change %u -> %u (%u left)",
+ SHORT_UUID(match->name.value), lowest,
+ match->lowest_id, left->nodeid);
+ } else
+ LOG_COND(log_membership_change, "[%s] Server unchanged at %u (%u left)",
+ SHORT_UUID(match->name.value), lowest, left->nodeid);
+
+ if ((match->state == INVALID) && !match->free_me) {
+ /*
+ * If all CPG members are waiting for checkpoints and they
+ * are all present in my startup_list, then I was the first to
+ * join and I must assume control.
+ *
+ * We do not normally end up here, but if there was a quick
+ * 'resume -> suspend -> resume' across the cluster, we may
+ * have initially thought we were not the first to join because
+ * of the presence of out-going (and unable to respond) members.
+ */
+
+ i = 1; /* We do not have a DM_ULOG_MEMBER_JOIN entry of our own */
+ dm_list_iterate_items_gen(rq, &match->startup_list, u.list)
+ if (rq->u_rq.request_type == DM_ULOG_MEMBER_JOIN)
+ i++;
+
+ if (i == member_list_entries) {
+ /*
+ * Last node who could have given me a checkpoint just left.
+ * Setting log state to VALID and acting as 'first join'.
+ */
+ match->state = VALID;
+ flush_startup_list(match);
+ }
+ }
+}
+
+static void cpg_config_callback(cpg_handle_t handle, const struct cpg_name *gname __attribute__((unused)),
+ const struct cpg_address *member_list,
+ size_t member_list_entries,
+ const struct cpg_address *left_list,
+ size_t left_list_entries,
+ const struct cpg_address *joined_list,
+ size_t joined_list_entries)
+{
+ struct clog_cpg *match;
+ int found = 0;
+
+ dm_list_iterate_items(match, &clog_cpg_list)
+ if (match->handle == handle) {
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ LOG_ERROR("Unable to find match for CPG config callback");
+ return;
+ }
+
+ if ((joined_list_entries + left_list_entries) > 1)
+ LOG_ERROR("[%s] More than one node joining/leaving",
+ SHORT_UUID(match->name.value));
+
+ if (joined_list_entries)
+ cpg_join_callback(match, joined_list,
+ member_list, member_list_entries);
+ else
+ cpg_leave_callback(match, left_list,
+ member_list, member_list_entries);
+}
+
+cpg_callbacks_t cpg_callbacks = {
+ .cpg_deliver_fn = cpg_message_callback,
+ .cpg_confchg_fn = cpg_config_callback,
+};
+
+/*
+ * remove_checkpoint
+ * @entry
+ *
+ * Returns: 1 if checkpoint removed, 0 if no checkpoints, -EXXX on error
+ */
+static int remove_checkpoint(struct clog_cpg *entry)
+{
+ int len;
+ SaNameT name;
+ SaAisErrorT rv;
+ SaCkptCheckpointHandleT h;
+
+ len = snprintf((char *)(name.value), SA_MAX_NAME_LENGTH, "bitmaps_%s_%u",
+ SHORT_UUID(entry->name.value), my_cluster_id);
+ name.length = len;
+
+open_retry:
+ rv = saCkptCheckpointOpen(ckpt_handle, &name, NULL,
+ SA_CKPT_CHECKPOINT_READ, 0, &h);
+ if (rv == SA_AIS_ERR_TRY_AGAIN) {
+ LOG_ERROR("abort_startup: ckpt open retry");
+ usleep(1000);
+ goto open_retry;
+ }
+
+ if (rv != SA_AIS_OK)
+ return 0;
+
+ LOG_DBG("[%s] Removing checkpoint", SHORT_UUID(entry->name.value));
+unlink_retry:
+ rv = saCkptCheckpointUnlink(ckpt_handle, &name);
+ if (rv == SA_AIS_ERR_TRY_AGAIN) {
+ LOG_ERROR("abort_startup: ckpt unlink retry");
+ usleep(1000);
+ goto unlink_retry;
+ }
+
+ if (rv != SA_AIS_OK) {
+ LOG_ERROR("[%s] Failed to unlink checkpoint: %s",
+ SHORT_UUID(entry->name.value), str_ais_error(rv));
+ return -EIO;
+ }
+
+ saCkptCheckpointClose(h);
+
+ return 1;
+}
+
+int create_cluster_cpg(char *uuid, uint64_t luid)
+{
+ int r;
+ size_t size;
+ struct clog_cpg *new = NULL;
+ struct clog_cpg *tmp;
+
+ dm_list_iterate_items(tmp, &clog_cpg_list)
+ if (!strncmp(tmp->name.value, uuid, CPG_MAX_NAME_LENGTH)) {
+ LOG_ERROR("Log entry already exists: %s", uuid);
+ return -EEXIST;
+ }
+
+ new = malloc(sizeof(*new));
+ if (!new) {
+ LOG_ERROR("Unable to allocate memory for clog_cpg");
+ return -ENOMEM;
+ }
+ memset(new, 0, sizeof(*new));
+ dm_list_init(&new->list);
+ new->lowest_id = 0xDEAD;
+ dm_list_init(&new->startup_list);
+ dm_list_init(&new->working_list);
+
+ size = ((strlen(uuid) + 1) > CPG_MAX_NAME_LENGTH) ?
+ CPG_MAX_NAME_LENGTH : (strlen(uuid) + 1);
+ strncpy(new->name.value, uuid, size);
+ new->name.length = (uint32_t)size;
+ new->luid = luid;
+
+ /*
+ * Ensure there are no stale checkpoints around before we join
+ */
+ if (remove_checkpoint(new) == 1)
+ LOG_COND(log_checkpoint,
+ "[%s] Removing checkpoints left from previous session",
+ SHORT_UUID(new->name.value));
+
+ r = cpg_initialize(&new->handle, &cpg_callbacks);
+ if (r != SA_AIS_OK) {
+ LOG_ERROR("cpg_initialize failed: Cannot join cluster");
+ free(new);
+ return -EPERM;
+ }
+
+ r = cpg_join(new->handle, &new->name);
+ if (r != SA_AIS_OK) {
+ LOG_ERROR("cpg_join failed: Cannot join cluster");
+ free(new);
+ return -EPERM;
+ }
+
+ new->cpg_state = VALID;
+ dm_list_add(&clog_cpg_list, &new->list);
+ LOG_DBG("New handle: %llu", (unsigned long long)new->handle);
+ LOG_DBG("New name: %s", new->name.value);
+
+ /* FIXME: better variable */
+ cpg_fd_get(new->handle, &r);
+ links_register(r, "cluster", do_cluster_work, NULL);
+
+ return 0;
+}
+
+static void abort_startup(struct clog_cpg *del)
+{
+ struct clog_request *rq, *n;
+
+ LOG_DBG("[%s] CPG teardown before checkpoint received",
+ SHORT_UUID(del->name.value));
+
+ dm_list_iterate_items_gen_safe(rq, n, &del->startup_list, u.list) {
+ dm_list_del(&rq->u.list);
+
+ LOG_DBG("[%s] Ignoring request from %u: %s",
+ SHORT_UUID(del->name.value), rq->originator,
+ _RQ_TYPE(rq->u_rq.request_type));
+ free(rq);
+ }
+
+ remove_checkpoint(del);
+}
+
+static int _destroy_cluster_cpg(struct clog_cpg *del)
+{
+ int r;
+ int state;
+
+ LOG_COND(log_resend_requests, "[%s] I am leaving.2.....",
+ SHORT_UUID(del->name.value));
+
+ /*
+ * We must send any left over checkpoints before
+ * leaving. If we don't, an incoming node could
+ * be stuck with no checkpoint and stall.
+ do_checkpoints(del); --- THIS COULD BE CAUSING OUR PROBLEMS:
+
+ - Incoming node deletes old checkpoints before joining
+ - A stale checkpoint is issued here by leaving node
+ - (leaving node leaves)
+ - Incoming node joins cluster and finds stale checkpoint.
+ - (leaving node leaves - option 2)
+ */
+ do_checkpoints(del, 1);
+
+ state = del->state;
+
+ del->cpg_state = INVALID;
+ del->state = LEAVING;
+
+ /*
+ * If the state is VALID, we might be processing the
+ * startup list. If so, we certainly don't want to
+ * clear the startup_list here by calling abort_startup
+ */
+ if (!dm_list_empty(&del->startup_list) && (state != VALID))
+ abort_startup(del);
+
+ r = cpg_leave(del->handle, &del->name);
+ if (r != CPG_OK)
+ LOG_ERROR("Error leaving CPG!");
+ return 0;
+}
+
+int destroy_cluster_cpg(char *uuid)
+{
+ struct clog_cpg *del, *tmp;
+
+ dm_list_iterate_items_safe(del, tmp, &clog_cpg_list)
+ if (!strncmp(del->name.value, uuid, CPG_MAX_NAME_LENGTH))
+ _destroy_cluster_cpg(del);
+
+ return 0;
+}
+
+int init_cluster(void)
+{
+ SaAisErrorT rv;
+
+ dm_list_init(&clog_cpg_list);
+ rv = saCkptInitialize(&ckpt_handle, &callbacks, &version);
+
+ if (rv != SA_AIS_OK)
+ return EXIT_CLUSTER_CKPT_INIT;
+
+ return 0;
+}
+
+void cleanup_cluster(void)
+{
+ SaAisErrorT err;
+
+ err = saCkptFinalize(ckpt_handle);
+ if (err != SA_AIS_OK)
+ LOG_ERROR("Failed to finalize checkpoint handle");
+}
+
+void cluster_debug(void)
+{
+ struct checkpoint_data *cp;
+ struct clog_cpg *entry;
+ struct clog_request *rq;
+ int i;
+
+ LOG_ERROR("");
+ LOG_ERROR("CLUSTER COMPONENT DEBUGGING::");
+ dm_list_iterate_items(entry, &clog_cpg_list) {
+ LOG_ERROR("%s::", SHORT_UUID(entry->name.value));
+ LOG_ERROR(" lowest_id : %u", entry->lowest_id);
+ LOG_ERROR(" state : %s", (entry->state == INVALID) ?
+ "INVALID" : (entry->state == VALID) ? "VALID" :
+ (entry->state == LEAVING) ? "LEAVING" : "UNKNOWN");
+ LOG_ERROR(" cpg_state : %d", entry->cpg_state);
+ LOG_ERROR(" free_me : %d", entry->free_me);
+ LOG_ERROR(" delay : %d", entry->delay);
+ LOG_ERROR(" resend_requests : %d", entry->resend_requests);
+ LOG_ERROR(" checkpoints_needed: %d", entry->checkpoints_needed);
+ for (i = 0, cp = entry->checkpoint_list;
+ i < MAX_CHECKPOINT_REQUESTERS; i++)
+ if (cp)
+ cp = cp->next;
+ else
+ break;
+ LOG_ERROR(" CKPTs waiting : %d", i);
+ LOG_ERROR(" Working list:");
+ dm_list_iterate_items_gen(rq, &entry->working_list, u.list)
+ LOG_ERROR(" %s/%u", _RQ_TYPE(rq->u_rq.request_type),
+ rq->u_rq.seq);
+
+ LOG_ERROR(" Startup list:");
+ dm_list_iterate_items_gen(rq, &entry->startup_list, u.list)
+ LOG_ERROR(" %s/%u", _RQ_TYPE(rq->u_rq.request_type),
+ rq->u_rq.seq);
+
+ LOG_ERROR("Command History:");
+ for (i = 0; i < DEBUGGING_HISTORY; i++) {
+ entry->idx++;
+ entry->idx = entry->idx % DEBUGGING_HISTORY;
+ if (entry->debugging[entry->idx][0] == '\0')
+ continue;
+ LOG_ERROR("%d:%d) %s", i, entry->idx,
+ entry->debugging[entry->idx]);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2004-2009 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _LVM_CLOG_CLUSTER_H
+#define _LVM_CLOG_CLUSTER_H
+
+#include "dm-log-userspace.h"
+#include "libdevmapper.h"
+
+#define DM_ULOG_RESPONSE 0x1000U /* in last byte of 32-bit value */
+#define DM_ULOG_CHECKPOINT_READY 21
+#define DM_ULOG_MEMBER_JOIN 22
+
+/*
+ * There is other information in addition to what can
+ * be found in the dm_ulog_request structure that we
+ * need for processing. 'clog_request' is the wrapping
+ * structure we use to make the additional fields
+ * available.
+ */
+struct clog_request {
+ /*
+ * If we don't use a union, the structure size will
+ * vary between 32-bit and 64-bit machines. So, we
+ * pack two 64-bit version numbers in there to force
+ * the size of the structure to be the same.
+ *
+ * The two version numbers also help us with endian
+ * issues. The first is always little endian, while
+ * the second is in native format of the sending
+ * machine. If the two are equal, there is no need
+ * to do endian conversions.
+ */
+ union {
+ uint64_t version[2]; /* LE version and native version */
+ struct dm_list list;
+ } u;
+
+ /*
+ * 'originator' is the machine from which the requests
+ * was made.
+ */
+ uint32_t originator;
+
+ /*
+ * 'pit_server' is the "point-in-time" server for the
+ * request. (I.e. The machine that was the server at
+ * the time the request was issued - only important during
+ * startup.
+ */
+ uint32_t pit_server;
+
+ /*
+ * The request from the kernel that is being processed
+ */
+ struct dm_ulog_request u_rq;
+};
+
+int init_cluster(void);
+void cleanup_cluster(void);
+void cluster_debug(void);
+
+int create_cluster_cpg(char *uuid, uint64_t luid);
+int destroy_cluster_cpg(char *uuid);
+
+int cluster_send(struct clog_request *rq);
+
+#endif /* _LVM_CLOG_CLUSTER_H */
--- /dev/null
+/*
+ * Copyright (C) 2004-2009 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _LVM_CLOG_COMMON_H
+#define _LVM_CLOG_COMMON_H
+
+/*
+ * If there are problems when forking off to become a daemon,
+ * the child will exist with one of these codes. This allows
+ * the parent to know the reason for the failure and print it
+ * to the launching terminal.
+ *
+ * #define EXIT_SUCCESS 0 (from stdlib.h)
+ * #define EXIT_FAILURE 1 (from stdlib.h)
+ */
+#define EXIT_LOCKFILE 2
+#define EXIT_KERNEL_SOCKET 3 /* Failed netlink socket create */
+#define EXIT_KERNEL_BIND 4
+#define EXIT_KERNEL_SETSOCKOPT 5
+#define EXIT_CLUSTER_CKPT_INIT 6 /* Failed to init checkpoint */
+#define EXIT_QUEUE_NOMEM 7
+
+#define DM_ULOG_REQUEST_SIZE 1024
+
+#endif /* _LVM_CLOG_COMMON_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 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 Lesser General Public License v.2.1.
+ */
+#include "logging.h"
+#include "cluster.h"
+#include "compat.h"
+#include "xlate.h"
+
+#include <errno.h>
+
+/*
+ * Older versions of the log daemon communicate with different
+ * versions of the inter-machine communication structure, which
+ * varies in size and fields. The older versions append the
+ * standard upstream version of the structure to every request.
+ * COMPAT_OFFSET is where the upstream structure starts.
+ */
+#define COMPAT_OFFSET 256
+
+static void v5_data_endian_switch(struct clog_request *rq, int to_network __attribute__((unused)))
+{
+ int i, end;
+ int64_t *pi64;
+ uint64_t *pu64;
+ uint32_t rq_type = rq->u_rq.request_type & ~DM_ULOG_RESPONSE;
+
+ if (rq->u_rq.request_type & DM_ULOG_RESPONSE) {
+ switch (rq_type) {
+ case DM_ULOG_CTR:
+ case DM_ULOG_DTR:
+ LOG_ERROR("Invalid response type in endian switch");
+ exit(EXIT_FAILURE);
+
+ case DM_ULOG_PRESUSPEND:
+ case DM_ULOG_POSTSUSPEND:
+ case DM_ULOG_RESUME:
+ case DM_ULOG_FLUSH:
+ case DM_ULOG_MARK_REGION:
+ case DM_ULOG_CLEAR_REGION:
+ case DM_ULOG_SET_REGION_SYNC:
+ case DM_ULOG_CHECKPOINT_READY:
+ case DM_ULOG_MEMBER_JOIN:
+ case DM_ULOG_STATUS_INFO:
+ case DM_ULOG_STATUS_TABLE:
+ /* No outbound data */
+ break;
+
+ case DM_ULOG_GET_REGION_SIZE:
+ case DM_ULOG_GET_SYNC_COUNT:
+ pu64 = (uint64_t *)rq->u_rq.data;
+ *pu64 = xlate64(*pu64);
+ break;
+ case DM_ULOG_IS_CLEAN:
+ case DM_ULOG_IN_SYNC:
+ pi64 = (int64_t *)rq->u_rq.data;
+ *pi64 = xlate64(*pi64);
+ break;
+ case DM_ULOG_GET_RESYNC_WORK:
+ case DM_ULOG_IS_REMOTE_RECOVERING:
+ pi64 = (int64_t *)rq->u_rq.data;
+ pu64 = ((uint64_t *)rq->u_rq.data) + 1;
+ *pi64 = xlate64(*pi64);
+ *pu64 = xlate64(*pu64);
+ break;
+ default:
+ LOG_ERROR("Unknown request type, %u", rq_type);
+ return;
+ }
+ } else {
+ switch (rq_type) {
+ case DM_ULOG_CTR:
+ case DM_ULOG_DTR:
+ LOG_ERROR("Invalid request type in endian switch");
+ exit(EXIT_FAILURE);
+
+ case DM_ULOG_PRESUSPEND:
+ case DM_ULOG_POSTSUSPEND:
+ case DM_ULOG_RESUME:
+ case DM_ULOG_GET_REGION_SIZE:
+ case DM_ULOG_FLUSH:
+ case DM_ULOG_GET_RESYNC_WORK:
+ case DM_ULOG_GET_SYNC_COUNT:
+ case DM_ULOG_STATUS_INFO:
+ case DM_ULOG_STATUS_TABLE:
+ case DM_ULOG_CHECKPOINT_READY:
+ case DM_ULOG_MEMBER_JOIN:
+ /* No incoming data */
+ break;
+ case DM_ULOG_IS_CLEAN:
+ case DM_ULOG_IN_SYNC:
+ case DM_ULOG_IS_REMOTE_RECOVERING:
+ pu64 = (uint64_t *)rq->u_rq.data;
+ *pu64 = xlate64(*pu64);
+ break;
+ case DM_ULOG_MARK_REGION:
+ case DM_ULOG_CLEAR_REGION:
+ end = rq->u_rq.data_size/sizeof(uint64_t);
+
+ pu64 = (uint64_t *)rq->u_rq.data;
+ for (i = 0; i < end; i++)
+ pu64[i] = xlate64(pu64[i]);
+ break;
+ case DM_ULOG_SET_REGION_SYNC:
+ pu64 = (uint64_t *)rq->u_rq.data;
+ pi64 = ((int64_t *)rq->u_rq.data) + 1;
+ *pu64 = xlate64(*pu64);
+ *pi64 = xlate64(*pi64);
+ break;
+ default:
+ LOG_ERROR("Unknown request type, %u", rq_type);
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+static int v5_endian_to_network(struct clog_request *rq)
+{
+ int size;
+ struct dm_ulog_request *u_rq = &rq->u_rq;
+
+ size = sizeof(*rq) + u_rq->data_size;
+
+ u_rq->error = xlate32(u_rq->error);
+ u_rq->seq = xlate32(u_rq->seq);
+ u_rq->request_type = xlate32(u_rq->request_type);
+ u_rq->data_size = xlate64(u_rq->data_size);
+
+ rq->originator = xlate32(rq->originator);
+
+ v5_data_endian_switch(rq, 1);
+
+ return size;
+}
+
+int clog_request_to_network(struct clog_request *rq)
+{
+ int r;
+
+ /* FIXME: Remove this safety check */
+ if (rq->u.version[0] != xlate64(rq->u.version[1])) {
+ LOG_ERROR("Programmer error: version[0] must be LE");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Are we already running in the endian mode we send
+ * over the wire?
+ */
+ if (rq->u.version[0] == rq->u.version[1])
+ return 0;
+
+ r = v5_endian_to_network(rq);
+ if (r < 0)
+ return r;
+ return 0;
+}
+
+static int v5_endian_from_network(struct clog_request *rq)
+{
+ int size;
+ struct dm_ulog_request *u_rq = &rq->u_rq;
+
+ u_rq->error = xlate32(u_rq->error);
+ u_rq->seq = xlate32(u_rq->seq);
+ u_rq->request_type = xlate32(u_rq->request_type);
+ u_rq->data_size = xlate64(u_rq->data_size);
+
+ rq->originator = xlate32(rq->originator);
+
+ size = sizeof(*rq) + u_rq->data_size;
+
+ v5_data_endian_switch(rq, 0);
+
+ return size;
+}
+
+int clog_request_from_network(void *data, size_t data_len)
+{
+ uint64_t *vp = data;
+ uint64_t version = xlate64(vp[0]);
+ uint64_t unconverted_version = vp[1];
+ struct clog_request *rq = data;
+
+ switch (version) {
+ case 5: /* Upstream */
+ if (version == unconverted_version)
+ return 0;
+ break;
+ case 4: /* RHEL 5.[45] */
+ case 3: /* RHEL 5.3 */
+ case 2: /* RHEL 5.2 */
+ /* FIXME: still need to account for payload */
+ if (data_len < (COMPAT_OFFSET + sizeof(*rq)))
+ return -ENOSPC;
+
+ rq = (struct clog_request *)((char *)data + COMPAT_OFFSET);
+ break;
+ default:
+ LOG_ERROR("Unable to process cluster message: "
+ "Incompatible version");
+ return -EINVAL;
+ }
+
+ v5_endian_from_network(rq);
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 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 Lesser General Public License v.2.1.
+ */
+#ifndef _LVM_CLOG_COMPAT_H
+#define _LVM_CLOG_COMPAT_H
+
+/*
+ * The intermachine communication structure version are:
+ * 0: Unused
+ * 1: Never in the wild
+ * 2: RHEL 5.2
+ * 3: RHEL 5.3
+ * 4: RHEL 5.4, RHEL 5.5
+ * 5: RHEL 6, Current Upstream Format
+ */
+#define CLOG_TFR_VERSION 5
+
+int clog_request_to_network(struct clog_request *rq);
+int clog_request_from_network(void *data, size_t data_len);
+
+#endif /* _LVM_CLOG_COMPAT_H */
--- /dev/null
+/*
+ * Copyright (C) 2004-2009 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "logging.h"
+#include "functions.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#define BYTE_SHIFT 3
+
+/*
+ * Magic for persistent mirrors: "MiRr"
+ * Following on-disk header information is stolen from
+ * drivers/md/dm-log.c
+ */
+#define MIRROR_MAGIC 0x4D695272
+#define MIRROR_DISK_VERSION 2
+#define LOG_OFFSET 2
+
+#define RESYNC_HISTORY 50
+//static char resync_history[RESYNC_HISTORY][128];
+//static int idx = 0;
+#define LOG_SPRINT(_lc, f, arg...) do { \
+ lc->idx++; \
+ lc->idx = lc->idx % RESYNC_HISTORY; \
+ sprintf(lc->resync_history[lc->idx], f, ## arg); \
+ } while (0)
+
+struct log_header {
+ uint32_t magic;
+ uint32_t version;
+ uint64_t nr_regions;
+};
+
+struct log_c {
+ struct dm_list list;
+
+ char uuid[DM_UUID_LEN];
+ uint64_t luid;
+
+ time_t delay; /* limits how fast a resume can happen after suspend */
+ int touched;
+ int in_sync; /* An in-sync that stays set until suspend/resume */
+ uint32_t region_size;
+ uint32_t region_count;
+ uint64_t sync_count;
+
+ dm_bitset_t clean_bits;
+ dm_bitset_t sync_bits;
+ uint32_t recoverer;
+ uint64_t recovering_region; /* -1 means not recovering */
+ uint64_t skip_bit_warning; /* used to warn if region skipped */
+ int sync_search;
+
+ int resume_override;
+
+ uint32_t block_on_error;
+ enum sync {
+ DEFAULTSYNC, /* Synchronize if necessary */
+ NOSYNC, /* Devices known to be already in sync */
+ FORCESYNC, /* Force a sync to happen */
+ } sync;
+
+ uint32_t state; /* current operational state of the log */
+
+ struct dm_list mark_list;
+
+ uint32_t recovery_halted;
+ struct recovery_request *recovery_request_list;
+
+ int disk_fd; /* -1 means no disk log */
+ int log_dev_failed;
+ uint64_t disk_nr_regions;
+ size_t disk_size; /* size of disk_buffer in bytes */
+ void *disk_buffer; /* aligned memory for O_DIRECT */
+ int idx;
+ char resync_history[RESYNC_HISTORY][128];
+};
+
+struct mark_entry {
+ struct dm_list list;
+ uint32_t nodeid;
+ uint64_t region;
+};
+
+struct recovery_request {
+ uint64_t region;
+ struct recovery_request *next;
+};
+
+static DM_LIST_INIT(log_list);
+static DM_LIST_INIT(log_pending_list);
+
+static int log_test_bit(dm_bitset_t bs, int bit)
+{
+ return dm_bit(bs, bit) ? 1 : 0;
+}
+
+static void log_set_bit(struct log_c *lc, dm_bitset_t bs, int bit)
+{
+ dm_bit_set(bs, bit);
+ lc->touched = 1;
+}
+
+static void log_clear_bit(struct log_c *lc, dm_bitset_t bs, int bit)
+{
+ dm_bit_clear(bs, bit);
+ lc->touched = 1;
+}
+
+static uint64_t find_next_zero_bit(dm_bitset_t bs, unsigned start)
+{
+ for (; dm_bit(bs, start); start++)
+ if (start >= *bs)
+ return (uint64_t)-1;
+
+ return start;
+}
+
+static uint64_t count_bits32(dm_bitset_t bs)
+{
+ unsigned i, size = bs[0]/(unsigned)DM_BITS_PER_INT + 1;
+ unsigned count = 0;
+
+ for (i = 1; i <= size; i++)
+ count += hweight32(bs[i]);
+
+ return (uint64_t)count;
+}
+
+/*
+ * get_log
+ *
+ * Returns: log if found, NULL otherwise
+ */
+static struct log_c *get_log(const char *uuid, uint64_t luid)
+{
+ struct log_c *lc;
+
+ dm_list_iterate_items(lc, &log_list)
+ if (!strcmp(lc->uuid, uuid) &&
+ (!luid || (luid == lc->luid)))
+ return lc;
+
+ return NULL;
+}
+
+/*
+ * get_pending_log
+ *
+ * Pending logs are logs that have been 'clog_ctr'ed, but
+ * have not joined the CPG (via clog_resume).
+ *
+ * Returns: log if found, NULL otherwise
+ */
+static struct log_c *get_pending_log(const char *uuid, uint64_t luid)
+{
+ struct log_c *lc;
+
+ dm_list_iterate_items(lc, &log_pending_list)
+ if (!strcmp(lc->uuid, uuid) &&
+ (!luid || (luid == lc->luid)))
+ return lc;
+
+ return NULL;
+}
+
+static void header_to_disk(struct log_header *mem, struct log_header *disk)
+{
+ memcpy(disk, mem, sizeof(struct log_header));
+}
+
+static void header_from_disk(struct log_header *mem, struct log_header *disk)
+{
+ memcpy(mem, disk, sizeof(struct log_header));
+}
+
+static int rw_log(struct log_c *lc, int do_write)
+{
+ int r;
+
+ r = (int)lseek(lc->disk_fd, 0, SEEK_SET);
+ if (r < 0) {
+ LOG_ERROR("[%s] rw_log: lseek failure: %s",
+ SHORT_UUID(lc->uuid), strerror(errno));
+ return -errno;
+ }
+
+ if (do_write) {
+ /* FIXME Cope with full set of non-error conditions */
+ r = write(lc->disk_fd, lc->disk_buffer, lc->disk_size);
+ if (r < 0) {
+ LOG_ERROR("[%s] rw_log: write failure: %s",
+ SHORT_UUID(lc->uuid), strerror(errno));
+ return -EIO; /* Failed disk write */
+ }
+ return 0;
+ }
+
+ /* Read */
+ /* FIXME Cope with full set of non-error conditions */
+ r = read(lc->disk_fd, lc->disk_buffer, lc->disk_size);
+ if (r < 0)
+ LOG_ERROR("[%s] rw_log: read failure: %s",
+ SHORT_UUID(lc->uuid), strerror(errno));
+ if (r != lc->disk_size)
+ return -EIO; /* Failed disk read */
+ return 0;
+}
+
+/*
+ * read_log
+ * @lc
+ *
+ * Valid return codes:
+ * -EINVAL: Invalid header, bits not copied
+ * -EIO: Unable to read disk log
+ * 0: Valid header, disk bit -> lc->clean_bits
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+static int read_log(struct log_c *lc)
+{
+ struct log_header lh;
+ size_t bitset_size;
+
+ memset(&lh, 0, sizeof(struct log_header));
+
+ if (rw_log(lc, 0))
+ return -EIO; /* Failed disk read */
+
+ header_from_disk(&lh, lc->disk_buffer);
+ if (lh.magic != MIRROR_MAGIC)
+ return -EINVAL;
+
+ lc->disk_nr_regions = lh.nr_regions;
+
+ /* Read disk bits into sync_bits */
+ bitset_size = lc->region_count / 8;
+ bitset_size += (lc->region_count % 8) ? 1 : 0;
+
+ /* 'lc->clean_bits + 1' becasue dm_bitset_t leads with a uint32_t */
+ memcpy(lc->clean_bits + 1, (char *)lc->disk_buffer + 1024, bitset_size);
+
+ return 0;
+}
+
+/*
+ * write_log
+ * @lc
+ *
+ * Returns: 0 on success, -EIO on failure
+ */
+static int write_log(struct log_c *lc)
+{
+ struct log_header lh;
+ size_t bitset_size;
+
+ lh.magic = MIRROR_MAGIC;
+ lh.version = MIRROR_DISK_VERSION;
+ lh.nr_regions = lc->region_count;
+
+ header_to_disk(&lh, lc->disk_buffer);
+
+ /* Write disk bits from clean_bits */
+ bitset_size = lc->region_count / 8;
+ bitset_size += (lc->region_count % 8) ? 1 : 0;
+
+ /* 'lc->clean_bits + 1' becasue dm_bitset_t leads with a uint32_t */
+ memcpy((char *)lc->disk_buffer + 1024, lc->clean_bits + 1, bitset_size);
+
+ if (rw_log(lc, 1)) {
+ lc->log_dev_failed = 1;
+ return -EIO; /* Failed disk write */
+ }
+ return 0;
+}
+
+/* FIXME Rewrite this function taking advantage of the udev changes (where in use) to improve its efficiency! */
+static int find_disk_path(char *major_minor_str, char *path_rtn, int *unlink_path __attribute__((unused)))
+{
+ int r;
+ DIR *dp;
+ struct dirent *dep;
+ struct stat statbuf;
+ int major, minor;
+
+ if (!strstr(major_minor_str, ":")) {
+ r = stat(major_minor_str, &statbuf);
+ if (r)
+ return -errno;
+ if (!S_ISBLK(statbuf.st_mode))
+ return -EINVAL;
+ sprintf(path_rtn, "%s", major_minor_str);
+ return 0;
+ }
+
+ r = sscanf(major_minor_str, "%d:%d", &major, &minor);
+ if (r != 2)
+ return -EINVAL;
+
+ /* FIXME dm_dir() */
+ LOG_DBG("Checking /dev/mapper for device %d:%d", major, minor);
+ /* Check /dev/mapper dir */
+ dp = opendir("/dev/mapper");
+ if (!dp)
+ return -ENOENT;
+
+ while ((dep = readdir(dp)) != NULL) {
+ /*
+ * FIXME: This is racy. By the time the path is used,
+ * it may point to something else. 'fstat' will be
+ * required upon opening to ensure we got what we
+ * wanted.
+ */
+
+ sprintf(path_rtn, "/dev/mapper/%s", dep->d_name);
+ stat(path_rtn, &statbuf);
+ if (S_ISBLK(statbuf.st_mode) &&
+ (major(statbuf.st_rdev) == major) &&
+ (minor(statbuf.st_rdev) == minor)) {
+ LOG_DBG(" %s: YES", dep->d_name);
+ closedir(dp);
+ return 0;
+ } else {
+ LOG_DBG(" %s: NO", dep->d_name);
+ }
+ }
+
+ closedir(dp);
+
+ /* FIXME Find out why this was here and deal with underlying problem. */
+ LOG_DBG("Path not found for %d/%d", major, minor);
+ return -ENOENT;
+
+ // LOG_DBG("Creating /dev/mapper/%d-%d", major, minor);
+ // sprintf(path_rtn, "/dev/mapper/%d-%d", major, minor);
+ // r = mknod(path_rtn, S_IFBLK | S_IRUSR | S_IWUSR, MKDEV(major, minor));
+ /*
+ * If we have to make the path, we unlink it after we open it
+ */
+ // *unlink_path = 1;
+ // return r ? -errno : 0;
+}
+
+static int _clog_ctr(char *uuid, uint64_t luid,
+ int argc, char **argv, uint64_t device_size)
+{
+ int i;
+ int r = 0;
+ char *p;
+ uint64_t region_size;
+ uint64_t region_count;
+ struct log_c *lc = NULL;
+ enum sync log_sync = DEFAULTSYNC;
+ uint32_t block_on_error = 0;
+
+ int disk_log = 0;
+ char disk_path[128];
+ int unlink_path = 0;
+ size_t page_size;
+ int pages;
+
+ /* If core log request, then argv[0] will be region_size */
+ if (!strtoll(argv[0], &p, 0) || *p) {
+ disk_log = 1;
+
+ if ((argc < 2) || (argc > 4)) {
+ LOG_ERROR("Too %s arguments to clustered-disk log type",
+ (argc < 3) ? "few" : "many");
+ r = -EINVAL;
+ goto fail;
+ }
+
+ r = find_disk_path(argv[0], disk_path, &unlink_path);
+ if (r) {
+ LOG_ERROR("Unable to find path to device %s", argv[0]);
+ goto fail;
+ }
+ LOG_DBG("Clustered log disk is %s", disk_path);
+ } else {
+ disk_log = 0;
+
+ if ((argc < 1) || (argc > 3)) {
+ LOG_ERROR("Too %s arguments to clustered-core log type",
+ (argc < 2) ? "few" : "many");
+ r = -EINVAL;
+ goto fail;
+ }
+ }
+
+ if (!(region_size = strtoll(argv[disk_log], &p, 0)) || *p) {
+ LOG_ERROR("Invalid region_size argument to clustered-%s log type",
+ (disk_log) ? "disk" : "core");
+ r = -EINVAL;
+ goto fail;
+ }
+
+ region_count = device_size / region_size;
+ if (device_size % region_size) {
+ /*
+ * I can't remember if device_size must be a multiple
+ * of region_size, so check it anyway.
+ */
+ region_count++;
+ }
+
+ for (i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "sync"))
+ log_sync = FORCESYNC;
+ else if (!strcmp(argv[i], "nosync"))
+ log_sync = NOSYNC;
+ else if (!strcmp(argv[i], "block_on_error"))
+ block_on_error = 1;
+ }
+
+ lc = dm_zalloc(sizeof(*lc));
+ if (!lc) {
+ LOG_ERROR("Unable to allocate cluster log context");
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ lc->region_size = region_size;
+ lc->region_count = region_count;
+ lc->sync = log_sync;
+ lc->block_on_error = block_on_error;
+ lc->sync_search = 0;
+ lc->recovering_region = (uint64_t)-1;
+ lc->skip_bit_warning = region_count;
+ lc->disk_fd = -1;
+ lc->log_dev_failed = 0;
+ strncpy(lc->uuid, uuid, DM_UUID_LEN);
+ lc->luid = luid;
+
+ if (get_log(lc->uuid, lc->luid) ||
+ get_pending_log(lc->uuid, lc->luid)) {
+ LOG_ERROR("[%s/%" PRIu64 "u] Log already exists, unable to create.",
+ SHORT_UUID(lc->uuid), lc->luid);
+ dm_free(lc);
+ return -EINVAL;
+ }
+
+ dm_list_init(&lc->mark_list);
+
+ lc->clean_bits = dm_bitset_create(NULL, region_count);
+ if (!lc->clean_bits) {
+ LOG_ERROR("Unable to allocate clean bitset");
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ lc->sync_bits = dm_bitset_create(NULL, region_count);
+ if (!lc->sync_bits) {
+ LOG_ERROR("Unable to allocate sync bitset");
+ r = -ENOMEM;
+ goto fail;
+ }
+ if (log_sync == NOSYNC)
+ dm_bit_set_all(lc->sync_bits);
+
+ lc->sync_count = (log_sync == NOSYNC) ? region_count : 0;
+
+ if (disk_log) {
+ page_size = sysconf(_SC_PAGESIZE);
+ pages = *(lc->clean_bits) / page_size;
+ pages += *(lc->clean_bits) % page_size ? 1 : 0;
+ pages += 1; /* for header */
+
+ r = open(disk_path, O_RDWR | O_DIRECT);
+ if (r < 0) {
+ LOG_ERROR("Unable to open log device, %s: %s",
+ disk_path, strerror(errno));
+ r = errno;
+ goto fail;
+ }
+ if (unlink_path)
+ unlink(disk_path);
+
+ lc->disk_fd = r;
+ lc->disk_size = pages * page_size;
+
+ r = posix_memalign(&(lc->disk_buffer), page_size,
+ lc->disk_size);
+ if (r) {
+ LOG_ERROR("Unable to allocate memory for disk_buffer");
+ goto fail;
+ }
+ memset(lc->disk_buffer, 0, lc->disk_size);
+ LOG_DBG("Disk log ready");
+ }
+
+ dm_list_add(&log_pending_list, &lc->list);
+
+ return 0;
+fail:
+ if (lc) {
+ if (lc->disk_fd >= 0 && close(lc->disk_fd))
+ LOG_ERROR("Close device error, %s: %s",
+ disk_path, strerror(errno));
+ free(lc->disk_buffer);
+ dm_free(lc->sync_bits);
+ dm_free(lc->clean_bits);
+ dm_free(lc);
+ }
+ return r;
+}
+
+/*
+ * clog_ctr
+ * @rq
+ *
+ * rq->data should contain constructor string as follows:
+ * <log_type> [disk] <region_size> [[no]sync] <device_len>
+ * The kernel is responsible for adding the <dev_len> argument
+ * to the end; otherwise, we cannot compute the region_count.
+ *
+ * FIXME: Currently relies on caller to fill in rq->error
+ */
+static int clog_dtr(struct dm_ulog_request *rq);
+static int clog_ctr(struct dm_ulog_request *rq)
+{
+ int argc, i, r = 0;
+ char *p, **argv = NULL;
+ char *dev_size_str;
+ uint64_t device_size;
+
+ /* Sanity checks */
+ if (!rq->data_size) {
+ LOG_ERROR("Received constructor request with no data");
+ return -EINVAL;
+ }
+
+ if (strlen(rq->data) > rq->data_size) {
+ LOG_ERROR("Received constructor request with bad data");
+ LOG_ERROR("strlen(rq->data)[%d] != rq->data_size[%llu]",
+ (int)strlen(rq->data),
+ (unsigned long long)rq->data_size);
+ LOG_ERROR("rq->data = '%s' [%d]",
+ rq->data, (int)strlen(rq->data));
+ return -EINVAL;
+ }
+
+ /* Split up args */
+ for (argc = 0, p = rq->data; (p = strstr(p, " ")); p++, argc++)
+ *p = '\0';
+
+ argv = malloc(argc * sizeof(char *));
+ if (!argv)
+ return -ENOMEM;
+
+ p = dev_size_str = rq->data;
+ p += strlen(p) + 1;
+ for (i = 0; i < argc; i++, p = p + strlen(p) + 1)
+ argv[i] = p;
+
+ if (strcmp(argv[0], "clustered-disk") &&
+ strcmp(argv[0], "clustered-core")) {
+ LOG_ERROR("Unsupported userspace log type, \"%s\"", argv[0]);
+ free(argv);
+ return -EINVAL;
+ }
+
+ if (!(device_size = strtoll(dev_size_str, &p, 0)) || *p) {
+ LOG_ERROR("Invalid device size argument: %s", dev_size_str);
+ free(argv);
+ return -EINVAL;
+ }
+
+ r = _clog_ctr(rq->uuid, rq->luid, argc - 1, argv + 1, device_size);
+
+ /* We join the CPG when we resume */
+
+ /* No returning data */
+ rq->data_size = 0;
+
+ if (r) {
+ LOG_ERROR("Failed to create cluster log (%s)", rq->uuid);
+ for (i = 0; i < argc; i++)
+ LOG_ERROR("argv[%d] = %s", i, argv[i]);
+ }
+ else
+ LOG_DBG("[%s] Cluster log created",
+ SHORT_UUID(rq->uuid));
+
+ free(argv);
+ return r;
+}
+
+/*
+ * clog_dtr
+ * @rq
+ *
+ */
+static int clog_dtr(struct dm_ulog_request *rq)
+{
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (lc) {
+ /*
+ * The log should not be on the official list. There
+ * should have been a suspend first.
+ */
+ LOG_ERROR("[%s] DTR before SUS: leaving CPG",
+ SHORT_UUID(rq->uuid));
+ destroy_cluster_cpg(rq->uuid);
+ } else if (!(lc = get_pending_log(rq->uuid, rq->luid))) {
+ LOG_ERROR("clog_dtr called on log that is not official or pending");
+ return -EINVAL;
+ }
+
+ LOG_DBG("[%s] Cluster log removed", SHORT_UUID(lc->uuid));
+
+ dm_list_del(&lc->list);
+ if (lc->disk_fd != -1)
+ close(lc->disk_fd);
+ if (lc->disk_buffer)
+ free(lc->disk_buffer);
+ dm_free(lc->clean_bits);
+ dm_free(lc->sync_bits);
+ dm_free(lc);
+
+ return 0;
+}
+
+/*
+ * clog_presuspend
+ * @rq
+ *
+ */
+static int clog_presuspend(struct dm_ulog_request *rq)
+{
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ if (lc->touched)
+ LOG_DBG("WARNING: log still marked as 'touched' during suspend");
+
+ lc->recovery_halted = 1;
+
+ return 0;
+}
+
+/*
+ * clog_postsuspend
+ * @rq
+ *
+ */
+static int clog_postsuspend(struct dm_ulog_request *rq)
+{
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ LOG_DBG("[%s] clog_postsuspend: leaving CPG", SHORT_UUID(lc->uuid));
+ destroy_cluster_cpg(rq->uuid);
+
+ lc->state = LOG_SUSPENDED;
+ lc->recovering_region = (uint64_t)-1;
+ lc->recoverer = (uint32_t)-1;
+ lc->delay = time(NULL);
+
+ return 0;
+}
+
+/*
+ * cluster_postsuspend
+ * @rq
+ *
+ */
+int cluster_postsuspend(char *uuid, uint64_t luid)
+{
+ struct log_c *lc = get_log(uuid, luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ LOG_DBG("[%s] clog_postsuspend: finalizing", SHORT_UUID(lc->uuid));
+ lc->resume_override = 0;
+
+ /* move log to pending list */
+ dm_list_del(&lc->list);
+ dm_list_add(&log_pending_list, &lc->list);
+
+ return 0;
+}
+
+/*
+ * clog_resume
+ * @rq
+ *
+ * Does the main work of resuming.
+ */
+static int clog_resume(struct dm_ulog_request *rq)
+{
+ uint32_t i;
+ int commit_log = 0;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ lc->in_sync = 0;
+ switch (lc->resume_override) {
+ case 1000:
+ LOG_ERROR("[%s] Additional resume issued before suspend",
+ SHORT_UUID(rq->uuid));
+#ifdef DEBUG
+ kill(getpid(), SIGUSR1);
+#endif
+ return 0;
+ case 0:
+ lc->resume_override = 1000;
+ if (lc->disk_fd == -1) {
+ LOG_DBG("[%s] Master resume.",
+ SHORT_UUID(lc->uuid));
+ goto no_disk;
+ }
+
+ LOG_DBG("[%s] Master resume: reading disk log",
+ SHORT_UUID(lc->uuid));
+ commit_log = 1;
+ break;
+ case 1:
+ LOG_ERROR("Error:: partial bit loading (just sync_bits)");
+ return -EINVAL;
+ case 2:
+ LOG_ERROR("Error:: partial bit loading (just clean_bits)");
+ return -EINVAL;
+ case 3:
+ LOG_DBG("[%s] Non-master resume: bits pre-loaded",
+ SHORT_UUID(lc->uuid));
+ lc->resume_override = 1000;
+ goto out;
+ default:
+ LOG_ERROR("Error:: multiple loading of bits (%d)",
+ lc->resume_override);
+ return -EINVAL;
+ }
+
+ if (lc->log_dev_failed) {
+ LOG_ERROR("Log device has failed, unable to read bits");
+ rq->error = 0; /* We can handle this so far */
+ lc->disk_nr_regions = 0;
+ } else
+ rq->error = read_log(lc);
+
+ switch (rq->error) {
+ case 0:
+ if (lc->disk_nr_regions < lc->region_count)
+ LOG_DBG("[%s] Mirror has grown, updating log bits",
+ SHORT_UUID(lc->uuid));
+ else if (lc->disk_nr_regions > lc->region_count)
+ LOG_DBG("[%s] Mirror has shrunk, updating log bits",
+ SHORT_UUID(lc->uuid));
+ break;
+ case -EINVAL:
+ LOG_DBG("[%s] (Re)initializing mirror log - resync issued.",
+ SHORT_UUID(lc->uuid));
+ lc->disk_nr_regions = 0;
+ break;
+ default:
+ LOG_ERROR("Failed to read disk log");
+ lc->disk_nr_regions = 0;
+ break;
+ }
+
+no_disk:
+ /* If mirror has grown, set bits appropriately */
+ if (lc->sync == NOSYNC)
+ for (i = lc->disk_nr_regions; i < lc->region_count; i++)
+ log_set_bit(lc, lc->clean_bits, i);
+ else
+ for (i = lc->disk_nr_regions; i < lc->region_count; i++)
+ log_clear_bit(lc, lc->clean_bits, i);
+
+ /* Clear any old bits if device has shrunk */
+ for (i = lc->region_count; i % 32; i++)
+ log_clear_bit(lc, lc->clean_bits, i);
+
+ /* copy clean across to sync */
+ dm_bit_copy(lc->sync_bits, lc->clean_bits);
+
+ if (commit_log && (lc->disk_fd >= 0)) {
+ rq->error = write_log(lc);
+ if (rq->error)
+ LOG_ERROR("Failed initial disk log write");
+ else
+ LOG_DBG("Disk log initialized");
+ lc->touched = 0;
+ }
+out:
+ /*
+ * Clear any old bits if device has shrunk - necessary
+ * for non-master resume
+ */
+ for (i = lc->region_count; i % 32; i++) {
+ log_clear_bit(lc, lc->clean_bits, i);
+ log_clear_bit(lc, lc->sync_bits, i);
+ }
+
+ lc->sync_count = count_bits32(lc->sync_bits);
+
+ LOG_SPRINT(lc, "[%s] Initial sync_count = %llu",
+ SHORT_UUID(lc->uuid), (unsigned long long)lc->sync_count);
+ lc->sync_search = 0;
+ lc->state = LOG_RESUMED;
+ lc->recovery_halted = 0;
+
+ return rq->error;
+}
+
+/*
+ * local_resume
+ * @rq
+ *
+ * If the log is pending, we must first join the cpg and
+ * put the log in the official list.
+ *
+ */
+int local_resume(struct dm_ulog_request *rq)
+{
+ int r;
+ time_t t;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc) {
+ /* Is the log in the pending list? */
+ lc = get_pending_log(rq->uuid, rq->luid);
+ if (!lc) {
+ LOG_ERROR("clog_resume called on log that is not official or pending");
+ return -EINVAL;
+ }
+
+ t = time(NULL);
+ t -= lc->delay;
+ /*
+ * This should be considered a temporary fix. It addresses
+ * a problem that exists when nodes suspend/resume in rapid
+ * succession. While the problem is very rare, it has been
+ * seen to happen in real-world-like testing.
+ *
+ * The problem:
+ * - Node A joins cluster
+ * - Node B joins cluster
+ * - Node A prepares checkpoint
+ * - Node A gets ready to write checkpoint
+ * - Node B leaves
+ * - Node B joins
+ * - Node A finishes write of checkpoint
+ * - Node B receives checkpoint meant for previous session
+ * -- Node B can now be non-coherent
+ *
+ * This timer will solve the problem for now, but could be
+ * replaced by a generation number sent with the resume
+ * command from the kernel. The generation number would
+ * be included in the name of the checkpoint to prevent
+ * reading stale data.
+ */
+ if ((t < 3) && (t >= 0))
+ sleep(3 - t);
+
+ /* Join the CPG */
+ r = create_cluster_cpg(rq->uuid, rq->luid);
+ if (r) {
+ LOG_ERROR("clog_resume: Failed to create cluster CPG");
+ return r;
+ }
+
+ /* move log to official list */
+ dm_list_del(&lc->list);
+ dm_list_add(&log_list, &lc->list);
+ }
+
+ return 0;
+}
+
+/*
+ * clog_get_region_size
+ * @rq
+ *
+ * Since this value doesn't change, the kernel
+ * should not need to talk to server to get this
+ * The function is here for completness
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+static int clog_get_region_size(struct dm_ulog_request *rq)
+{
+ uint64_t *rtn = (uint64_t *)rq->data;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc && !(lc = get_pending_log(rq->uuid, rq->luid)))
+ return -EINVAL;
+
+ *rtn = lc->region_size;
+ rq->data_size = sizeof(*rtn);
+
+ return 0;
+}
+
+/*
+ * clog_is_clean
+ * @rq
+ *
+ * Returns: 1 if clean, 0 otherwise
+ */
+static int clog_is_clean(struct dm_ulog_request *rq)
+{
+ int64_t *rtn = (int64_t *)rq->data;
+ uint64_t *region = (uint64_t *)rq->data;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ *rtn = log_test_bit(lc->clean_bits, *region);
+ rq->data_size = sizeof(*rtn);
+
+ return 0;
+}
+
+/*
+ * clog_in_sync
+ * @rq
+ *
+ * We ignore any request for non-block. That
+ * should be handled elsewhere. (If the request
+ * has come this far, it has already blocked.)
+ *
+ * Returns: 1 if in-sync, 0 otherwise
+ */
+static int clog_in_sync(struct dm_ulog_request *rq)
+{
+ int64_t *rtn = (int64_t *)rq->data;
+ uint64_t *region_p = (uint64_t *)rq->data;
+ uint64_t region = *region_p;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ if (region > lc->region_count)
+ return -EINVAL;
+
+ *rtn = log_test_bit(lc->sync_bits, region);
+
+ /*
+ * If the mirror was successfully recovered, we want to always
+ * force every machine to write to all devices - otherwise,
+ * corruption will occur. Here's how:
+ * Node1 suffers a failure and marks a region out-of-sync
+ * Node2 attempts a write, gets by is_remote_recovering,
+ * and queries the sync status of the region - finding
+ * it out-of-sync.
+ * Node2 thinks the write should be a nosync write, but it
+ * hasn't suffered the drive failure that Node1 has yet.
+ * It then issues a generic_make_request directly to
+ * the primary image only - which is exactly the device
+ * that has suffered the failure.
+ * Node2 suffers a lost write - which completely bypasses the
+ * mirror layer because it had gone through generic_m_r.
+ * The file system will likely explode at this point due to
+ * I/O errors. If it wasn't the primary that failed, it is
+ * easily possible in this case to issue writes to just one
+ * of the remaining images - also leaving the mirror inconsistent.
+ *
+ * We let in_sync() return 1 in a cluster regardless of what is
+ * in the bitmap once recovery has successfully completed on a
+ * mirror. This ensures the mirroring code will continue to
+ * attempt to write to all mirror images. The worst that can
+ * happen for reads is that additional read attempts may be
+ * taken.
+ *
+ * Futher investigation may be required to determine if there are
+ * similar possible outcomes when the mirror is in the process of
+ * recovering. In that case, lc->in_sync would not have been set
+ * yet.
+ */
+ if (!*rtn && lc->in_sync)
+ *rtn = 1;
+
+ if (*rtn)
+ LOG_DBG("[%s] Region is in-sync: %llu",
+ SHORT_UUID(lc->uuid), (unsigned long long)region);
+ else
+ LOG_DBG("[%s] Region is not in-sync: %llu",
+ SHORT_UUID(lc->uuid), (unsigned long long)region);
+
+ rq->data_size = sizeof(*rtn);
+
+ return 0;
+}
+
+/*
+ * clog_flush
+ * @rq
+ *
+ */
+static int clog_flush(struct dm_ulog_request *rq, int server)
+{
+ int r = 0;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ if (!lc->touched)
+ return 0;
+
+ /*
+ * Do the actual flushing of the log only
+ * if we are the server.
+ */
+ if (server && (lc->disk_fd >= 0)) {
+ r = rq->error = write_log(lc);
+ if (r)
+ LOG_ERROR("[%s] Error writing to disk log",
+ SHORT_UUID(lc->uuid));
+ else
+ LOG_DBG("[%s] Disk log written", SHORT_UUID(lc->uuid));
+ }
+
+ lc->touched = 0;
+
+ return r;
+
+}
+
+/*
+ * mark_region
+ * @lc
+ * @region
+ * @who
+ *
+ * Put a mark region request in the tree for tracking.
+ *
+ * Returns: 0 on success, -EXXX on error
+ */
+static int mark_region(struct log_c *lc, uint64_t region, uint32_t who)
+{
+ int found = 0;
+ struct mark_entry *m;
+
+ dm_list_iterate_items(m, &lc->mark_list)
+ if (m->region == region) {
+ found = 1;
+ if (m->nodeid == who)
+ return 0;
+ }
+
+ if (!found)
+ log_clear_bit(lc, lc->clean_bits, region);
+
+ /*
+ * Save allocation until here - if there is a failure,
+ * at least we have cleared the bit.
+ */
+ m = malloc(sizeof(*m));
+ if (!m) {
+ LOG_ERROR("Unable to allocate space for mark_entry: %llu/%u",
+ (unsigned long long)region, who);
+ return -ENOMEM;
+ }
+
+ m->nodeid = who;
+ m->region = region;
+ dm_list_add(&lc->mark_list, &m->list);
+
+ return 0;
+}
+
+/*
+ * clog_mark_region
+ * @rq
+ *
+ * rq may contain more than one mark request. We
+ * can determine the number from the 'data_size' field.
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+static int clog_mark_region(struct dm_ulog_request *rq, uint32_t originator)
+{
+ int r;
+ int count;
+ uint64_t *region;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ if (rq->data_size % sizeof(uint64_t)) {
+ LOG_ERROR("Bad data size given for mark_region request");
+ return -EINVAL;
+ }
+
+ count = rq->data_size / sizeof(uint64_t);
+ region = (uint64_t *)&rq->data;
+
+ for (; count > 0; count--, region++) {
+ r = mark_region(lc, *region, originator);
+ if (r)
+ return r;
+ }
+
+ rq->data_size = 0;
+
+ return 0;
+}
+
+static int clear_region(struct log_c *lc, uint64_t region, uint32_t who)
+{
+ int other_matches = 0;
+ struct mark_entry *m, *n;
+
+ dm_list_iterate_items_safe(m, n, &lc->mark_list)
+ if (m->region == region) {
+ if (m->nodeid == who) {
+ dm_list_del(&m->list);
+ free(m);
+ } else
+ other_matches = 1;
+ }
+
+ /*
+ * Clear region if:
+ * 1) It is in-sync
+ * 2) There are no other machines that have it marked
+ */
+ if (!other_matches && log_test_bit(lc->sync_bits, region))
+ log_set_bit(lc, lc->clean_bits, region);
+
+ return 0;
+}
+
+/*
+ * clog_clear_region
+ * @rq
+ *
+ * rq may contain more than one clear request. We
+ * can determine the number from the 'data_size' field.
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+static int clog_clear_region(struct dm_ulog_request *rq, uint32_t originator)
+{
+ int r;
+ int count;
+ uint64_t *region;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ if (rq->data_size % sizeof(uint64_t)) {
+ LOG_ERROR("Bad data size given for clear_region request");
+ return -EINVAL;
+ }
+
+ count = rq->data_size / sizeof(uint64_t);
+ region = (uint64_t *)&rq->data;
+
+ for (; count > 0; count--, region++) {
+ r = clear_region(lc, *region, originator);
+ if (r)
+ return r;
+ }
+
+ rq->data_size = 0;
+
+ return 0;
+}
+
+/*
+ * clog_get_resync_work
+ * @rq
+ *
+ */
+static int clog_get_resync_work(struct dm_ulog_request *rq, uint32_t originator)
+{
+ struct {
+ int64_t i;
+ uint64_t r;
+ } *pkg = (void *)rq->data;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ rq->data_size = sizeof(*pkg);
+ pkg->i = 0;
+
+ if (lc->sync_search >= lc->region_count) {
+ /*
+ * FIXME: handle intermittent errors during recovery
+ * by resetting sync_search... but not to many times.
+ */
+ LOG_SPRINT(lc, "GET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+ "Recovery finished",
+ rq->seq, SHORT_UUID(lc->uuid), originator);
+ return 0;
+ }
+
+ if (lc->recovering_region != (uint64_t)-1) {
+ if (lc->recoverer == originator) {
+ LOG_SPRINT(lc, "GET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+ "Re-requesting work (%llu)",
+ rq->seq, SHORT_UUID(lc->uuid), originator,
+ (unsigned long long)lc->recovering_region);
+ pkg->r = lc->recovering_region;
+ pkg->i = 1;
+ LOG_COND(log_resend_requests, "***** RE-REQUEST *****");
+ } else {
+ LOG_SPRINT(lc, "GET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+ "Someone already recovering (%llu)",
+ rq->seq, SHORT_UUID(lc->uuid), originator,
+ (unsigned long long)lc->recovering_region);
+ }
+
+ return 0;
+ }
+
+ while (lc->recovery_request_list) {
+ struct recovery_request *del;
+
+ del = lc->recovery_request_list;
+ lc->recovery_request_list = del->next;
+
+ pkg->r = del->region;
+ free(del);
+
+ if (!log_test_bit(lc->sync_bits, pkg->r)) {
+ LOG_SPRINT(lc, "GET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+ "Assigning priority resync work (%llu)",
+ rq->seq, SHORT_UUID(lc->uuid), originator,
+ (unsigned long long)pkg->r);
+ pkg->i = 1;
+ lc->recovering_region = pkg->r;
+ lc->recoverer = originator;
+ return 0;
+ }
+ }
+
+ pkg->r = find_next_zero_bit(lc->sync_bits, lc->sync_search);
+
+ if (pkg->r >= lc->region_count) {
+ LOG_SPRINT(lc, "GET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+ "Resync work complete.",
+ rq->seq, SHORT_UUID(lc->uuid), originator);
+ lc->sync_search = lc->region_count + 1;
+ return 0;
+ }
+
+ lc->sync_search = pkg->r + 1;
+
+ LOG_SPRINT(lc, "GET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+ "Assigning resync work (%llu)",
+ rq->seq, SHORT_UUID(lc->uuid), originator,
+ (unsigned long long)pkg->r);
+ pkg->i = 1;
+ lc->recovering_region = pkg->r;
+ lc->recoverer = originator;
+
+ return 0;
+}
+
+/*
+ * clog_set_region_sync
+ * @rq
+ */
+static int clog_set_region_sync(struct dm_ulog_request *rq, uint32_t originator)
+{
+ struct {
+ uint64_t region;
+ int64_t in_sync;
+ } *pkg = (void *)rq->data;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ lc->recovering_region = (uint64_t)-1;
+
+ if (pkg->in_sync) {
+ if (log_test_bit(lc->sync_bits, pkg->region)) {
+ LOG_SPRINT(lc, "SET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+ "Region already set (%llu)",
+ rq->seq, SHORT_UUID(lc->uuid), originator,
+ (unsigned long long)pkg->region);
+ } else {
+ log_set_bit(lc, lc->sync_bits, pkg->region);
+ lc->sync_count++;
+
+ /* The rest of this section is all for debugging */
+ LOG_SPRINT(lc, "SET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+ "Setting region (%llu)",
+ rq->seq, SHORT_UUID(lc->uuid), originator,
+ (unsigned long long)pkg->region);
+ if (pkg->region == lc->skip_bit_warning)
+ lc->skip_bit_warning = lc->region_count;
+
+ if (pkg->region > (lc->skip_bit_warning + 5)) {
+ LOG_SPRINT(lc, "*** Region #%llu skipped during recovery ***",
+ (unsigned long long)lc->skip_bit_warning);
+ lc->skip_bit_warning = lc->region_count;
+#ifdef DEBUG
+ kill(getpid(), SIGUSR1);
+#endif
+ }
+
+ if (!log_test_bit(lc->sync_bits,
+ (pkg->region) ? pkg->region - 1 : 0)) {
+ LOG_SPRINT(lc, "*** Previous bit not set ***");
+ lc->skip_bit_warning = (pkg->region) ?
+ pkg->region - 1 : 0;
+ }
+ }
+ } else if (log_test_bit(lc->sync_bits, pkg->region)) {
+ lc->sync_count--;
+ log_clear_bit(lc, lc->sync_bits, pkg->region);
+ LOG_SPRINT(lc, "SET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+ "Unsetting region (%llu)",
+ rq->seq, SHORT_UUID(lc->uuid), originator,
+ (unsigned long long)pkg->region);
+ }
+
+ if (lc->sync_count != count_bits32(lc->sync_bits)) {
+ unsigned long long reset = count_bits32(lc->sync_bits);
+
+ LOG_SPRINT(lc, "SET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+ "sync_count(%llu) != bitmap count(%llu)",
+ rq->seq, SHORT_UUID(lc->uuid), originator,
+ (unsigned long long)lc->sync_count, reset);
+#ifdef DEBUG
+ kill(getpid(), SIGUSR1);
+#endif
+ lc->sync_count = reset;
+ }
+
+ if (lc->sync_count > lc->region_count)
+ LOG_SPRINT(lc, "SET - SEQ#=%u, UUID=%s, nodeid = %u:: "
+ "(lc->sync_count > lc->region_count) - this is bad",
+ rq->seq, SHORT_UUID(lc->uuid), originator);
+
+ if (lc->sync_count == lc->region_count)
+ lc->in_sync = 1;
+
+ rq->data_size = 0;
+ return 0;
+}
+
+/*
+ * clog_get_sync_count
+ * @rq
+ */
+static int clog_get_sync_count(struct dm_ulog_request *rq, uint32_t originator)
+{
+ uint64_t *sync_count = (uint64_t *)rq->data;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ /*
+ * FIXME: Mirror requires us to be able to ask for
+ * the sync count while pending... but I don't like
+ * it because other machines may not be suspended and
+ * the stored value may not be accurate.
+ */
+ if (!lc)
+ lc = get_pending_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ *sync_count = lc->sync_count;
+
+ rq->data_size = sizeof(*sync_count);
+
+ if (lc->sync_count != count_bits32(lc->sync_bits)) {
+ unsigned long long reset = count_bits32(lc->sync_bits);
+
+ LOG_SPRINT(lc, "get_sync_count - SEQ#=%u, UUID=%s, nodeid = %u:: "
+ "sync_count(%llu) != bitmap count(%llu)",
+ rq->seq, SHORT_UUID(lc->uuid), originator,
+ (unsigned long long)lc->sync_count, reset);
+#ifdef DEBUG
+ kill(getpid(), SIGUSR1);
+#endif
+ lc->sync_count = reset;
+ }
+
+ return 0;
+}
+
+static int core_status_info(struct log_c *lc __attribute__((unused)), struct dm_ulog_request *rq)
+{
+ int r;
+ char *data = (char *)rq->data;
+
+ r = sprintf(data, "1 clustered-core");
+ if (r < 0)
+ return r;
+
+ rq->data_size = r;
+
+ return 0;
+}
+
+static int disk_status_info(struct log_c *lc, struct dm_ulog_request *rq)
+{
+ int r;
+ char *data = (char *)rq->data;
+ struct stat statbuf;
+
+ if(fstat(lc->disk_fd, &statbuf)) {
+ rq->error = -errno;
+ return -errno;
+ }
+
+ r = sprintf(data, "3 clustered-disk %d:%d %c",
+ major(statbuf.st_rdev), minor(statbuf.st_rdev),
+ (lc->log_dev_failed) ? 'D' : 'A');
+ if (r < 0)
+ return r;
+
+ rq->data_size = r;
+
+ return 0;
+}
+
+/*
+ * clog_status_info
+ * @rq
+ *
+ */
+static int clog_status_info(struct dm_ulog_request *rq)
+{
+ int r;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ lc = get_pending_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ if (lc->disk_fd == -1)
+ r = core_status_info(lc, rq);
+ else
+ r = disk_status_info(lc, rq);
+
+ return r;
+}
+
+static int core_status_table(struct log_c *lc, struct dm_ulog_request *rq)
+{
+ int r;
+ char *data = (char *)rq->data;
+
+ r = sprintf(data, "clustered-core %u %s%s ",
+ lc->region_size,
+ (lc->sync == DEFAULTSYNC) ? "" :
+ (lc->sync == NOSYNC) ? "nosync " : "sync ",
+ (lc->block_on_error) ? "block_on_error" : "");
+ if (r < 0)
+ return r;
+
+ rq->data_size = r;
+ return 0;
+}
+
+static int disk_status_table(struct log_c *lc, struct dm_ulog_request *rq)
+{
+ int r;
+ char *data = (char *)rq->data;
+ struct stat statbuf;
+
+ if(fstat(lc->disk_fd, &statbuf)) {
+ rq->error = -errno;
+ return -errno;
+ }
+
+ r = sprintf(data, "clustered-disk %d:%d %u %s%s ",
+ major(statbuf.st_rdev), minor(statbuf.st_rdev),
+ lc->region_size,
+ (lc->sync == DEFAULTSYNC) ? "" :
+ (lc->sync == NOSYNC) ? "nosync " : "sync ",
+ (lc->block_on_error) ? "block_on_error" : "");
+ if (r < 0)
+ return r;
+
+ rq->data_size = r;
+ return 0;
+}
+
+/*
+ * clog_status_table
+ * @rq
+ *
+ */
+static int clog_status_table(struct dm_ulog_request *rq)
+{
+ int r;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ lc = get_pending_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ if (lc->disk_fd == -1)
+ r = core_status_table(lc, rq);
+ else
+ r = disk_status_table(lc, rq);
+
+ return r;
+}
+
+/*
+ * clog_is_remote_recovering
+ * @rq
+ *
+ */
+static int clog_is_remote_recovering(struct dm_ulog_request *rq)
+{
+ uint64_t *region_p = (uint64_t *)rq->data;
+ uint64_t region = *region_p;
+ struct {
+ int64_t is_recovering;
+ uint64_t in_sync_hint;
+ } *pkg = (void *)rq->data;
+ struct log_c *lc = get_log(rq->uuid, rq->luid);
+
+ if (!lc)
+ return -EINVAL;
+
+ if (region > lc->region_count)
+ return -EINVAL;
+
+ if (lc->recovery_halted) {
+ LOG_DBG("[%s] Recovery halted... [not remote recovering]: %llu",
+ SHORT_UUID(lc->uuid), (unsigned long long)region);
+ pkg->is_recovering = 0;
+ pkg->in_sync_hint = lc->region_count; /* none are recovering */
+ } else {
+ pkg->is_recovering = !log_test_bit(lc->sync_bits, region);
+
+ /*
+ * Remember, 'lc->sync_search' is 1 plus the region
+ * currently being recovered. So, we must take off 1
+ * to account for that; but only if 'sync_search > 1'.
+ */
+ pkg->in_sync_hint = lc->sync_search ? (lc->sync_search - 1) : 0;
+ LOG_DBG("[%s] Region is %s: %llu",
+ SHORT_UUID(lc->uuid),
+ (region == lc->recovering_region) ?
+ "currently remote recovering" :
+ (pkg->is_recovering) ? "pending remote recovery" :
+ "not remote recovering", (unsigned long long)region);
+ }
+
+ if (pkg->is_recovering &&
+ (region != lc->recovering_region)) {
+ struct recovery_request *rr;
+
+ /* Already in the list? */
+ for (rr = lc->recovery_request_list; rr; rr = rr->next)
+ if (rr->region == region)
+ goto out;
+
+ /* Failure to allocated simply means we can't prioritize it */
+ rr = malloc(sizeof(*rr));
+ if (!rr)
+ goto out;
+
+ LOG_DBG("[%s] Adding region to priority list: %llu",
+ SHORT_UUID(lc->uuid), (unsigned long long)region);
+ rr->region = region;
+ rr->next = lc->recovery_request_list;
+ lc->recovery_request_list = rr;
+ }
+
+out:
+
+ rq->data_size = sizeof(*pkg);
+
+ return 0;
+}
+
+
+/*
+ * do_request
+ * @rq: the request
+ * @server: is this request performed by the server
+ *
+ * An inability to perform this function will return an error
+ * from this function. However, an inability to successfully
+ * perform the request will fill in the 'rq->error' field.
+ *
+ * 'rq' (or more correctly, rq->u_rq.data) should be of sufficient
+ * size to hold any returning data. Currently, local.c uses 2kiB
+ * to hold 'rq' - leaving ~1.5kiB for return data... more than
+ * enough for all the implemented functions here.
+ *
+ * Returns: 0 on success, -EXXX on error
+ */
+int do_request(struct clog_request *rq, int server)
+{
+ int r;
+
+ if (!rq)
+ return 0;
+
+ if (rq->u_rq.error)
+ LOG_DBG("Programmer error: rq struct has error set");
+
+ switch (rq->u_rq.request_type) {
+ case DM_ULOG_CTR:
+ r = clog_ctr(&rq->u_rq);
+ break;
+ case DM_ULOG_DTR:
+ r = clog_dtr(&rq->u_rq);
+ break;
+ case DM_ULOG_PRESUSPEND:
+ r = clog_presuspend(&rq->u_rq);
+ break;
+ case DM_ULOG_POSTSUSPEND:
+ r = clog_postsuspend(&rq->u_rq);
+ break;
+ case DM_ULOG_RESUME:
+ r = clog_resume(&rq->u_rq);
+ break;
+ case DM_ULOG_GET_REGION_SIZE:
+ r = clog_get_region_size(&rq->u_rq);
+ break;
+ case DM_ULOG_IS_CLEAN:
+ r = clog_is_clean(&rq->u_rq);
+ break;
+ case DM_ULOG_IN_SYNC:
+ r = clog_in_sync(&rq->u_rq);
+ break;
+ case DM_ULOG_FLUSH:
+ r = clog_flush(&rq->u_rq, server);
+ break;
+ case DM_ULOG_MARK_REGION:
+ r = clog_mark_region(&rq->u_rq, rq->originator);
+ break;
+ case DM_ULOG_CLEAR_REGION:
+ r = clog_clear_region(&rq->u_rq, rq->originator);
+ break;
+ case DM_ULOG_GET_RESYNC_WORK:
+ r = clog_get_resync_work(&rq->u_rq, rq->originator);
+ break;
+ case DM_ULOG_SET_REGION_SYNC:
+ r = clog_set_region_sync(&rq->u_rq, rq->originator);
+ break;
+ case DM_ULOG_GET_SYNC_COUNT:
+ r = clog_get_sync_count(&rq->u_rq, rq->originator);
+ break;
+ case DM_ULOG_STATUS_INFO:
+ r = clog_status_info(&rq->u_rq);
+ break;
+ case DM_ULOG_STATUS_TABLE:
+ r = clog_status_table(&rq->u_rq);
+ break;
+ case DM_ULOG_IS_REMOTE_RECOVERING:
+ r = clog_is_remote_recovering(&rq->u_rq);
+ break;
+ default:
+ LOG_ERROR("Unknown request");
+ r = rq->u_rq.error = -EINVAL;
+ break;
+ }
+
+ if (r && !rq->u_rq.error)
+ rq->u_rq.error = r;
+ else if (r != rq->u_rq.error)
+ LOG_DBG("Warning: error from function != rq->u_rq.error");
+
+ if (rq->u_rq.error && rq->u_rq.data_size) {
+ /* Make sure I'm handling errors correctly above */
+ LOG_DBG("Programmer error: rq->u_rq.error && rq->u_rq.data_size");
+ rq->u_rq.data_size = 0;
+ }
+
+ return 0;
+}
+
+static void print_bits(dm_bitset_t bs, int print)
+{
+ int i, size;
+ char outbuf[128];
+ unsigned char *buf = (unsigned char *)(bs + 1);
+
+ size = (*bs % 8) ? 1 : 0;
+ size += (*bs / 8);
+
+ memset(outbuf, 0, sizeof(outbuf));
+
+ for (i = 0; i < size; i++) {
+ if (!(i % 16)) {
+ if (outbuf[0] != '\0') {
+ if (print)
+ LOG_PRINT("%s", outbuf);
+ else
+ LOG_DBG("%s", outbuf);
+ }
+ memset(outbuf, 0, sizeof(outbuf));
+ sprintf(outbuf, "[%3d - %3d]", i, i+15);
+ }
+ sprintf(outbuf + strlen(outbuf), " %.2X", (unsigned char)buf[i]);
+ }
+ if (outbuf[0] != '\0') {
+ if (print)
+ LOG_PRINT("%s", outbuf);
+ else
+ LOG_DBG("%s", outbuf);
+ }
+}
+
+/* int store_bits(const char *uuid, const char *which, char **buf)*/
+int push_state(const char *uuid, uint64_t luid,
+ const char *which, char **buf, uint32_t debug_who)
+{
+ int bitset_size;
+ struct log_c *lc;
+
+ if (*buf)
+ LOG_ERROR("store_bits: *buf != NULL");
+
+ lc = get_log(uuid, luid);
+ if (!lc) {
+ LOG_ERROR("store_bits: No log found for %s", uuid);
+ return -EINVAL;
+ }
+
+ if (!strcmp(which, "recovering_region")) {
+ *buf = malloc(64); /* easily handles the 2 written numbers */
+ if (!*buf)
+ return -ENOMEM;
+ sprintf(*buf, "%llu %u", (unsigned long long)lc->recovering_region,
+ lc->recoverer);
+
+ LOG_SPRINT(lc, "CKPT SEND - SEQ#=X, UUID=%s, nodeid = %u:: "
+ "recovering_region=%llu, recoverer=%u, sync_count=%llu",
+ SHORT_UUID(lc->uuid), debug_who,
+ (unsigned long long)lc->recovering_region,
+ lc->recoverer,
+ (unsigned long long)count_bits32(lc->sync_bits));
+ return 64;
+ }
+
+ /* Size in 'int's */
+ bitset_size = (*(lc->clean_bits) / DM_BITS_PER_INT) + 1;
+
+ /* Size in bytes */
+ bitset_size *= 4;
+
+ *buf = malloc(bitset_size);
+
+ if (!*buf) {
+ LOG_ERROR("store_bits: Unable to allocate memory");
+ return -ENOMEM;
+ }
+
+ if (!strncmp(which, "sync_bits", 9)) {
+ memcpy(*buf, lc->sync_bits + 1, bitset_size);
+
+ LOG_DBG("[%s] storing sync_bits (sync_count = %llu):",
+ SHORT_UUID(uuid), (unsigned long long)
+ count_bits32(lc->sync_bits));
+
+ print_bits(lc->sync_bits, 0);
+ } else if (!strncmp(which, "clean_bits", 9)) {
+ memcpy(*buf, lc->clean_bits + 1, bitset_size);
+
+ LOG_DBG("[%s] storing clean_bits:", SHORT_UUID(lc->uuid));
+
+ print_bits(lc->clean_bits, 0);
+ }
+
+ return bitset_size;
+}
+
+/*int load_bits(const char *uuid, const char *which, char *buf, int size)*/
+int pull_state(const char *uuid, uint64_t luid,
+ const char *which, char *buf, int size)
+{
+ int bitset_size;
+ struct log_c *lc;
+
+ if (!buf) {
+ LOG_ERROR("pull_state: buf == NULL");
+ return -EINVAL;
+ }
+
+ lc = get_log(uuid, luid);
+ if (!lc) {
+ LOG_ERROR("pull_state: No log found for %s", uuid);
+ return -EINVAL;
+ }
+
+ if (!strncmp(which, "recovering_region", 17)) {
+ sscanf(buf, "%llu %u", (unsigned long long *)&lc->recovering_region,
+ &lc->recoverer);
+ LOG_SPRINT(lc, "CKPT INIT - SEQ#=X, UUID=%s, nodeid = X:: "
+ "recovering_region=%llu, recoverer=%u",
+ SHORT_UUID(lc->uuid),
+ (unsigned long long)lc->recovering_region, lc->recoverer);
+ return 0;
+ }
+
+ /* Size in 'int's */
+ bitset_size = (*(lc->clean_bits) /DM_BITS_PER_INT) + 1;
+
+ /* Size in bytes */
+ bitset_size *= 4;
+
+ if (bitset_size != size) {
+ LOG_ERROR("pull_state(%s): bad bitset_size (%d vs %d)",
+ which, size, bitset_size);
+ return -EINVAL;
+ }
+
+ if (!strncmp(which, "sync_bits", 9)) {
+ lc->resume_override += 1;
+ memcpy(lc->sync_bits + 1, buf, bitset_size);
+
+ LOG_DBG("[%s] loading sync_bits (sync_count = %llu):",
+ SHORT_UUID(lc->uuid),(unsigned long long)
+ count_bits32(lc->sync_bits));
+
+ print_bits(lc->sync_bits, 0);
+ } else if (!strncmp(which, "clean_bits", 9)) {
+ lc->resume_override += 2;
+ memcpy(lc->clean_bits + 1, buf, bitset_size);
+
+ LOG_DBG("[%s] loading clean_bits:", SHORT_UUID(lc->uuid));
+
+ print_bits(lc->sync_bits, 0);
+ }
+
+ return 0;
+}
+
+int log_get_state(struct dm_ulog_request *rq)
+{
+ struct log_c *lc;
+
+ lc = get_log(rq->uuid, rq->luid);
+ if (!lc)
+ /* FIXME Callers are ignoring this */
+ return -EINVAL;
+
+ return (int)lc->state;
+}
+
+/*
+ * log_status
+ *
+ * Returns: 1 if logs are still present, 0 otherwise
+ */
+int log_status(void)
+{
+ if (!dm_list_empty(&log_list) || !dm_list_empty(&log_pending_list))
+ return 1;
+
+ return 0;
+}
+
+void log_debug(void)
+{
+ struct log_c *lc;
+ uint64_t r;
+ int i;
+
+ LOG_ERROR("");
+ LOG_ERROR("LOG COMPONENT DEBUGGING::");
+ LOG_ERROR("Official log list:");
+ LOG_ERROR("Pending log list:");
+ dm_list_iterate_items(lc, &log_pending_list) {
+ LOG_ERROR("%s", lc->uuid);
+ LOG_ERROR("sync_bits:");
+ print_bits(lc->sync_bits, 1);
+ LOG_ERROR("clean_bits:");
+ print_bits(lc->clean_bits, 1);
+ }
+
+ dm_list_iterate_items(lc, &log_list) {
+ LOG_ERROR("%s", lc->uuid);
+ LOG_ERROR(" recoverer : %" PRIu32, lc->recoverer);
+ LOG_ERROR(" recovering_region: %" PRIu64, lc->recovering_region);
+ LOG_ERROR(" recovery_halted : %s", (lc->recovery_halted) ?
+ "YES" : "NO");
+ LOG_ERROR("sync_bits:");
+ print_bits(lc->sync_bits, 1);
+ LOG_ERROR("clean_bits:");
+ print_bits(lc->clean_bits, 1);
+
+ LOG_ERROR("Validating %s::", SHORT_UUID(lc->uuid));
+ r = find_next_zero_bit(lc->sync_bits, 0);
+ LOG_ERROR(" lc->region_count = %" PRIu32, lc->region_count);
+ LOG_ERROR(" lc->sync_count = %" PRIu64, lc->sync_count);
+ LOG_ERROR(" next zero bit = %" PRIu64, r);
+ if ((r > lc->region_count) ||
+ ((r == lc->region_count) && (lc->sync_count > lc->region_count))) {
+ LOG_ERROR("ADJUSTING SYNC_COUNT");
+ lc->sync_count = lc->region_count;
+ }
+
+ LOG_ERROR("Resync request history:");
+ for (i = 0; i < RESYNC_HISTORY; i++) {
+ lc->idx++;
+ lc->idx = lc->idx % RESYNC_HISTORY;
+ if (lc->resync_history[lc->idx][0] == '\0')
+ continue;
+ LOG_ERROR("%d:%d) %s", i, lc->idx,
+ lc->resync_history[lc->idx]);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2004-2009 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _LVM_CLOG_FUNCTIONS_H
+#define _LVM_CLOG_FUNCTIONS_H
+
+#include "dm-log-userspace.h"
+#include "cluster.h"
+
+#define LOG_RESUMED 1
+#define LOG_SUSPENDED 2
+
+int local_resume(struct dm_ulog_request *rq);
+int cluster_postsuspend(char *, uint64_t);
+
+int do_request(struct clog_request *rq, int server);
+int push_state(const char *uuid, uint64_t luid,
+ const char *which, char **buf, uint32_t debug_who);
+int pull_state(const char *uuid, uint64_t luid,
+ const char *which, char *buf, int size);
+
+int log_get_state(struct dm_ulog_request *rq);
+int log_status(void);
+void log_debug(void);
+
+#endif /* _LVM_CLOG_FUNCTIONS_H */
--- /dev/null
+/*
+ * Copyright (C) 2004-2009 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "logging.h"
+#include "link_mon.h"
+
+#include <errno.h>
+#include <poll.h>
+#include <stdlib.h>
+
+struct link_callback {
+ int fd;
+ const char *name;
+ void *data;
+ int (*callback)(void *data);
+
+ struct link_callback *next;
+};
+
+static unsigned used_pfds = 0;
+static unsigned free_pfds = 0;
+static struct pollfd *pfds = NULL;
+static struct link_callback *callbacks = NULL;
+
+int links_register(int fd, const char *name, int (*callback)(void *data), void *data)
+{
+ unsigned i;
+ struct link_callback *lc;
+
+ for (i = 0; i < used_pfds; i++) {
+ if (fd == pfds[i].fd) {
+ LOG_ERROR("links_register: Duplicate file descriptor");
+ return -EINVAL;
+ }
+ }
+
+ lc = malloc(sizeof(*lc));
+ if (!lc)
+ return -ENOMEM;
+
+ lc->fd = fd;
+ lc->name = name;
+ lc->data = data;
+ lc->callback = callback;
+
+ if (!free_pfds) {
+ struct pollfd *tmp;
+ tmp = realloc(pfds, sizeof(struct pollfd) * ((used_pfds*2) + 1));
+ if (!tmp) {
+ free(lc);
+ return -ENOMEM;
+ }
+
+ pfds = tmp;
+ free_pfds = used_pfds + 1;
+ }
+
+ free_pfds--;
+ pfds[used_pfds].fd = fd;
+ pfds[used_pfds].events = POLLIN;
+ pfds[used_pfds].revents = 0;
+ used_pfds++;
+
+ lc->next = callbacks;
+ callbacks = lc;
+ LOG_DBG("Adding %s/%d", lc->name, lc->fd);
+ LOG_DBG(" used_pfds = %u, free_pfds = %u",
+ used_pfds, free_pfds);
+
+ return 0;
+}
+
+int links_unregister(int fd)
+{
+ unsigned i;
+ struct link_callback *p, *c;
+
+ for (i = 0; i < used_pfds; i++)
+ if (fd == pfds[i].fd) {
+ /* entire struct is copied (overwritten) */
+ pfds[i] = pfds[used_pfds - 1];
+ used_pfds--;
+ free_pfds++;
+ }
+
+ for (p = NULL, c = callbacks; c; p = c, c = c->next)
+ if (fd == c->fd) {
+ LOG_DBG("Freeing up %s/%d", c->name, c->fd);
+ LOG_DBG(" used_pfds = %u, free_pfds = %u",
+ used_pfds, free_pfds);
+ if (p)
+ p->next = c->next;
+ else
+ callbacks = c->next;
+ free(c);
+ break;
+ }
+
+ return 0;
+}
+
+int links_monitor(void)
+{
+ unsigned i;
+ int r;
+
+ for (i = 0; i < used_pfds; i++) {
+ pfds[i].revents = 0;
+ }
+
+ r = poll(pfds, used_pfds, -1);
+ if (r <= 0)
+ return r;
+
+ r = 0;
+ /* FIXME: handle POLLHUP */
+ for (i = 0; i < used_pfds; i++)
+ if (pfds[i].revents & POLLIN) {
+ LOG_DBG("Data ready on %d", pfds[i].fd);
+
+ /* FIXME: Add this back return 1;*/
+ r++;
+ }
+
+ return r;
+}
+
+int links_issue_callbacks(void)
+{
+ unsigned i;
+ struct link_callback *lc;
+
+ for (i = 0; i < used_pfds; i++)
+ if (pfds[i].revents & POLLIN)
+ for (lc = callbacks; lc; lc = lc->next)
+ if (pfds[i].fd == lc->fd) {
+ LOG_DBG("Issuing callback on %s/%d",
+ lc->name, lc->fd);
+ lc->callback(lc->data);
+ break;
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2004-2009 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _LVM_CLOG_LINK_MON_H
+#define _LVM_CLOG_LINK_MON_H
+
+int links_register(int fd, const char *name, int (*callback)(void *data), void *data);
+int links_unregister(int fd);
+int links_monitor(void);
+int links_issue_callbacks(void);
+
+#endif /* _LVM_CLOG_LINK_MON_H */
--- /dev/null
+/*
+ * Copyright (C) 2004-2009 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "logging.h"
+#include "common.h"
+#include "functions.h"
+#include "link_mon.h"
+#include "local.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <linux/connector.h>
+#include <linux/netlink.h>
+#include <unistd.h>
+
+#ifndef CN_IDX_DM
+/* Kernel 2.6.31 is required to run this code */
+#define CN_IDX_DM 0x7 /* Device Mapper */
+#define CN_VAL_DM_USERSPACE_LOG 0x1
+#endif
+
+static int cn_fd; /* Connector (netlink) socket fd */
+static char recv_buf[2048];
+static char send_buf[2048];
+
+
+/* FIXME: merge this function with kernel_send_helper */
+static int kernel_ack(uint32_t seq, int error)
+{
+ int r;
+ struct nlmsghdr *nlh = (struct nlmsghdr *)send_buf;
+ struct cn_msg *msg = NLMSG_DATA(nlh);
+
+ if (error < 0) {
+ LOG_ERROR("Programmer error: error codes must be positive");
+ return -EINVAL;
+ }
+
+ memset(send_buf, 0, sizeof(send_buf));
+
+ nlh->nlmsg_seq = 0;
+ nlh->nlmsg_pid = getpid();
+ nlh->nlmsg_type = NLMSG_DONE;
+ nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct cn_msg));
+ nlh->nlmsg_flags = 0;
+
+ msg->len = 0;
+ msg->id.idx = CN_IDX_DM;
+ msg->id.val = CN_VAL_DM_USERSPACE_LOG;
+ msg->seq = seq;
+ msg->ack = error;
+
+ r = send(cn_fd, nlh, NLMSG_LENGTH(sizeof(struct cn_msg)), 0);
+ /* FIXME: do better error processing */
+ if (r <= 0)
+ return -EBADE;
+
+ return 0;
+}
+
+
+/*
+ * kernel_recv
+ * @rq: the newly allocated request from kernel
+ *
+ * Read requests from the kernel and allocate space for the new request.
+ * If there is no request from the kernel, *rq is NULL.
+ *
+ * This function is not thread safe due to returned stack pointer. In fact,
+ * the returned pointer must not be in-use when this function is called again.
+ *
+ * Returns: 0 on success, -EXXX on error
+ */
+static int kernel_recv(struct clog_request **rq)
+{
+ int r = 0;
+ ssize_t len;
+ char *foo;
+ struct cn_msg *msg;
+ struct dm_ulog_request *u_rq;
+ struct nlmsghdr *nlmsg_h;
+
+ *rq = NULL;
+ memset(recv_buf, 0, sizeof(recv_buf));
+
+ len = recv(cn_fd, recv_buf, sizeof(recv_buf), 0);
+ if (len < 0) {
+ LOG_ERROR("Failed to recv message from kernel");
+ r = -errno;
+ goto fail;
+ }
+
+ nlmsg_h = (struct nlmsghdr *)recv_buf;
+ switch (nlmsg_h->nlmsg_type) {
+ case NLMSG_ERROR:
+ LOG_ERROR("Unable to recv message from kernel: NLMSG_ERROR");
+ r = -EBADE;
+ goto fail;
+ case NLMSG_DONE:
+ msg = (struct cn_msg *)NLMSG_DATA((struct nlmsghdr *)recv_buf);
+ len -= (ssize_t)sizeof(struct nlmsghdr);
+
+ if (len < (ssize_t)sizeof(struct cn_msg)) {
+ LOG_ERROR("Incomplete request from kernel received");
+ r = -EBADE;
+ goto fail;
+ }
+
+ if (msg->len > DM_ULOG_REQUEST_SIZE) {
+ LOG_ERROR("Not enough space to receive kernel request (%d/%d)",
+ msg->len, DM_ULOG_REQUEST_SIZE);
+ r = -EBADE;
+ goto fail;
+ }
+
+ if (!msg->len)
+ LOG_ERROR("Zero length message received");
+
+ len -= (ssize_t)sizeof(struct cn_msg);
+
+ if (len < msg->len)
+ LOG_ERROR("len = %zd, msg->len = %" PRIu16, len, msg->len);
+
+ msg->data[msg->len] = '\0'; /* Cleaner way to ensure this? */
+ u_rq = (struct dm_ulog_request *)msg->data;
+
+ if (!u_rq->request_type) {
+ LOG_DBG("Bad transmission, requesting resend [%u]",
+ msg->seq);
+ r = -EAGAIN;
+
+ if (kernel_ack(msg->seq, EAGAIN)) {
+ LOG_ERROR("Failed to NACK kernel transmission [%u]",
+ msg->seq);
+ r = -EBADE;
+ }
+ }
+
+ /*
+ * Now we've got sizeof(struct cn_msg) + sizeof(struct nlmsghdr)
+ * worth of space that precede the request structure from the
+ * kernel. Since that space isn't going to be used again, we
+ * can take it for our purposes; rather than allocating a whole
+ * new structure and doing a memcpy.
+ *
+ * We should really make sure 'clog_request' doesn't grow
+ * beyond what is available to us, but we need only check it
+ * once... perhaps at compile time?
+ */
+ foo = (char *)u_rq;
+ foo -= (sizeof(struct clog_request) - sizeof(struct dm_ulog_request));
+ *rq = (struct clog_request *) foo;
+
+ /* Clear the wrapper container fields */
+ memset(*rq, 0, (size_t)((char *)u_rq - (char *)(*rq)));
+ break;
+ default:
+ LOG_ERROR("Unknown nlmsg_type");
+ r = -EBADE;
+ }
+
+fail:
+ if (r)
+ *rq = NULL;
+
+ return (r == -EAGAIN) ? 0 : r;
+}
+
+static int kernel_send_helper(void *data, uint16_t out_size)
+{
+ int r;
+ struct nlmsghdr *nlh;
+ struct cn_msg *msg;
+
+ memset(send_buf, 0, sizeof(send_buf));
+
+ nlh = (struct nlmsghdr *)send_buf;
+ nlh->nlmsg_seq = 0; /* FIXME: Is this used? */
+ nlh->nlmsg_pid = getpid();
+ nlh->nlmsg_type = NLMSG_DONE;
+ nlh->nlmsg_len = NLMSG_LENGTH(out_size + sizeof(struct cn_msg));
+ nlh->nlmsg_flags = 0;
+
+ msg = NLMSG_DATA(nlh);
+ memcpy(msg->data, data, out_size);
+ msg->len = out_size;
+ msg->id.idx = CN_IDX_DM;
+ msg->id.val = CN_VAL_DM_USERSPACE_LOG;
+ msg->seq = 0;
+
+ r = send(cn_fd, nlh, NLMSG_LENGTH(out_size + sizeof(struct cn_msg)), 0);
+ /* FIXME: do better error processing */
+ if (r <= 0)
+ return -EBADE;
+
+ return 0;
+}
+
+/*
+ * do_local_work
+ *
+ * Any processing errors are placed in the 'rq'
+ * structure to be reported back to the kernel.
+ * It may be pointless for this function to
+ * return an int.
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+static int do_local_work(void *data __attribute__((unused)))
+{
+ int r;
+ struct clog_request *rq;
+ struct dm_ulog_request *u_rq = NULL;
+
+ r = kernel_recv(&rq);
+ if (r)
+ return r;
+
+ if (!rq)
+ return 0;
+
+ u_rq = &rq->u_rq;
+ LOG_DBG("[%s] Request from kernel received: [%s/%u]",
+ SHORT_UUID(u_rq->uuid), RQ_TYPE(u_rq->request_type),
+ u_rq->seq);
+ switch (u_rq->request_type) {
+ case DM_ULOG_CTR:
+ case DM_ULOG_DTR:
+ case DM_ULOG_GET_REGION_SIZE:
+ case DM_ULOG_IN_SYNC:
+ case DM_ULOG_GET_SYNC_COUNT:
+ case DM_ULOG_STATUS_INFO:
+ case DM_ULOG_STATUS_TABLE:
+ case DM_ULOG_PRESUSPEND:
+ /* We do not specify ourselves as server here */
+ r = do_request(rq, 0);
+ if (r)
+ LOG_DBG("Returning failed request to kernel [%s]",
+ RQ_TYPE(u_rq->request_type));
+ r = kernel_send(u_rq);
+ if (r)
+ LOG_ERROR("Failed to respond to kernel [%s]",
+ RQ_TYPE(u_rq->request_type));
+
+ break;
+ case DM_ULOG_RESUME:
+ /*
+ * Resume is a special case that requires a local
+ * component to join the CPG, and a cluster component
+ * to handle the request.
+ */
+ r = local_resume(u_rq);
+ if (r) {
+ LOG_DBG("Returning failed request to kernel [%s]",
+ RQ_TYPE(u_rq->request_type));
+ r = kernel_send(u_rq);
+ if (r)
+ LOG_ERROR("Failed to respond to kernel [%s]",
+ RQ_TYPE(u_rq->request_type));
+ break;
+ }
+ /* ELSE, fall through */
+ case DM_ULOG_IS_CLEAN:
+ case DM_ULOG_FLUSH:
+ case DM_ULOG_MARK_REGION:
+ case DM_ULOG_GET_RESYNC_WORK:
+ case DM_ULOG_SET_REGION_SYNC:
+ case DM_ULOG_IS_REMOTE_RECOVERING:
+ case DM_ULOG_POSTSUSPEND:
+ r = cluster_send(rq);
+ if (r) {
+ u_rq->data_size = 0;
+ u_rq->error = r;
+ kernel_send(u_rq);
+ }
+
+ break;
+ case DM_ULOG_CLEAR_REGION:
+ r = kernel_ack(u_rq->seq, 0);
+
+ r = cluster_send(rq);
+ if (r) {
+ /*
+ * FIXME: store error for delivery on flush
+ * This would allow us to optimize MARK_REGION
+ * too.
+ */
+ }
+
+ break;
+ default:
+ LOG_ERROR("Invalid log request received (%u), ignoring.",
+ u_rq->request_type);
+
+ return 0;
+ }
+
+ if (r && !u_rq->error)
+ u_rq->error = r;
+
+ return r;
+}
+
+/*
+ * kernel_send
+ * @u_rq: result to pass back to kernel
+ *
+ * This function returns the u_rq structure
+ * (containing the results) to the kernel.
+ * It then frees the structure.
+ *
+ * WARNING: should the structure be freed if
+ * there is an error? I vote 'yes'. If the
+ * kernel doesn't get the response, it should
+ * resend the request.
+ *
+ * Returns: 0 on success, -EXXX on failure
+ */
+int kernel_send(struct dm_ulog_request *u_rq)
+{
+ int r;
+ uint16_t size;
+
+ if (!u_rq)
+ return -EINVAL;
+
+ size = (uint16_t)(sizeof(struct dm_ulog_request) + u_rq->data_size);
+
+ if (!u_rq->data_size && !u_rq->error) {
+ /* An ACK is all that is needed */
+
+ /* FIXME: add ACK code */
+ } else if (size > DM_ULOG_REQUEST_SIZE) {
+ /*
+ * If we gotten here, we've already overrun
+ * our allotted space somewhere.
+ *
+ * We must do something, because the kernel
+ * is waiting for a response.
+ */
+ LOG_ERROR("Not enough space to respond to server");
+ u_rq->error = -ENOSPC;
+ size = sizeof(struct dm_ulog_request);
+ }
+
+ r = kernel_send_helper(u_rq, size);
+ if (r)
+ LOG_ERROR("Failed to send msg to kernel.");
+
+ return r;
+}
+
+/*
+ * init_local
+ *
+ * Initialize kernel communication socket (netlink)
+ *
+ * Returns: 0 on success, values from common.h on failure
+ */
+int init_local(void)
+{
+ int r = 0;
+ unsigned opt;
+ struct sockaddr_nl addr;
+
+ cn_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
+ if (cn_fd < 0)
+ return EXIT_KERNEL_SOCKET;
+
+ /* memset to fix valgrind complaint */
+ memset(&addr, 0, sizeof(struct sockaddr_nl));
+
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = CN_IDX_DM;
+ addr.nl_pid = 0;
+
+ r = bind(cn_fd, (struct sockaddr *) &addr, sizeof(addr));
+ if (r < 0) {
+ close(cn_fd);
+ return EXIT_KERNEL_BIND;
+ }
+
+ opt = addr.nl_groups;
+ r = setsockopt(cn_fd, 270, NETLINK_ADD_MEMBERSHIP, &opt, sizeof(opt));
+ if (r) {
+ close(cn_fd);
+ return EXIT_KERNEL_SETSOCKOPT;
+ }
+
+ /*
+ r = fcntl(cn_fd, F_SETFL, FNDELAY);
+ */
+
+ links_register(cn_fd, "local", do_local_work, NULL);
+
+ return 0;
+}
+
+/*
+ * cleanup_local
+ *
+ * Clean up before exiting
+ */
+void cleanup_local(void)
+{
+ links_unregister(cn_fd);
+ close(cn_fd);
+}
--- /dev/null
+/*
+ * Copyright (C) 2004-2009 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _LVM_CLOG_LOCAL_H
+#define _LVM_CLOG_LOCAL_H
+
+int init_local(void);
+void cleanup_local(void);
+
+int kernel_send(struct dm_ulog_request *rq);
+
+#endif /* _LVM_CLOG_LOCAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2004-2009 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "logging.h"
+
+const char *__rq_types_off_by_one[] = {
+ "DM_ULOG_CTR",
+ "DM_ULOG_DTR",
+ "DM_ULOG_PRESUSPEND",
+ "DM_ULOG_POSTSUSPEND",
+ "DM_ULOG_RESUME",
+ "DM_ULOG_GET_REGION_SIZE",
+ "DM_ULOG_IS_CLEAN",
+ "DM_ULOG_IN_SYNC",
+ "DM_ULOG_FLUSH",
+ "DM_ULOG_MARK_REGION",
+ "DM_ULOG_CLEAR_REGION",
+ "DM_ULOG_GET_RESYNC_WORK",
+ "DM_ULOG_SET_REGION_SYNC",
+ "DM_ULOG_GET_SYNC_COUNT",
+ "DM_ULOG_STATUS_INFO",
+ "DM_ULOG_STATUS_TABLE",
+ "DM_ULOG_IS_REMOTE_RECOVERING",
+ NULL
+};
+
+int log_tabbing = 0;
+int log_is_open = 0;
+
+/*
+ * Variables for various conditional logging
+ */
+#ifdef MEMB
+int log_membership_change = 1;
+#else
+int log_membership_change = 0;
+#endif
+
+#ifdef CKPT
+int log_checkpoint = 1;
+#else
+int log_checkpoint = 0;
+#endif
+
+#ifdef RESEND
+int log_resend_requests = 1;
+#else
+int log_resend_requests = 0;
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2004-2009 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_CLOG_LOGGING_H
+#define _LVM_CLOG_LOGGING_H
+
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "configure.h"
+#include <stdio.h>
+#include <stdint.h>
+#include <syslog.h>
+
+/* SHORT_UUID - print last 8 chars of a string */
+#define SHORT_UUID(x) (strlen(x) > 8) ? ((x) + (strlen(x) - 8)) : (x)
+
+extern const char *__rq_types_off_by_one[];
+#define RQ_TYPE(x) __rq_types_off_by_one[(x) - 1]
+
+extern int log_tabbing;
+extern int log_is_open;
+extern int log_membership_change;
+extern int log_checkpoint;
+extern int log_resend_requests;
+
+#define LOG_OPEN(ident, option, facility) do { \
+ openlog(ident, option, facility); \
+ log_is_open = 1; \
+ } while (0)
+
+#define LOG_CLOSE(void) do { \
+ log_is_open = 0; \
+ closelog(); \
+ } while (0)
+
+#define LOG_OUTPUT(level, f, arg...) do { \
+ int __i; \
+ char __buffer[16]; \
+ FILE *fp = (level > LOG_NOTICE) ? stderr : stdout; \
+ if (log_is_open) { \
+ for (__i = 0; (__i < log_tabbing) && (__i < 15); __i++) \
+ __buffer[__i] = '\t'; \
+ __buffer[__i] = '\0'; \
+ syslog(level, "%s" f "\n", __buffer, ## arg); \
+ } else { \
+ for (__i = 0; __i < log_tabbing; __i++) \
+ fprintf(fp, "\t"); \
+ fprintf(fp, f "\n", ## arg); \
+ } \
+ } while (0)
+
+
+#ifdef DEBUG
+#define LOG_DBG(f, arg...) LOG_OUTPUT(LOG_DEBUG, f, ## arg)
+#else /* DEBUG */
+#define LOG_DBG(f, arg...)
+#endif /* DEBUG */
+
+#define LOG_COND(__X, f, arg...) do {\
+ if (__X) { \
+ LOG_OUTPUT(LOG_NOTICE, f, ## arg); \
+ } \
+ } while (0)
+#define LOG_PRINT(f, arg...) LOG_OUTPUT(LOG_NOTICE, f, ## arg)
+#define LOG_ERROR(f, arg...) LOG_OUTPUT(LOG_ERR, f, ## arg)
+
+#endif /* _LVM_CLOG_LOGGING_H */
--- /dev/null
+init_fifos
+fini_fifos
+daemon_talk
--- /dev/null
+#
+# Copyright (C) 2005-2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES = libdevmapper-event.c
+SOURCES2 = dmeventd.c
+
+TARGETS = dmeventd
+
+.PHONY: install_lib_dynamic install_lib_static install_include \
+ install_pkgconfig install_dmeventd_dynamic install_dmeventd_static \
+ install_lib install_dmeventd
+
+INSTALL_DMEVENTD_TARGETS = install_dmeventd_dynamic
+INSTALL_LIB_TARGETS = install_lib_dynamic
+
+LIB_NAME = libdevmapper-event
+ifeq ("@STATIC_LINK@", "yes")
+ LIB_STATIC = $(LIB_NAME).a
+ TARGETS += $(LIB_STATIC) dmeventd.static
+ INSTALL_DMEVENTD_TARGETS += install_dmeventd_static
+ INSTALL_LIB_TARGETS += install_lib_static
+endif
+
+LIB_VERSION = $(LIB_VERSION_DM)
+LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
+
+CLEAN_TARGETS = dmeventd.static $(LIB_NAME).a
+
+ifneq ($(MAKECMDGOALS),device-mapper)
+ SUBDIRS+=plugins
+endif
+
+CFLOW_LIST = $(SOURCES)
+CFLOW_LIST_TARGET = $(LIB_NAME).cflow
+CFLOW_TARGET = dmeventd
+
+EXPORTED_HEADER = $(srcdir)/libdevmapper-event.h
+EXPORTED_FN_PREFIX = dm_event
+
+include $(top_builddir)/make.tmpl
+
+all: device-mapper
+device-mapper: $(TARGETS)
+
+LIBS += -ldevmapper
+LVMLIBS += -ldevmapper-event $(PTHREAD_LIBS)
+
+dmeventd: $(LIB_SHARED) dmeventd.o
+ $(CC) $(CFLAGS) $(LDFLAGS) -L. -o $@ dmeventd.o \
+ $(DL_LIBS) $(LVMLIBS) $(LIBS) -rdynamic
+
+dmeventd.static: $(LIB_STATIC) dmeventd.o $(interfacebuilddir)/libdevmapper.a
+ $(CC) $(CFLAGS) $(LDFLAGS) -static -L. -L$(interfacebuilddir) -o $@ \
+ dmeventd.o $(DL_LIBS) $(LVMLIBS) $(LIBS) $(STATIC_LIBS)
+
+ifeq ("@PKGCONFIG@", "yes")
+ INSTALL_LIB_TARGETS += install_pkgconfig
+endif
+
+ifneq ("$(CFLOW_CMD)", "")
+CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
+-include $(top_builddir)/libdm/libdevmapper.cflow
+-include $(top_builddir)/lib/liblvm-internal.cflow
+-include $(top_builddir)/lib/liblvm2cmd.cflow
+-include $(top_builddir)/daemons/dmeventd/$(LIB_NAME).cflow
+-include $(top_builddir)/daemons/dmeventd/plugins/mirror/$(LIB_NAME)-lvm2mirror.cflow
+endif
+
+install_include: $(srcdir)/libdevmapper-event.h
+ $(INSTALL_DATA) -D $< $(includedir)/$(<F)
+
+install_pkgconfig: libdevmapper-event.pc
+ $(INSTALL_DATA) -D $< $(pkgconfigdir)/devmapper-event.pc
+
+install_lib_dynamic: install_lib_shared
+
+install_lib_static: $(LIB_STATIC)
+ $(INSTALL_DATA) -D $< $(usrlibdir)/$(<F)
+
+install_lib: $(INSTALL_LIB_TARGETS)
+
+install_dmeventd_dynamic: dmeventd
+ $(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
+
+install_dmeventd_static: dmeventd.static
+ $(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
+
+install_dmeventd: $(INSTALL_DMEVENTD_TARGETS)
+
+install: install_include install_lib install_dmeventd
+
+install_device-mapper: install_include install_lib install_dmeventd
+
+DISTCLEAN_TARGETS += libdevmapper-event.pc .exported_symbols_generated
--- /dev/null
+/*
+ * Copyright (C) 2005-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * dmeventd - dm event daemon to monitor active mapped devices
+ */
+
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "configure.h"
+#include "libdevmapper.h"
+#include "libdevmapper-event.h"
+#include "dmeventd.h"
+//#include "libmultilog.h"
+#include "dm-logging.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <signal.h>
+#include <arpa/inet.h> /* for htonl, ntohl */
+
+#ifdef linux
+# include <malloc.h>
+
+# define OOM_ADJ_FILE "/proc/self/oom_adj"
+
+/* From linux/oom.h */
+# define OOM_DISABLE (-17)
+# define OOM_ADJUST_MIN (-16)
+
+#endif
+
+/* FIXME We use syslog for now, because multilog is not yet implemented */
+#include <syslog.h>
+
+static volatile sig_atomic_t _exit_now = 0; /* set to '1' when signal is given to exit */
+static volatile sig_atomic_t _thread_registries_empty = 1; /* registries are empty initially */
+
+/* List (un)link macros. */
+#define LINK(x, head) dm_list_add(head, &(x)->list)
+#define LINK_DSO(dso) LINK(dso, &_dso_registry)
+#define LINK_THREAD(thread) LINK(thread, &_thread_registry)
+
+#define UNLINK(x) dm_list_del(&(x)->list)
+#define UNLINK_DSO(x) UNLINK(x)
+#define UNLINK_THREAD(x) UNLINK(x)
+
+#define DAEMON_NAME "dmeventd"
+
+/*
+ Global mutex for thread list access. Has to be held when:
+ - iterating thread list
+ - adding or removing elements from thread list
+ - changing or reading thread_status's fields:
+ processing, status, events
+ Use _lock_mutex() and _unlock_mutex() to hold/release it
+*/
+static pthread_mutex_t _global_mutex;
+
+/*
+ There are three states a thread can attain (see struct
+ thread_status, field int status):
+
+ - DM_THREAD_RUNNING: thread has started up and is either working or
+ waiting for events... transitions to either SHUTDOWN or DONE
+ - DM_THREAD_SHUTDOWN: thread is still doing something, but it is
+ supposed to terminate (and transition to DONE) as soon as it
+ finishes whatever it was doing at the point of flipping state to
+ SHUTDOWN... the thread is still on the thread list
+ - DM_THREAD_DONE: thread has terminated and has been moved over to
+ unused thread list, cleanup pending
+ */
+#define DM_THREAD_RUNNING 0
+#define DM_THREAD_SHUTDOWN 1
+#define DM_THREAD_DONE 2
+
+#define THREAD_STACK_SIZE (300*1024)
+
+#define DEBUGLOG(fmt, args...) _debuglog(fmt, ## args)
+
+int dmeventd_debug = 0;
+static int _foreground = 0;
+static int _restart = 0;
+static char **_initial_registrations = 0;
+
+/* Data kept about a DSO. */
+struct dso_data {
+ struct dm_list list;
+
+ char *dso_name; /* DSO name (eg, "evms", "dmraid", "lvm2"). */
+
+ void *dso_handle; /* Opaque handle as returned from dlopen(). */
+ unsigned int ref_count; /* Library reference count. */
+
+ /*
+ * Event processing.
+ *
+ * The DSO can do whatever appropriate steps if an event
+ * happens such as changing the mapping in case a mirror
+ * fails, update the application metadata etc.
+ *
+ * This function gets a dm_task that is a result of
+ * DM_DEVICE_WAITEVENT ioctl (results equivalent to
+ * DM_DEVICE_STATUS). It should not destroy it.
+ * The caller must dispose of the task.
+ */
+ void (*process_event)(struct dm_task *dmt, enum dm_event_mask event, void **user);
+
+ /*
+ * Device registration.
+ *
+ * When an application registers a device for an event, the DSO
+ * can carry out appropriate steps so that a later call to
+ * the process_event() function is sane (eg, read metadata
+ * and activate a mapping).
+ */
+ int (*register_device)(const char *device, const char *uuid, int major,
+ int minor, void **user);
+
+ /*
+ * Device unregistration.
+ *
+ * In case all devices of a mapping (eg, RAID10) are unregistered
+ * for events, the DSO can recognize this and carry out appropriate
+ * steps (eg, deactivate mapping, metadata update).
+ */
+ int (*unregister_device)(const char *device, const char *uuid,
+ int major, int minor, void **user);
+};
+static DM_LIST_INIT(_dso_registry);
+
+/* Structure to keep parsed register variables from client message. */
+struct message_data {
+ char *id;
+ char *dso_name; /* Name of DSO. */
+ char *device_uuid; /* Mapped device path. */
+ union {
+ char *str; /* Events string as fetched from message. */
+ enum dm_event_mask field; /* Events bitfield. */
+ } events;
+ union {
+ char *str;
+ uint32_t secs;
+ } timeout;
+ struct dm_event_daemon_message *msg; /* Pointer to message buffer. */
+};
+
+/*
+ * Housekeeping of thread+device states.
+ *
+ * One thread per mapped device which can block on it until an event
+ * occurs and the event processing function of the DSO gets called.
+ */
+struct thread_status {
+ struct dm_list list;
+
+ pthread_t thread;
+
+ struct dso_data *dso_data; /* DSO this thread accesses. */
+
+ struct {
+ char *uuid;
+ char *name;
+ int major, minor;
+ } device;
+ uint32_t event_nr; /* event number */
+ int processing; /* Set when event is being processed */
+
+ int status; /* see DM_THREAD_{RUNNING,SHUTDOWN,DONE}
+ constants above */
+ enum dm_event_mask events; /* bitfield for event filter. */
+ enum dm_event_mask current_events; /* bitfield for occured events. */
+ struct dm_task *current_task;
+ time_t next_time;
+ uint32_t timeout;
+ struct dm_list timeout_list;
+ void *dso_private; /* dso per-thread status variable */
+};
+static DM_LIST_INIT(_thread_registry);
+static DM_LIST_INIT(_thread_registry_unused);
+
+static int _timeout_running;
+static DM_LIST_INIT(_timeout_registry);
+static pthread_mutex_t _timeout_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t _timeout_cond = PTHREAD_COND_INITIALIZER;
+
+static void _debuglog(const char *fmt, ...)
+{
+ time_t P;
+ va_list ap;
+
+ if (!_foreground)
+ return;
+
+ va_start(ap,fmt);
+
+ time(&P);
+ fprintf(stderr, "dmeventd[%p]: %.15s ", (void *) pthread_self(), ctime(&P)+4 );
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+
+ va_end(ap);
+}
+
+/* Allocate/free the status structure for a monitoring thread. */
+static struct thread_status *_alloc_thread_status(struct message_data *data,
+ struct dso_data *dso_data)
+{
+ struct thread_status *ret = (typeof(ret)) dm_zalloc(sizeof(*ret));
+
+ if (!ret)
+ return NULL;
+
+ if (!(ret->device.uuid = dm_strdup(data->device_uuid))) {
+ dm_free(ret);
+ return NULL;
+ }
+
+ ret->current_task = NULL;
+ ret->device.name = NULL;
+ ret->device.major = ret->device.minor = 0;
+ ret->dso_data = dso_data;
+ ret->events = data->events.field;
+ ret->timeout = data->timeout.secs;
+ dm_list_init(&ret->timeout_list);
+
+ return ret;
+}
+
+static void _lib_put(struct dso_data *data);
+static void _free_thread_status(struct thread_status *thread)
+{
+ _lib_put(thread->dso_data);
+ if (thread->current_task)
+ dm_task_destroy(thread->current_task);
+ dm_free(thread->device.uuid);
+ dm_free(thread->device.name);
+ dm_free(thread);
+}
+
+/* Allocate/free DSO data. */
+static struct dso_data *_alloc_dso_data(struct message_data *data)
+{
+ struct dso_data *ret = (typeof(ret)) dm_zalloc(sizeof(*ret));
+
+ if (!ret)
+ return NULL;
+
+ if (!(ret->dso_name = dm_strdup(data->dso_name))) {
+ dm_free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+/* Create a device monitoring thread. */
+static int _pthread_create_smallstack(pthread_t *t, void *(*fun)(void *), void *arg)
+{
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ /*
+ * We use a smaller stack since it gets preallocated in its entirety
+ */
+ pthread_attr_setstacksize(&attr, THREAD_STACK_SIZE);
+ return pthread_create(t, &attr, fun, arg);
+}
+
+static void _free_dso_data(struct dso_data *data)
+{
+ dm_free(data->dso_name);
+ dm_free(data);
+}
+
+/*
+ * Fetch a string off src and duplicate it into *ptr.
+ * Pay attention to zero-length strings.
+ */
+/* FIXME? move to libdevmapper to share with the client lib (need to
+ make delimiter a parameter then) */
+static int _fetch_string(char **ptr, char **src, const int delimiter)
+{
+ int ret = 0;
+ char *p;
+ size_t len;
+
+ if ((p = strchr(*src, delimiter)))
+ *p = 0;
+
+ if ((*ptr = dm_strdup(*src))) {
+ if ((len = strlen(*ptr)))
+ *src += len;
+ else {
+ dm_free(*ptr);
+ *ptr = NULL;
+ }
+
+ (*src)++;
+ ret = 1;
+ }
+
+ if (p)
+ *p = delimiter;
+
+ return ret;
+}
+
+/* Free message memory. */
+static void _free_message(struct message_data *message_data)
+{
+ dm_free(message_data->id);
+ dm_free(message_data->dso_name);
+
+ dm_free(message_data->device_uuid);
+
+}
+
+/* Parse a register message from the client. */
+static int _parse_message(struct message_data *message_data)
+{
+ int ret = 0;
+ char *p = message_data->msg->data;
+ struct dm_event_daemon_message *msg = message_data->msg;
+
+ if (!msg->data)
+ return 0;
+
+ /*
+ * Retrieve application identifier, mapped device
+ * path and events # string from message.
+ */
+ if (_fetch_string(&message_data->id, &p, ' ') &&
+ _fetch_string(&message_data->dso_name, &p, ' ') &&
+ _fetch_string(&message_data->device_uuid, &p, ' ') &&
+ _fetch_string(&message_data->events.str, &p, ' ') &&
+ _fetch_string(&message_data->timeout.str, &p, ' ')) {
+ if (message_data->events.str) {
+ enum dm_event_mask i = atoi(message_data->events.str);
+
+ /*
+ * Free string representaion of events.
+ * Not needed an more.
+ */
+ dm_free(message_data->events.str);
+ message_data->events.field = i;
+ }
+ if (message_data->timeout.str) {
+ uint32_t secs = atoi(message_data->timeout.str);
+ dm_free(message_data->timeout.str);
+ message_data->timeout.secs = secs ? secs :
+ DM_EVENT_DEFAULT_TIMEOUT;
+ }
+
+ ret = 1;
+ }
+
+ dm_free(msg->data);
+ msg->data = NULL;
+ msg->size = 0;
+ return ret;
+};
+
+/* Global mutex to lock access to lists et al. See _global_mutex
+ above. */
+static int _lock_mutex(void)
+{
+ return pthread_mutex_lock(&_global_mutex);
+}
+
+static int _unlock_mutex(void)
+{
+ return pthread_mutex_unlock(&_global_mutex);
+}
+
+/* Check, if a device exists. */
+static int _fill_device_data(struct thread_status *ts)
+{
+ struct dm_task *dmt;
+ struct dm_info dmi;
+
+ if (!ts->device.uuid)
+ return 0;
+
+ ts->device.name = NULL;
+ ts->device.major = ts->device.minor = 0;
+
+ dmt = dm_task_create(DM_DEVICE_INFO);
+ if (!dmt)
+ return 0;
+
+ dm_task_set_uuid(dmt, ts->device.uuid);
+ if (!dm_task_run(dmt))
+ goto fail;
+
+ ts->device.name = dm_strdup(dm_task_get_name(dmt));
+ if (!ts->device.name)
+ goto fail;
+
+ if (!dm_task_get_info(dmt, &dmi))
+ goto fail;
+
+ ts->device.major = dmi.major;
+ ts->device.minor = dmi.minor;
+
+ dm_task_destroy(dmt);
+ return 1;
+
+ fail:
+ dm_task_destroy(dmt);
+ dm_free(ts->device.name);
+ return 0;
+}
+
+/*
+ * Find an existing thread for a device.
+ *
+ * Mutex must be held when calling this.
+ */
+static struct thread_status *_lookup_thread_status(struct message_data *data)
+{
+ struct thread_status *thread;
+
+ dm_list_iterate_items(thread, &_thread_registry)
+ if (!strcmp(data->device_uuid, thread->device.uuid))
+ return thread;
+
+ return NULL;
+}
+
+static int _get_status(struct message_data *message_data)
+{
+ struct dm_event_daemon_message *msg = message_data->msg;
+ struct thread_status *thread;
+ int i = 0, j = 0;
+ int ret = -1;
+ int count = dm_list_size(&_thread_registry);
+ int size = 0, current = 0;
+ char *buffers[count];
+ char *message;
+
+ dm_free(msg->data);
+
+ for (i = 0; i < count; ++i)
+ buffers[i] = NULL;
+
+ i = 0;
+ _lock_mutex();
+ dm_list_iterate_items(thread, &_thread_registry) {
+ if ((current = dm_asprintf(buffers + i, "0:%d %s %s %u %" PRIu32 ";",
+ i, thread->dso_data->dso_name,
+ thread->device.uuid, thread->events,
+ thread->timeout)) < 0) {
+ _unlock_mutex();
+ goto out;
+ }
+ ++ i;
+ size += current;
+ }
+ _unlock_mutex();
+
+ msg->size = size + strlen(message_data->id) + 1;
+ msg->data = dm_malloc(msg->size);
+ if (!msg->data)
+ goto out;
+ *msg->data = 0;
+
+ message = msg->data;
+ strcpy(message, message_data->id);
+ message += strlen(message_data->id);
+ *message = ' ';
+ message ++;
+ for (j = 0; j < i; ++j) {
+ strcpy(message, buffers[j]);
+ message += strlen(buffers[j]);
+ }
+
+ ret = 0;
+ out:
+ for (j = 0; j < i; ++j)
+ dm_free(buffers[j]);
+ return ret;
+
+}
+
+/* Cleanup at exit. */
+static void _exit_dm_lib(void)
+{
+ dm_lib_release();
+ dm_lib_exit();
+}
+
+static void _exit_timeout(void *unused __attribute__((unused)))
+{
+ _timeout_running = 0;
+ pthread_mutex_unlock(&_timeout_mutex);
+}
+
+/* Wake up monitor threads every so often. */
+static void *_timeout_thread(void *unused __attribute__((unused)))
+{
+ struct timespec timeout;
+ time_t curr_time;
+
+ timeout.tv_nsec = 0;
+ pthread_cleanup_push(_exit_timeout, NULL);
+ pthread_mutex_lock(&_timeout_mutex);
+
+ while (!dm_list_empty(&_timeout_registry)) {
+ struct thread_status *thread;
+
+ timeout.tv_sec = 0;
+ curr_time = time(NULL);
+
+ dm_list_iterate_items_gen(thread, &_timeout_registry, timeout_list) {
+ if (thread->next_time <= curr_time) {
+ thread->next_time = curr_time + thread->timeout;
+ pthread_kill(thread->thread, SIGALRM);
+ }
+
+ if (thread->next_time < timeout.tv_sec || !timeout.tv_sec)
+ timeout.tv_sec = thread->next_time;
+ }
+
+ pthread_cond_timedwait(&_timeout_cond, &_timeout_mutex,
+ &timeout);
+ }
+
+ pthread_cleanup_pop(1);
+
+ return NULL;
+}
+
+static int _register_for_timeout(struct thread_status *thread)
+{
+ int ret = 0;
+
+ pthread_mutex_lock(&_timeout_mutex);
+
+ thread->next_time = time(NULL) + thread->timeout;
+
+ if (dm_list_empty(&thread->timeout_list)) {
+ dm_list_add(&_timeout_registry, &thread->timeout_list);
+ if (_timeout_running)
+ pthread_cond_signal(&_timeout_cond);
+ }
+
+ if (!_timeout_running) {
+ pthread_t timeout_id;
+
+ if (!(ret = -_pthread_create_smallstack(&timeout_id, _timeout_thread, NULL)))
+ _timeout_running = 1;
+ }
+
+ pthread_mutex_unlock(&_timeout_mutex);
+
+ return ret;
+}
+
+static void _unregister_for_timeout(struct thread_status *thread)
+{
+ pthread_mutex_lock(&_timeout_mutex);
+ if (!dm_list_empty(&thread->timeout_list)) {
+ dm_list_del(&thread->timeout_list);
+ dm_list_init(&thread->timeout_list);
+ }
+ pthread_mutex_unlock(&_timeout_mutex);
+}
+
+static void _no_intr_log(int level, const char *file, int line,
+ const char *f, ...)
+{
+ va_list ap;
+
+ if (errno == EINTR)
+ return;
+ if (level > _LOG_WARN)
+ return;
+
+ va_start(ap, f);
+
+ if (level < _LOG_WARN)
+ vfprintf(stderr, f, ap);
+ else
+ vprintf(f, ap);
+
+ va_end(ap);
+
+ if (level < _LOG_WARN)
+ fprintf(stderr, "\n");
+ else
+ fprintf(stdout, "\n");
+}
+
+static sigset_t _unblock_sigalrm(void)
+{
+ sigset_t set, old;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGALRM);
+ pthread_sigmask(SIG_UNBLOCK, &set, &old);
+ return old;
+}
+
+#define DM_WAIT_RETRY 0
+#define DM_WAIT_INTR 1
+#define DM_WAIT_FATAL 2
+
+/* Wait on a device until an event occurs. */
+static int _event_wait(struct thread_status *thread, struct dm_task **task)
+{
+ sigset_t set;
+ int ret = DM_WAIT_RETRY;
+ struct dm_task *dmt;
+ struct dm_info info;
+
+ *task = 0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
+ return DM_WAIT_RETRY;
+
+ thread->current_task = dmt;
+
+ if (!dm_task_set_uuid(dmt, thread->device.uuid) ||
+ !dm_task_set_event_nr(dmt, thread->event_nr))
+ goto out;
+
+ /*
+ * This is so that you can break out of waiting on an event,
+ * either for a timeout event, or to cancel the thread.
+ */
+ set = _unblock_sigalrm();
+ dm_log_init(_no_intr_log);
+ errno = 0;
+ if (dm_task_run(dmt)) {
+ thread->current_events |= DM_EVENT_DEVICE_ERROR;
+ ret = DM_WAIT_INTR;
+
+ if ((ret = dm_task_get_info(dmt, &info)))
+ thread->event_nr = info.event_nr;
+ } else if (thread->events & DM_EVENT_TIMEOUT && errno == EINTR) {
+ thread->current_events |= DM_EVENT_TIMEOUT;
+ ret = DM_WAIT_INTR;
+ } else if (thread->status == DM_THREAD_SHUTDOWN && errno == EINTR) {
+ ret = DM_WAIT_FATAL;
+ } else {
+ syslog(LOG_NOTICE, "dm_task_run failed, errno = %d, %s",
+ errno, strerror(errno));
+ if (errno == ENXIO) {
+ syslog(LOG_ERR, "%s disappeared, detaching",
+ thread->device.name);
+ ret = DM_WAIT_FATAL;
+ }
+ }
+
+ pthread_sigmask(SIG_SETMASK, &set, NULL);
+ dm_log_init(NULL);
+
+ out:
+ if (ret == DM_WAIT_FATAL || ret == DM_WAIT_RETRY) {
+ dm_task_destroy(dmt);
+ thread->current_task = NULL;
+ } else
+ *task = dmt;
+
+ return ret;
+}
+
+/* Register a device with the DSO. */
+static int _do_register_device(struct thread_status *thread)
+{
+ return thread->dso_data->register_device(thread->device.name,
+ thread->device.uuid,
+ thread->device.major,
+ thread->device.minor,
+ &(thread->dso_private));
+}
+
+/* Unregister a device with the DSO. */
+static int _do_unregister_device(struct thread_status *thread)
+{
+ return thread->dso_data->unregister_device(thread->device.name,
+ thread->device.uuid,
+ thread->device.major,
+ thread->device.minor,
+ &(thread->dso_private));
+}
+
+/* Process an event in the DSO. */
+static void _do_process_event(struct thread_status *thread, struct dm_task *task)
+{
+ thread->dso_data->process_event(task, thread->current_events, &(thread->dso_private));
+}
+
+/* Thread cleanup handler to unregister device. */
+static void _monitor_unregister(void *arg)
+{
+ struct thread_status *thread = arg, *thread_iter;
+
+ if (!_do_unregister_device(thread))
+ syslog(LOG_ERR, "%s: %s unregister failed\n", __func__,
+ thread->device.name);
+ if (thread->current_task)
+ dm_task_destroy(thread->current_task);
+ thread->current_task = NULL;
+
+ _lock_mutex();
+ if (thread->events & DM_EVENT_TIMEOUT) {
+ /* _unregister_for_timeout locks another mutex, we
+ don't want to deadlock so we release our mutex for
+ a bit */
+ _unlock_mutex();
+ _unregister_for_timeout(thread);
+ _lock_mutex();
+ }
+ /* we may have been relinked to unused registry since we were
+ called, so check that */
+ dm_list_iterate_items(thread_iter, &_thread_registry_unused)
+ if (thread_iter == thread) {
+ thread->status = DM_THREAD_DONE;
+ _unlock_mutex();
+ return;
+ }
+ thread->status = DM_THREAD_DONE;
+ UNLINK_THREAD(thread);
+ LINK(thread, &_thread_registry_unused);
+ _unlock_mutex();
+}
+
+static struct dm_task *_get_device_status(struct thread_status *ts)
+{
+ struct dm_task *dmt = dm_task_create(DM_DEVICE_STATUS);
+
+ if (!dmt)
+ return NULL;
+
+ dm_task_set_uuid(dmt, ts->device.uuid);
+
+ if (!dm_task_run(dmt)) {
+ dm_task_destroy(dmt);
+ return NULL;
+ }
+
+ return dmt;
+}
+
+/* Device monitoring thread. */
+static void *_monitor_thread(void *arg)
+{
+ struct thread_status *thread = arg;
+ int wait_error = 0;
+ struct dm_task *task;
+
+ pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+ pthread_cleanup_push(_monitor_unregister, thread);
+
+ /* Wait for do_process_request() to finish its task. */
+ _lock_mutex();
+ thread->status = DM_THREAD_RUNNING;
+ _unlock_mutex();
+
+ /* Loop forever awaiting/analyzing device events. */
+ while (1) {
+ thread->current_events = 0;
+
+ wait_error = _event_wait(thread, &task);
+ if (wait_error == DM_WAIT_RETRY)
+ continue;
+
+ if (wait_error == DM_WAIT_FATAL)
+ break;
+
+ /* Timeout occurred, task is not filled properly.
+ * We get device status here for processing it in DSO.
+ */
+ if (wait_error == DM_WAIT_INTR &&
+ thread->current_events & DM_EVENT_TIMEOUT) {
+ dm_task_destroy(task);
+ task = _get_device_status(thread);
+ /* FIXME: syslog fail here ? */
+ if (!(thread->current_task = task))
+ continue;
+ }
+
+ /*
+ * We know that wait succeeded and stored a
+ * pointer to dm_task with device status into task.
+ */
+
+ /*
+ * Check against filter.
+ *
+ * If there's current events delivered from _event_wait() AND
+ * the device got registered for those events AND
+ * those events haven't been processed yet, call
+ * the DSO's process_event() handler.
+ */
+ _lock_mutex();
+ if (thread->status == DM_THREAD_SHUTDOWN) {
+ _unlock_mutex();
+ break;
+ }
+ _unlock_mutex();
+
+ if (thread->events & thread->current_events) {
+ _lock_mutex();
+ thread->processing = 1;
+ _unlock_mutex();
+
+ _do_process_event(thread, task);
+ dm_task_destroy(task);
+ thread->current_task = NULL;
+
+ _lock_mutex();
+ thread->processing = 0;
+ _unlock_mutex();
+ } else {
+ dm_task_destroy(task);
+ thread->current_task = NULL;
+ }
+ }
+
+ pthread_cleanup_pop(1);
+
+ return NULL;
+}
+
+/* Create a device monitoring thread. */
+static int _create_thread(struct thread_status *thread)
+{
+ return _pthread_create_smallstack(&thread->thread, _monitor_thread, thread);
+}
+
+static int _terminate_thread(struct thread_status *thread)
+{
+ return pthread_kill(thread->thread, SIGALRM);
+}
+
+/* DSO reference counting. Call with _global_mutex locked! */
+static void _lib_get(struct dso_data *data)
+{
+ data->ref_count++;
+}
+
+static void _lib_put(struct dso_data *data)
+{
+ if (!--data->ref_count) {
+ dlclose(data->dso_handle);
+ UNLINK_DSO(data);
+ _free_dso_data(data);
+ }
+}
+
+/* Find DSO data. */
+static struct dso_data *_lookup_dso(struct message_data *data)
+{
+ struct dso_data *dso_data, *ret = NULL;
+
+ dm_list_iterate_items(dso_data, &_dso_registry)
+ if (!strcmp(data->dso_name, dso_data->dso_name)) {
+ _lib_get(dso_data);
+ ret = dso_data;
+ break;
+ }
+
+ return ret;
+}
+
+/* Lookup DSO symbols we need. */
+static int _lookup_symbol(void *dl, void **symbol, const char *name)
+{
+ if ((*symbol = dlsym(dl, name)))
+ return 1;
+
+ return 0;
+}
+
+static int lookup_symbols(void *dl, struct dso_data *data)
+{
+ return _lookup_symbol(dl, (void *) &data->process_event,
+ "process_event") &&
+ _lookup_symbol(dl, (void *) &data->register_device,
+ "register_device") &&
+ _lookup_symbol(dl, (void *) &data->unregister_device,
+ "unregister_device");
+}
+
+/* Load an application specific DSO. */
+static struct dso_data *_load_dso(struct message_data *data)
+{
+ void *dl;
+ struct dso_data *ret = NULL;
+
+ if (!(dl = dlopen(data->dso_name, RTLD_NOW))) {
+ const char *dlerr = dlerror();
+ syslog(LOG_ERR, "dmeventd %s dlopen failed: %s", data->dso_name,
+ dlerr);
+ data->msg->size =
+ dm_asprintf(&(data->msg->data), "%s %s dlopen failed: %s",
+ data->id, data->dso_name, dlerr);
+ return NULL;
+ }
+
+ if (!(ret = _alloc_dso_data(data))) {
+ dlclose(dl);
+ return NULL;
+ }
+
+ if (!(lookup_symbols(dl, ret))) {
+ _free_dso_data(ret);
+ dlclose(dl);
+ return NULL;
+ }
+
+ /*
+ * Keep handle to close the library once
+ * we've got no references to it any more.
+ */
+ ret->dso_handle = dl;
+ _lib_get(ret);
+
+ _lock_mutex();
+ LINK_DSO(ret);
+ _unlock_mutex();
+
+ return ret;
+}
+
+/* Return success on daemon active check. */
+static int _active(struct message_data *message_data)
+{
+ return 0;
+}
+
+/*
+ * Register for an event.
+ *
+ * Only one caller at a time here, because we use
+ * a FIFO and lock it against multiple accesses.
+ */
+static int _register_for_event(struct message_data *message_data)
+{
+ int ret = 0;
+ struct thread_status *thread, *thread_new = NULL;
+ struct dso_data *dso_data;
+
+ if (!(dso_data = _lookup_dso(message_data)) &&
+ !(dso_data = _load_dso(message_data))) {
+ stack;
+#ifdef ELIBACC
+ ret = -ELIBACC;
+#else
+ ret = -ENODEV;
+#endif
+ goto out;
+ }
+
+ /* Preallocate thread status struct to avoid deadlock. */
+ if (!(thread_new = _alloc_thread_status(message_data, dso_data))) {
+ stack;
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (!_fill_device_data(thread_new)) {
+ stack;
+ ret = -ENODEV;
+ goto out;
+ }
+
+ _lock_mutex();
+
+ /* If creation of timeout thread fails (as it may), we fail
+ here completely. The client is responsible for either
+ retrying later or trying to register without timeout
+ events. However, if timeout thread cannot be started, it
+ usually means we are so starved on resources that we are
+ almost as good as dead already... */
+ if (thread_new->events & DM_EVENT_TIMEOUT) {
+ ret = -_register_for_timeout(thread_new);
+ if (ret) {
+ _unlock_mutex();
+ goto out;
+ }
+ }
+
+ if (!(thread = _lookup_thread_status(message_data))) {
+ _unlock_mutex();
+
+ if (!(ret = _do_register_device(thread_new)))
+ goto out;
+
+ thread = thread_new;
+ thread_new = NULL;
+
+ /* Try to create the monitoring thread for this device. */
+ _lock_mutex();
+ if ((ret = -_create_thread(thread))) {
+ _unlock_mutex();
+ _do_unregister_device(thread);
+ _free_thread_status(thread);
+ goto out;
+ } else
+ LINK_THREAD(thread);
+ }
+
+ /* Or event # into events bitfield. */
+ thread->events |= message_data->events.field;
+
+ _unlock_mutex();
+
+ out:
+ /*
+ * Deallocate thread status after releasing
+ * the lock in case we haven't used it.
+ */
+ if (thread_new)
+ _free_thread_status(thread_new);
+
+ return ret;
+}
+
+/*
+ * Unregister for an event.
+ *
+ * Only one caller at a time here as with register_for_event().
+ */
+static int _unregister_for_event(struct message_data *message_data)
+{
+ int ret = 0;
+ struct thread_status *thread;
+
+ /*
+ * Clear event in bitfield and deactivate
+ * monitoring thread in case bitfield is 0.
+ */
+ _lock_mutex();
+
+ if (!(thread = _lookup_thread_status(message_data))) {
+ _unlock_mutex();
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (thread->status == DM_THREAD_DONE) {
+ /* the thread has terminated while we were not
+ watching */
+ _unlock_mutex();
+ return 0;
+ }
+
+ thread->events &= ~message_data->events.field;
+
+ if (!(thread->events & DM_EVENT_TIMEOUT))
+ _unregister_for_timeout(thread);
+ /*
+ * In case there's no events to monitor on this device ->
+ * unlink and terminate its monitoring thread.
+ */
+ if (!thread->events) {
+ UNLINK_THREAD(thread);
+ LINK(thread, &_thread_registry_unused);
+ }
+ _unlock_mutex();
+
+ out:
+ return ret;
+}
+
+/*
+ * Get registered device.
+ *
+ * Only one caller at a time here as with register_for_event().
+ */
+static int _registered_device(struct message_data *message_data,
+ struct thread_status *thread)
+{
+ struct dm_event_daemon_message *msg = message_data->msg;
+
+ const char *fmt = "%s %s %s %u";
+ const char *id = message_data->id;
+ const char *dso = thread->dso_data->dso_name;
+ const char *dev = thread->device.uuid;
+ unsigned events = ((thread->status == DM_THREAD_RUNNING)
+ && (thread->events)) ? thread->events : thread->
+ events | DM_EVENT_REGISTRATION_PENDING;
+
+ dm_free(msg->data);
+
+ msg->size = dm_asprintf(&(msg->data), fmt, id, dso, dev, events);
+
+ _unlock_mutex();
+
+ return 0;
+}
+
+static int _want_registered_device(char *dso_name, char *device_uuid,
+ struct thread_status *thread)
+{
+ /* If DSO names and device paths are equal. */
+ if (dso_name && device_uuid)
+ return !strcmp(dso_name, thread->dso_data->dso_name) &&
+ !strcmp(device_uuid, thread->device.uuid) &&
+ (thread->status == DM_THREAD_RUNNING ||
+ (thread->events & DM_EVENT_REGISTRATION_PENDING));
+
+ /* If DSO names are equal. */
+ if (dso_name)
+ return !strcmp(dso_name, thread->dso_data->dso_name) &&
+ (thread->status == DM_THREAD_RUNNING ||
+ (thread->events & DM_EVENT_REGISTRATION_PENDING));
+
+ /* If device paths are equal. */
+ if (device_uuid)
+ return !strcmp(device_uuid, thread->device.uuid) &&
+ (thread->status == DM_THREAD_RUNNING ||
+ (thread->events & DM_EVENT_REGISTRATION_PENDING));
+
+ return 1;
+}
+
+static int _get_registered_dev(struct message_data *message_data, int next)
+{
+ struct thread_status *thread, *hit = NULL;
+
+ _lock_mutex();
+
+ /* Iterate list of threads checking if we want a particular one. */
+ dm_list_iterate_items(thread, &_thread_registry)
+ if (_want_registered_device(message_data->dso_name,
+ message_data->device_uuid,
+ thread)) {
+ hit = thread;
+ break;
+ }
+
+ /*
+ * If we got a registered device and want the next one ->
+ * fetch next conforming element off the list.
+ */
+ if (hit && !next) {
+ _unlock_mutex();
+ return _registered_device(message_data, hit);
+ }
+
+ if (!hit)
+ goto out;
+
+ thread = hit;
+
+ while (1) {
+ if (dm_list_end(&_thread_registry, &thread->list))
+ goto out;
+
+ thread = dm_list_item(thread->list.n, struct thread_status);
+ if (_want_registered_device(message_data->dso_name, NULL, thread)) {
+ hit = thread;
+ break;
+ }
+ }
+
+ _unlock_mutex();
+ return _registered_device(message_data, hit);
+
+ out:
+ _unlock_mutex();
+
+ return -ENOENT;
+}
+
+static int _get_registered_device(struct message_data *message_data)
+{
+ return _get_registered_dev(message_data, 0);
+}
+
+static int _get_next_registered_device(struct message_data *message_data)
+{
+ return _get_registered_dev(message_data, 1);
+}
+
+static int _set_timeout(struct message_data *message_data)
+{
+ struct thread_status *thread;
+
+ _lock_mutex();
+ if ((thread = _lookup_thread_status(message_data)))
+ thread->timeout = message_data->timeout.secs;
+ _unlock_mutex();
+
+ return thread ? 0 : -ENODEV;
+}
+
+static int _get_timeout(struct message_data *message_data)
+{
+ struct thread_status *thread;
+ struct dm_event_daemon_message *msg = message_data->msg;
+
+ dm_free(msg->data);
+
+ _lock_mutex();
+ if ((thread = _lookup_thread_status(message_data))) {
+ msg->size =
+ dm_asprintf(&(msg->data), "%s %" PRIu32, message_data->id,
+ thread->timeout);
+ } else {
+ msg->data = NULL;
+ msg->size = 0;
+ }
+ _unlock_mutex();
+
+ return thread ? 0 : -ENODEV;
+}
+
+/* Initialize a fifos structure with path names. */
+static void _init_fifos(struct dm_event_fifos *fifos)
+{
+ memset(fifos, 0, sizeof(*fifos));
+
+ fifos->client_path = DM_EVENT_FIFO_CLIENT;
+ fifos->server_path = DM_EVENT_FIFO_SERVER;
+}
+
+/* Open fifos used for client communication. */
+static int _open_fifos(struct dm_event_fifos *fifos)
+{
+ int orig_errno;
+
+ /* Create client fifo. */
+ (void) dm_prepare_selinux_context(fifos->client_path, S_IFIFO);
+ if ((mkfifo(fifos->client_path, 0600) == -1) && errno != EEXIST) {
+ syslog(LOG_ERR, "%s: Failed to create client fifo.\n", __func__);
+ orig_errno = errno;
+ (void) dm_prepare_selinux_context(NULL, 0);
+ stack;
+ return -orig_errno;
+ }
+
+ /* Create server fifo. */
+ (void) dm_prepare_selinux_context(fifos->server_path, S_IFIFO);
+ if ((mkfifo(fifos->server_path, 0600) == -1) && errno != EEXIST) {
+ syslog(LOG_ERR, "%s: Failed to create server fifo.\n", __func__);
+ orig_errno = errno;
+ (void) dm_prepare_selinux_context(NULL, 0);
+ stack;
+ return -orig_errno;
+ }
+
+ (void) dm_prepare_selinux_context(NULL, 0);
+
+ struct stat st;
+
+ /* Warn about wrong permissions if applicable */
+ if ((!stat(fifos->client_path, &st)) && (st.st_mode & 0777) != 0600)
+ syslog(LOG_WARNING, "Fixing wrong permissions on %s",
+ fifos->client_path);
+
+ if ((!stat(fifos->server_path, &st)) && (st.st_mode & 0777) != 0600)
+ syslog(LOG_WARNING, "Fixing wrong permissions on %s",
+ fifos->server_path);
+
+ /* If they were already there, make sure permissions are ok. */
+ if (chmod(fifos->client_path, 0600)) {
+ syslog(LOG_ERR, "Unable to set correct file permissions on %s",
+ fifos->client_path);
+ return -errno;
+ }
+
+ if (chmod(fifos->server_path, 0600)) {
+ syslog(LOG_ERR, "Unable to set correct file permissions on %s",
+ fifos->server_path);
+ return -errno;
+ }
+
+ /* Need to open read+write or we will block or fail */
+ if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) {
+ stack;
+ return -errno;
+ }
+
+ /* Need to open read+write for select() to work. */
+ if ((fifos->client = open(fifos->client_path, O_RDWR)) < 0) {
+ stack;
+ close(fifos->server);
+ return -errno;
+ }
+
+ return 0;
+}
+
+/*
+ * Read message from client making sure that data is available
+ * and a complete message is read. Must not block indefinitely.
+ */
+static int _client_read(struct dm_event_fifos *fifos,
+ struct dm_event_daemon_message *msg)
+{
+ struct timeval t;
+ unsigned bytes = 0;
+ int ret = 0;
+ fd_set fds;
+ size_t size = 2 * sizeof(uint32_t); /* status + size */
+ uint32_t *header = alloca(size);
+ char *buf = (char *)header;
+
+ msg->data = NULL;
+
+ errno = 0;
+ while (bytes < size && errno != EOF) {
+ /* Watch client read FIFO for input. */
+ FD_ZERO(&fds);
+ FD_SET(fifos->client, &fds);
+ t.tv_sec = 1;
+ t.tv_usec = 0;
+ ret = select(fifos->client + 1, &fds, NULL, NULL, &t);
+
+ if (!ret && !bytes) /* nothing to read */
+ return 0;
+
+ if (!ret) /* trying to finish read */
+ continue;
+
+ if (ret < 0) /* error */
+ return 0;
+
+ ret = read(fifos->client, buf + bytes, size - bytes);
+ bytes += ret > 0 ? ret : 0;
+ if (header && (bytes == 2 * sizeof(uint32_t))) {
+ msg->cmd = ntohl(header[0]);
+ msg->size = ntohl(header[1]);
+ buf = msg->data = dm_malloc(msg->size);
+ size = msg->size;
+ bytes = 0;
+ header = 0;
+ }
+ }
+
+ if (bytes != size) {
+ dm_free(msg->data);
+ msg->data = NULL;
+ msg->size = 0;
+ }
+
+ return bytes == size;
+}
+
+/*
+ * Write a message to the client making sure that it is ready to write.
+ */
+static int _client_write(struct dm_event_fifos *fifos,
+ struct dm_event_daemon_message *msg)
+{
+ unsigned bytes = 0;
+ int ret = 0;
+ fd_set fds;
+
+ size_t size = 2 * sizeof(uint32_t) + msg->size;
+ uint32_t *header = alloca(size);
+ char *buf = (char *)header;
+
+ header[0] = htonl(msg->cmd);
+ header[1] = htonl(msg->size);
+ if (msg->data)
+ memcpy(buf + 2 * sizeof(uint32_t), msg->data, msg->size);
+
+ errno = 0;
+ while (bytes < size && errno != EIO) {
+ do {
+ /* Watch client write FIFO to be ready for output. */
+ FD_ZERO(&fds);
+ FD_SET(fifos->server, &fds);
+ } while (select(fifos->server + 1, NULL, &fds, NULL, NULL) !=
+ 1);
+
+ ret = write(fifos->server, buf + bytes, size - bytes);
+ bytes += ret > 0 ? ret : 0;
+ }
+
+ return bytes == size;
+}
+
+/*
+ * Handle a client request.
+ *
+ * We put the request handling functions into
+ * a list because of the growing number.
+ */
+static int _handle_request(struct dm_event_daemon_message *msg,
+ struct message_data *message_data)
+{
+ static struct {
+ unsigned int cmd;
+ int (*f)(struct message_data *);
+ } requests[] = {
+ { DM_EVENT_CMD_REGISTER_FOR_EVENT, _register_for_event},
+ { DM_EVENT_CMD_UNREGISTER_FOR_EVENT, _unregister_for_event},
+ { DM_EVENT_CMD_GET_REGISTERED_DEVICE, _get_registered_device},
+ { DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE,
+ _get_next_registered_device},
+ { DM_EVENT_CMD_SET_TIMEOUT, _set_timeout},
+ { DM_EVENT_CMD_GET_TIMEOUT, _get_timeout},
+ { DM_EVENT_CMD_ACTIVE, _active},
+ { DM_EVENT_CMD_GET_STATUS, _get_status},
+ }, *req;
+
+ for (req = requests; req < requests + sizeof(requests); req++)
+ if (req->cmd == msg->cmd)
+ return req->f(message_data);
+
+ return -EINVAL;
+}
+
+/* Process a request passed from the communication thread. */
+static int _do_process_request(struct dm_event_daemon_message *msg)
+{
+ int ret;
+ char *answer;
+ static struct message_data message_data;
+
+ /* Parse the message. */
+ memset(&message_data, 0, sizeof(message_data));
+ message_data.msg = msg;
+ if (msg->cmd == DM_EVENT_CMD_HELLO || msg->cmd == DM_EVENT_CMD_DIE) {
+ ret = 0;
+ answer = msg->data;
+ if (answer) {
+ msg->size = dm_asprintf(&(msg->data), "%s %s", answer,
+ msg->cmd == DM_EVENT_CMD_DIE ? "DYING" : "HELLO");
+ dm_free(answer);
+ } else {
+ msg->size = 0;
+ msg->data = NULL;
+ }
+ } else if (msg->cmd != DM_EVENT_CMD_ACTIVE && !_parse_message(&message_data)) {
+ stack;
+ ret = -EINVAL;
+ } else
+ ret = _handle_request(msg, &message_data);
+
+ msg->cmd = ret;
+ if (!msg->data)
+ msg->size = dm_asprintf(&(msg->data), "%s %s", message_data.id, strerror(-ret));
+
+ _free_message(&message_data);
+
+ return ret;
+}
+
+/* Only one caller at a time. */
+static void _process_request(struct dm_event_fifos *fifos)
+{
+ int die = 0;
+ struct dm_event_daemon_message msg;
+
+ memset(&msg, 0, sizeof(msg));
+
+ /*
+ * Read the request from the client (client_read, client_write
+ * give true on success and false on failure).
+ */
+ if (!_client_read(fifos, &msg))
+ return;
+
+ if (msg.cmd == DM_EVENT_CMD_DIE)
+ die = 1;
+
+ /* _do_process_request fills in msg (if memory allows for
+ data, otherwise just cmd and size = 0) */
+ _do_process_request(&msg);
+
+ if (!_client_write(fifos, &msg))
+ stack;
+
+ if (die) raise(9);
+
+ dm_free(msg.data);
+}
+
+static void _process_initial_registrations(void)
+{
+ int i = 0;
+ char *reg;
+ struct dm_event_daemon_message msg = { 0, 0, NULL };
+
+ while ((reg = _initial_registrations[i])) {
+ msg.cmd = DM_EVENT_CMD_REGISTER_FOR_EVENT;
+ msg.size = strlen(reg);
+ msg.data = reg;
+ _do_process_request(&msg);
+ ++ i;
+ }
+}
+
+static void _cleanup_unused_threads(void)
+{
+ int ret;
+ struct dm_list *l;
+ struct thread_status *thread;
+
+ _lock_mutex();
+ while ((l = dm_list_first(&_thread_registry_unused))) {
+ thread = dm_list_item(l, struct thread_status);
+ if (thread->processing)
+ break; /* cleanup on the next round */
+
+ if (thread->status == DM_THREAD_RUNNING) {
+ thread->status = DM_THREAD_SHUTDOWN;
+ break;
+ }
+
+ if (thread->status == DM_THREAD_SHUTDOWN) {
+ if (!thread->events) {
+ /* turn codes negative -- should we be returning this? */
+ ret = _terminate_thread(thread);
+
+ if (ret == ESRCH) {
+ thread->status = DM_THREAD_DONE;
+ } else if (ret) {
+ syslog(LOG_ERR,
+ "Unable to terminate thread: %s\n",
+ strerror(-ret));
+ stack;
+ }
+ break;
+ }
+
+ dm_list_del(l);
+ syslog(LOG_ERR,
+ "thread can't be on unused list unless !thread->events");
+ thread->status = DM_THREAD_RUNNING;
+ LINK_THREAD(thread);
+
+ continue;
+ }
+
+ if (thread->status == DM_THREAD_DONE) {
+ dm_list_del(l);
+ pthread_join(thread->thread, NULL);
+ _free_thread_status(thread);
+ }
+ }
+
+ _unlock_mutex();
+}
+
+static void _sig_alarm(int signum __attribute__((unused)))
+{
+ pthread_testcancel();
+}
+
+/* Init thread signal handling. */
+static void _init_thread_signals(void)
+{
+ sigset_t my_sigset;
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = _sig_alarm;
+ sigaction(SIGALRM, &act, NULL);
+ sigfillset(&my_sigset);
+
+ /* These are used for exiting */
+ sigdelset(&my_sigset, SIGTERM);
+ sigdelset(&my_sigset, SIGINT);
+ sigdelset(&my_sigset, SIGHUP);
+ sigdelset(&my_sigset, SIGQUIT);
+
+ pthread_sigmask(SIG_BLOCK, &my_sigset, NULL);
+}
+
+/*
+ * exit_handler
+ * @sig
+ *
+ * Set the global variable which the process should
+ * be watching to determine when to exit.
+ */
+static void _exit_handler(int sig __attribute__((unused)))
+{
+ /*
+ * We exit when '_exit_now' is set.
+ * That is, when a signal has been received.
+ *
+ * We can not simply set '_exit_now' unless all
+ * threads are done processing.
+ */
+ if (!_thread_registries_empty) {
+ syslog(LOG_ERR, "There are still devices being monitored.");
+ syslog(LOG_ERR, "Refusing to exit.");
+ } else
+ _exit_now = 1;
+
+}
+
+#ifdef linux
+/*
+ * Protection against OOM killer if kernel supports it
+ */
+static int _set_oom_adj(int val)
+{
+ FILE *fp;
+
+ struct stat st;
+
+ if (stat(OOM_ADJ_FILE, &st) == -1) {
+ if (errno == ENOENT)
+ DEBUGLOG(OOM_ADJ_FILE " not found");
+ else
+ perror(OOM_ADJ_FILE ": stat failed");
+ return 1;
+ }
+
+ if (!(fp = fopen(OOM_ADJ_FILE, "w"))) {
+ perror(OOM_ADJ_FILE ": fopen failed");
+ return 0;
+ }
+
+ fprintf(fp, "%i", val);
+ if (dm_fclose(fp))
+ perror(OOM_ADJ_FILE ": fclose failed");
+
+ return 1;
+}
+#endif
+
+static void remove_lockfile(void)
+{
+ if (unlink(DMEVENTD_PIDFILE))
+ perror(DMEVENTD_PIDFILE ": unlink failed");
+}
+
+static void _daemonize(void)
+{
+ int child_status;
+ int fd;
+ pid_t pid;
+ struct rlimit rlim;
+ struct timeval tval;
+ sigset_t my_sigset;
+
+ sigemptyset(&my_sigset);
+ if (sigprocmask(SIG_SETMASK, &my_sigset, NULL) < 0) {
+ fprintf(stderr, "Unable to restore signals.\n");
+ exit(EXIT_FAILURE);
+ }
+ signal(SIGTERM, &_exit_handler);
+
+ switch (pid = fork()) {
+ case -1:
+ perror("fork failed:");
+ exit(EXIT_FAILURE);
+
+ case 0: /* Child */
+ break;
+
+ default:
+ /* Wait for response from child */
+ while (!waitpid(pid, &child_status, WNOHANG) && !_exit_now) {
+ tval.tv_sec = 0;
+ tval.tv_usec = 250000; /* .25 sec */
+ select(0, NULL, NULL, NULL, &tval);
+ }
+
+ if (_exit_now) /* Child has signaled it is ok - we can exit now */
+ exit(EXIT_SUCCESS);
+
+ /* Problem with child. Determine what it is by exit code */
+ switch (WEXITSTATUS(child_status)) {
+ case EXIT_DESC_CLOSE_FAILURE:
+ case EXIT_DESC_OPEN_FAILURE:
+ case EXIT_FIFO_FAILURE:
+ case EXIT_CHDIR_FAILURE:
+ default:
+ fprintf(stderr, "Child exited with code %d\n", WEXITSTATUS(child_status));
+ break;
+ }
+
+ exit(WEXITSTATUS(child_status));
+ }
+
+ if (chdir("/"))
+ exit(EXIT_CHDIR_FAILURE);
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
+ fd = 256; /* just have to guess */
+ else
+ fd = rlim.rlim_cur;
+
+ for (--fd; fd >= 0; fd--)
+ close(fd);
+
+ if ((open("/dev/null", O_RDONLY) < 0) ||
+ (open("/dev/null", O_WRONLY) < 0) ||
+ (open("/dev/null", O_WRONLY) < 0))
+ exit(EXIT_DESC_OPEN_FAILURE);
+
+ setsid();
+}
+
+static void restart(void)
+{
+ struct dm_event_fifos fifos;
+ struct dm_event_daemon_message msg = { 0, 0, NULL };
+ int i, count = 0;
+ char *message;
+ int length;
+
+ /* Get the list of registrations from the running daemon. */
+
+ if (!init_fifos(&fifos)) {
+ fprintf(stderr, "Could not initiate communication with existing dmeventd.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0)) {
+ fprintf(stderr, "Could not communicate with existing dmeventd.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_GET_STATUS, "-", "-", 0, 0)) {
+ exit(EXIT_FAILURE);
+ }
+
+ message = msg.data;
+ message = strchr(message, ' ');
+ ++ message;
+ length = strlen(msg.data);
+ for (i = 0; i < length; ++i) {
+ if (msg.data[i] == ';') {
+ msg.data[i] = 0;
+ ++count;
+ }
+ }
+
+ _initial_registrations = dm_malloc(sizeof(char*) * (count + 1));
+ for (i = 0; i < count; ++i) {
+ _initial_registrations[i] = dm_strdup(message);
+ message += strlen(message) + 1;
+ }
+ _initial_registrations[count] = 0;
+
+ if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_DIE, "-", "-", 0, 0)) {
+ fprintf(stderr, "Old dmeventd refused to die.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ fini_fifos(&fifos);
+}
+
+static void usage(char *prog, FILE *file)
+{
+ fprintf(file, "Usage:\n"
+ "%s [-V] [-h] [-d] [-d] [-d] [-f]\n\n"
+ " -V Show version of dmeventd\n"
+ " -h Show this help information\n"
+ " -d Log debug messages to syslog (-d, -dd, -ddd)\n"
+ " -f Don't fork, run in the foreground\n\n", prog);
+}
+
+int main(int argc, char *argv[])
+{
+ signed char opt;
+ struct dm_event_fifos fifos;
+ //struct sys_log logdata = {DAEMON_NAME, LOG_DAEMON};
+
+ opterr = 0;
+ optind = 0;
+
+ while ((opt = getopt(argc, argv, "?fhVdR")) != EOF) {
+ switch (opt) {
+ case 'h':
+ usage(argv[0], stdout);
+ exit(0);
+ case '?':
+ usage(argv[0], stderr);
+ exit(0);
+ case 'R':
+ _restart++;
+ break;
+ case 'f':
+ _foreground++;
+ break;
+ case 'd':
+ dmeventd_debug++;
+ break;
+ case 'V':
+ printf("dmeventd version: %s\n", DM_LIB_VERSION);
+ exit(1);
+ break;
+ }
+ }
+
+ /*
+ * Switch to C locale to avoid reading large locale-archive file
+ * used by some glibc (on some distributions it takes over 100MB).
+ * Daemon currently needs to use mlockall().
+ */
+ if (setenv("LANG", "C", 1))
+ perror("Cannot set LANG to C");
+
+ if (_restart)
+ restart();
+
+ if (!_foreground)
+ _daemonize();
+
+ openlog("dmeventd", LOG_PID, LOG_DAEMON);
+
+ (void) dm_prepare_selinux_context(DMEVENTD_PIDFILE, S_IFREG);
+ if (dm_create_lockfile(DMEVENTD_PIDFILE) == 0)
+ exit(EXIT_FAILURE);
+
+ atexit(remove_lockfile);
+ (void) dm_prepare_selinux_context(NULL, 0);
+
+ /* Set the rest of the signals to cause '_exit_now' to be set */
+ signal(SIGINT, &_exit_handler);
+ signal(SIGHUP, &_exit_handler);
+ signal(SIGQUIT, &_exit_handler);
+
+#ifdef linux
+ if (!_set_oom_adj(OOM_DISABLE) && !_set_oom_adj(OOM_ADJUST_MIN))
+ syslog(LOG_ERR, "Failed to set oom_adj to protect against OOM killer");
+#endif
+
+ _init_thread_signals();
+
+ //multilog_clear_logging();
+ //multilog_add_type(std_syslog, &logdata);
+ //multilog_init_verbose(std_syslog, _LOG_DEBUG);
+ //multilog_async(1);
+
+ _init_fifos(&fifos);
+
+ pthread_mutex_init(&_global_mutex, NULL);
+
+ if (_open_fifos(&fifos))
+ exit(EXIT_FIFO_FAILURE);
+
+ /* Signal parent, letting them know we are ready to go. */
+ if (!_foreground)
+ kill(getppid(), SIGTERM);
+ syslog(LOG_NOTICE, "dmeventd ready for processing.");
+
+ if (_initial_registrations)
+ _process_initial_registrations();
+
+ while (!_exit_now) {
+ _process_request(&fifos);
+ _cleanup_unused_threads();
+ if (!dm_list_empty(&_thread_registry)
+ || !dm_list_empty(&_thread_registry_unused))
+ _thread_registries_empty = 0;
+ else
+ _thread_registries_empty = 1;
+ }
+
+ _exit_dm_lib();
+
+ pthread_mutex_destroy(&_global_mutex);
+
+ syslog(LOG_NOTICE, "dmeventd shutting down.");
+ closelog();
+
+ exit(EXIT_SUCCESS);
+}
--- /dev/null
+/*
+ * Copyright (C) 2005-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __DMEVENTD_DOT_H__
+#define __DMEVENTD_DOT_H__
+
+/* FIXME This stuff must be configurable. */
+
+#define DM_EVENT_DAEMON "/sbin/dmeventd"
+#define DM_EVENT_LOCKFILE "/var/lock/dmeventd"
+#define DM_EVENT_FIFO_CLIENT "/var/run/dmeventd-client"
+#define DM_EVENT_FIFO_SERVER "/var/run/dmeventd-server"
+#define DM_EVENT_PIDFILE "/var/run/dmeventd.pid"
+
+#define DM_EVENT_DEFAULT_TIMEOUT 10
+
+/* Commands for the daemon passed in the message below. */
+enum dm_event_command {
+ DM_EVENT_CMD_ACTIVE = 1,
+ DM_EVENT_CMD_REGISTER_FOR_EVENT,
+ DM_EVENT_CMD_UNREGISTER_FOR_EVENT,
+ DM_EVENT_CMD_GET_REGISTERED_DEVICE,
+ DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE,
+ DM_EVENT_CMD_SET_TIMEOUT,
+ DM_EVENT_CMD_GET_TIMEOUT,
+ DM_EVENT_CMD_HELLO,
+ DM_EVENT_CMD_DIE,
+ DM_EVENT_CMD_GET_STATUS,
+};
+
+/* Message passed between client and daemon. */
+struct dm_event_daemon_message {
+ uint32_t cmd;
+ uint32_t size;
+ char *data;
+};
+
+/* FIXME Is this meant to be exported? I can't see where the
+ interface uses it. */
+/* Fifos for client/daemon communication. */
+struct dm_event_fifos {
+ int client;
+ int server;
+ const char *client_path;
+ const char *server_path;
+};
+
+/* EXIT_SUCCESS 0 -- stdlib.h */
+/* EXIT_FAILURE 1 -- stdlib.h */
+/* EXIT_LOCKFILE_INUSE 2 -- obsoleted */
+#define EXIT_DESC_CLOSE_FAILURE 3
+#define EXIT_DESC_OPEN_FAILURE 4
+/* EXIT_OPEN_PID_FAILURE 5 -- obsoleted */
+#define EXIT_FIFO_FAILURE 6
+#define EXIT_CHDIR_FAILURE 7
+
+/* Implemented in libdevmapper-event.c, but not part of public API. */
+int daemon_talk(struct dm_event_fifos *fifos,
+ struct dm_event_daemon_message *msg, int cmd,
+ const char *dso_name, const char *dev_name,
+ enum dm_event_mask evmask, uint32_t timeout);
+int init_fifos(struct dm_event_fifos *fifos);
+void fini_fifos(struct dm_event_fifos *fifos);
+
+#endif /* __DMEVENTD_DOT_H__ */
--- /dev/null
+/*
+ * Copyright (C) 2005-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+#include "libdevmapper-event.h"
+//#include "libmultilog.h"
+#include "dmeventd.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <arpa/inet.h> /* for htonl, ntohl */
+
+static int _sequence_nr = 0;
+
+struct dm_event_handler {
+ char *dso;
+
+ char *dmeventd_path;
+
+ char *dev_name;
+
+ char *uuid;
+ int major;
+ int minor;
+ uint32_t timeout;
+
+ enum dm_event_mask mask;
+};
+
+static void _dm_event_handler_clear_dev_info(struct dm_event_handler *dmevh)
+{
+ dm_free(dmevh->dev_name);
+ dm_free(dmevh->uuid);
+ dmevh->dev_name = dmevh->uuid = NULL;
+ dmevh->major = dmevh->minor = 0;
+}
+
+struct dm_event_handler *dm_event_handler_create(void)
+{
+ struct dm_event_handler *dmevh = NULL;
+
+ if (!(dmevh = dm_malloc(sizeof(*dmevh))))
+ return NULL;
+
+ dmevh->dmeventd_path = NULL;
+ dmevh->dso = dmevh->dev_name = dmevh->uuid = NULL;
+ dmevh->major = dmevh->minor = 0;
+ dmevh->mask = 0;
+ dmevh->timeout = 0;
+
+ return dmevh;
+}
+
+void dm_event_handler_destroy(struct dm_event_handler *dmevh)
+{
+ _dm_event_handler_clear_dev_info(dmevh);
+ dm_free(dmevh->dso);
+ dm_free(dmevh->dmeventd_path);
+ dm_free(dmevh);
+}
+
+int dm_event_handler_set_dmeventd_path(struct dm_event_handler *dmevh, const char *dmeventd_path)
+{
+ if (!dmeventd_path) /* noop */
+ return 0;
+
+ dm_free(dmevh->dmeventd_path);
+
+ dmevh->dmeventd_path = dm_strdup(dmeventd_path);
+ if (!dmevh->dmeventd_path)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int dm_event_handler_set_dso(struct dm_event_handler *dmevh, const char *path)
+{
+ if (!path) /* noop */
+ return 0;
+ dm_free(dmevh->dso);
+
+ dmevh->dso = dm_strdup(path);
+ if (!dmevh->dso)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int dm_event_handler_set_dev_name(struct dm_event_handler *dmevh, const char *dev_name)
+{
+ if (!dev_name)
+ return 0;
+
+ _dm_event_handler_clear_dev_info(dmevh);
+
+ dmevh->dev_name = dm_strdup(dev_name);
+ if (!dmevh->dev_name)
+ return -ENOMEM;
+ return 0;
+}
+
+int dm_event_handler_set_uuid(struct dm_event_handler *dmevh, const char *uuid)
+{
+ if (!uuid)
+ return 0;
+
+ _dm_event_handler_clear_dev_info(dmevh);
+
+ dmevh->uuid = dm_strdup(uuid);
+ if (!dmevh->uuid)
+ return -ENOMEM;
+ return 0;
+}
+
+void dm_event_handler_set_major(struct dm_event_handler *dmevh, int major)
+{
+ int minor = dmevh->minor;
+
+ _dm_event_handler_clear_dev_info(dmevh);
+
+ dmevh->major = major;
+ dmevh->minor = minor;
+}
+
+void dm_event_handler_set_minor(struct dm_event_handler *dmevh, int minor)
+{
+ int major = dmevh->major;
+
+ _dm_event_handler_clear_dev_info(dmevh);
+
+ dmevh->major = major;
+ dmevh->minor = minor;
+}
+
+void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh,
+ enum dm_event_mask evmask)
+{
+ dmevh->mask = evmask;
+}
+
+void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout)
+{
+ dmevh->timeout = timeout;
+}
+
+const char *dm_event_handler_get_dso(const struct dm_event_handler *dmevh)
+{
+ return dmevh->dso;
+}
+
+const char *dm_event_handler_get_dev_name(const struct dm_event_handler *dmevh)
+{
+ return dmevh->dev_name;
+}
+
+const char *dm_event_handler_get_uuid(const struct dm_event_handler *dmevh)
+{
+ return dmevh->uuid;
+}
+
+int dm_event_handler_get_major(const struct dm_event_handler *dmevh)
+{
+ return dmevh->major;
+}
+
+int dm_event_handler_get_minor(const struct dm_event_handler *dmevh)
+{
+ return dmevh->minor;
+}
+
+int dm_event_handler_get_timeout(const struct dm_event_handler *dmevh)
+{
+ return dmevh->timeout;
+}
+
+enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh)
+{
+ return dmevh->mask;
+}
+
+static int _check_message_id(struct dm_event_daemon_message *msg)
+{
+ int pid, seq_nr;
+
+ if ((sscanf(msg->data, "%d:%d", &pid, &seq_nr) != 2) ||
+ (pid != getpid()) || (seq_nr != _sequence_nr)) {
+ log_error("Ignoring out-of-sequence reply from dmeventd. "
+ "Expected %d:%d but received %s", getpid(),
+ _sequence_nr, msg->data);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * daemon_read
+ * @fifos
+ * @msg
+ *
+ * Read message from daemon.
+ *
+ * Returns: 0 on failure, 1 on success
+ */
+static int _daemon_read(struct dm_event_fifos *fifos,
+ struct dm_event_daemon_message *msg)
+{
+ unsigned bytes = 0;
+ int ret, i;
+ fd_set fds;
+ struct timeval tval = { 0, 0 };
+ size_t size = 2 * sizeof(uint32_t); /* status + size */
+ uint32_t *header = alloca(size);
+ char *buf = (char *)header;
+
+ while (bytes < size) {
+ for (i = 0, ret = 0; (i < 20) && (ret < 1); i++) {
+ /* Watch daemon read FIFO for input. */
+ FD_ZERO(&fds);
+ FD_SET(fifos->server, &fds);
+ tval.tv_sec = 1;
+ ret = select(fifos->server + 1, &fds, NULL, NULL,
+ &tval);
+ if (ret < 0 && errno != EINTR) {
+ log_error("Unable to read from event server");
+ return 0;
+ }
+ }
+ if (ret < 1) {
+ log_error("Unable to read from event server.");
+ return 0;
+ }
+
+ ret = read(fifos->server, buf + bytes, size);
+ if (ret < 0) {
+ if ((errno == EINTR) || (errno == EAGAIN))
+ continue;
+ else {
+ log_error("Unable to read from event server.");
+ return 0;
+ }
+ }
+
+ bytes += ret;
+ if (header && (bytes == 2 * sizeof(uint32_t))) {
+ msg->cmd = ntohl(header[0]);
+ msg->size = ntohl(header[1]);
+ buf = msg->data = dm_malloc(msg->size);
+ size = msg->size;
+ bytes = 0;
+ header = 0;
+ }
+ }
+
+ if (bytes != size) {
+ dm_free(msg->data);
+ msg->data = NULL;
+ }
+ return bytes == size;
+}
+
+/* Write message to daemon. */
+static int _daemon_write(struct dm_event_fifos *fifos,
+ struct dm_event_daemon_message *msg)
+{
+ unsigned bytes = 0;
+ int ret = 0;
+ fd_set fds;
+
+ size_t size = 2 * sizeof(uint32_t) + msg->size;
+ uint32_t *header = alloca(size);
+ char *buf = (char *)header;
+ char drainbuf[128];
+ struct timeval tval = { 0, 0 };
+
+ header[0] = htonl(msg->cmd);
+ header[1] = htonl(msg->size);
+ memcpy(buf + 2 * sizeof(uint32_t), msg->data, msg->size);
+
+ /* drain the answer fifo */
+ while (1) {
+ FD_ZERO(&fds);
+ FD_SET(fifos->server, &fds);
+ tval.tv_usec = 100;
+ ret = select(fifos->server + 1, &fds, NULL, NULL, &tval);
+ if ((ret < 0) && (errno != EINTR)) {
+ log_error("Unable to talk to event daemon");
+ return 0;
+ }
+ if (ret == 0)
+ break;
+ read(fifos->server, drainbuf, 127);
+ }
+
+ while (bytes < size) {
+ do {
+ /* Watch daemon write FIFO to be ready for output. */
+ FD_ZERO(&fds);
+ FD_SET(fifos->client, &fds);
+ ret = select(fifos->client + 1, NULL, &fds, NULL, NULL);
+ if ((ret < 0) && (errno != EINTR)) {
+ log_error("Unable to talk to event daemon");
+ return 0;
+ }
+ } while (ret < 1);
+
+ ret = write(fifos->client, buf + bytes, size - bytes);
+ if (ret < 0) {
+ if ((errno == EINTR) || (errno == EAGAIN))
+ continue;
+ else {
+ log_error("Unable to talk to event daemon");
+ return 0;
+ }
+ }
+
+ bytes += ret;
+ }
+
+ return bytes == size;
+}
+
+int daemon_talk(struct dm_event_fifos *fifos,
+ struct dm_event_daemon_message *msg, int cmd,
+ const char *dso_name, const char *dev_name,
+ enum dm_event_mask evmask, uint32_t timeout)
+{
+ const char *dso = dso_name ? dso_name : "-";
+ const char *dev = dev_name ? dev_name : "-";
+ const char *fmt = "%d:%d %s %s %u %" PRIu32;
+ int msg_size;
+ memset(msg, 0, sizeof(*msg));
+
+ /*
+ * Set command and pack the arguments
+ * into ASCII message string.
+ */
+ msg->cmd = cmd;
+ if (cmd == DM_EVENT_CMD_HELLO)
+ fmt = "%d:%d HELLO";
+ if ((msg_size = dm_asprintf(&(msg->data), fmt, getpid(), _sequence_nr,
+ dso, dev, evmask, timeout)) < 0) {
+ log_error("_daemon_talk: message allocation failed");
+ return -ENOMEM;
+ }
+ msg->size = msg_size;
+
+ /*
+ * Write command and message to and
+ * read status return code from daemon.
+ */
+ if (!_daemon_write(fifos, msg)) {
+ stack;
+ dm_free(msg->data);
+ msg->data = 0;
+ return -EIO;
+ }
+
+ do {
+
+ dm_free(msg->data);
+ msg->data = 0;
+
+ if (!_daemon_read(fifos, msg)) {
+ stack;
+ return -EIO;
+ }
+ } while (!_check_message_id(msg));
+
+ _sequence_nr++;
+
+ return (int32_t) msg->cmd;
+}
+
+/*
+ * start_daemon
+ *
+ * This function forks off a process (dmeventd) that will handle
+ * the events. I am currently test opening one of the fifos to
+ * ensure that the daemon is running and listening... I thought
+ * this would be less expensive than fork/exec'ing every time.
+ * Perhaps there is an even quicker/better way (no, checking the
+ * lock file is _not_ a better way).
+ *
+ * Returns: 1 on success, 0 otherwise
+ */
+static int _start_daemon(char *dmeventd_path, struct dm_event_fifos *fifos)
+{
+ int pid, ret = 0;
+ int status;
+ struct stat statbuf;
+ char default_dmeventd_path[] = DMEVENTD_PATH;
+ char *args[] = { dmeventd_path ? : default_dmeventd_path, NULL };
+
+ if (stat(fifos->client_path, &statbuf))
+ goto start_server;
+
+ if (!S_ISFIFO(statbuf.st_mode)) {
+ log_error("%s is not a fifo.", fifos->client_path);
+ return 0;
+ }
+
+ /* Anyone listening? If not, errno will be ENXIO */
+ fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK);
+ if (fifos->client >= 0) {
+ /* server is running and listening */
+
+ close(fifos->client);
+ return 1;
+ } else if (errno != ENXIO) {
+ /* problem */
+
+ log_error("%s: Can't open client fifo %s: %s",
+ __func__, fifos->client_path, strerror(errno));
+ stack;
+ return 0;
+ }
+
+ start_server:
+ /* server is not running */
+
+ if (!strncmp(DMEVENTD_PATH, "/", 1) && stat(DMEVENTD_PATH, &statbuf)) {
+ log_error("Unable to find dmeventd.");
+ return_0;
+ }
+
+ pid = fork();
+
+ if (pid < 0)
+ log_error("Unable to fork.");
+
+ else if (!pid) {
+ execvp(args[0], args);
+ log_error("Unable to exec dmeventd: %s", strerror(errno));
+ _exit(EXIT_FAILURE);
+ } else {
+ if (waitpid(pid, &status, 0) < 0)
+ log_error("Unable to start dmeventd: %s",
+ strerror(errno));
+ else if (WEXITSTATUS(status))
+ log_error("Unable to start dmeventd.");
+ else
+ ret = 1;
+ }
+
+ return ret;
+}
+
+int init_fifos(struct dm_event_fifos *fifos)
+{
+ /* FIXME? Is fifo the most suitable method? Why not share
+ comms/daemon code with something else e.g. multipath? */
+
+ /* FIXME Make these either configurable or depend directly on dmeventd_path */
+ fifos->client_path = DM_EVENT_FIFO_CLIENT;
+ fifos->server_path = DM_EVENT_FIFO_SERVER;
+
+ /* Open the fifo used to read from the daemon. */
+ if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) {
+ log_error("%s: open server fifo %s",
+ __func__, fifos->server_path);
+ stack;
+ return 0;
+ }
+
+ /* Lock out anyone else trying to do communication with the daemon. */
+ if (flock(fifos->server, LOCK_EX) < 0) {
+ log_error("%s: flock %s", __func__, fifos->server_path);
+ close(fifos->server);
+ return 0;
+ }
+
+/* if ((fifos->client = open(fifos->client_path, O_WRONLY | O_NONBLOCK)) < 0) {*/
+ if ((fifos->client = open(fifos->client_path, O_RDWR | O_NONBLOCK)) < 0) {
+ log_error("%s: Can't open client fifo %s: %s",
+ __func__, fifos->client_path, strerror(errno));
+ close(fifos->server);
+ stack;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Initialize client. */
+static int _init_client(char *dmeventd_path, struct dm_event_fifos *fifos)
+{
+ /* init fifos */
+ memset(fifos, 0, sizeof(*fifos));
+
+ /* FIXME Make these either configurable or depend directly on dmeventd_path */
+ fifos->client_path = DM_EVENT_FIFO_CLIENT;
+ fifos->server_path = DM_EVENT_FIFO_SERVER;
+
+ if (!_start_daemon(dmeventd_path, fifos))
+ return_0;
+
+ return init_fifos(fifos);
+}
+
+void fini_fifos(struct dm_event_fifos *fifos)
+{
+ if (flock(fifos->server, LOCK_UN))
+ log_error("flock unlock %s", fifos->server_path);
+
+ close(fifos->client);
+ close(fifos->server);
+}
+
+/* Get uuid of a device */
+static struct dm_task *_get_device_info(const struct dm_event_handler *dmevh)
+{
+ struct dm_task *dmt;
+ struct dm_info info;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
+ log_error("_get_device_info: dm_task creation for info failed");
+ return NULL;
+ }
+
+ if (dmevh->uuid)
+ dm_task_set_uuid(dmt, dmevh->uuid);
+ else if (dmevh->dev_name)
+ dm_task_set_name(dmt, dmevh->dev_name);
+ else if (dmevh->major && dmevh->minor) {
+ dm_task_set_major(dmt, dmevh->major);
+ dm_task_set_minor(dmt, dmevh->minor);
+ }
+
+ /* FIXME Add name or uuid or devno to messages */
+ if (!dm_task_run(dmt)) {
+ log_error("_get_device_info: dm_task_run() failed");
+ goto failed;
+ }
+
+ if (!dm_task_get_info(dmt, &info)) {
+ log_error("_get_device_info: failed to get info for device");
+ goto failed;
+ }
+
+ if (!info.exists) {
+ log_error("_get_device_info: device not found");
+ goto failed;
+ }
+
+ return dmt;
+
+failed:
+ dm_task_destroy(dmt);
+ return NULL;
+}
+
+/* Handle the event (de)registration call and return negative error codes. */
+static int _do_event(int cmd, char *dmeventd_path, struct dm_event_daemon_message *msg,
+ const char *dso_name, const char *dev_name,
+ enum dm_event_mask evmask, uint32_t timeout)
+{
+ int ret;
+ struct dm_event_fifos fifos;
+
+ if (!_init_client(dmeventd_path, &fifos)) {
+ stack;
+ return -ESRCH;
+ }
+
+ ret = daemon_talk(&fifos, msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0);
+
+ dm_free(msg->data);
+ msg->data = 0;
+
+ if (!ret)
+ ret = daemon_talk(&fifos, msg, cmd, dso_name, dev_name, evmask, timeout);
+
+ /* what is the opposite of init? */
+ fini_fifos(&fifos);
+
+ return ret;
+}
+
+/* External library interface. */
+int dm_event_register_handler(const struct dm_event_handler *dmevh)
+{
+ int ret = 1, err;
+ const char *uuid;
+ struct dm_task *dmt;
+ struct dm_event_daemon_message msg = { 0, 0, NULL };
+
+ if (!(dmt = _get_device_info(dmevh))) {
+ stack;
+ return 0;
+ }
+
+ uuid = dm_task_get_uuid(dmt);
+
+ if ((err = _do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg,
+ dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
+ log_error("%s: event registration failed: %s",
+ dm_task_get_name(dmt),
+ msg.data ? msg.data : strerror(-err));
+ ret = 0;
+ }
+
+ dm_free(msg.data);
+
+ dm_task_destroy(dmt);
+
+ return ret;
+}
+
+int dm_event_unregister_handler(const struct dm_event_handler *dmevh)
+{
+ int ret = 1, err;
+ const char *uuid;
+ struct dm_task *dmt;
+ struct dm_event_daemon_message msg = { 0, 0, NULL };
+
+ if (!(dmt = _get_device_info(dmevh))) {
+ stack;
+ return 0;
+ }
+
+ uuid = dm_task_get_uuid(dmt);
+
+ if ((err = _do_event(DM_EVENT_CMD_UNREGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg,
+ dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) {
+ log_error("%s: event deregistration failed: %s",
+ dm_task_get_name(dmt),
+ msg.data ? msg.data : strerror(-err));
+ ret = 0;
+ }
+
+ dm_free(msg.data);
+
+ dm_task_destroy(dmt);
+
+ return ret;
+}
+
+/* Fetch a string off src and duplicate it into *dest. */
+/* FIXME: move to separate module to share with the daemon. */
+static char *_fetch_string(char **src, const int delimiter)
+{
+ char *p, *ret;
+
+ if ((p = strchr(*src, delimiter)))
+ *p = 0;
+
+ if ((ret = dm_strdup(*src)))
+ *src += strlen(ret) + 1;
+
+ if (p)
+ *p = delimiter;
+
+ return ret;
+}
+
+/* Parse a device message from the daemon. */
+static int _parse_message(struct dm_event_daemon_message *msg, char **dso_name,
+ char **uuid, enum dm_event_mask *evmask)
+{
+ char *id = NULL;
+ char *p = msg->data;
+
+ if ((id = _fetch_string(&p, ' ')) &&
+ (*dso_name = _fetch_string(&p, ' ')) &&
+ (*uuid = _fetch_string(&p, ' '))) {
+ *evmask = atoi(p);
+
+ dm_free(id);
+ return 0;
+ }
+
+ if (id)
+ dm_free(id);
+ return -ENOMEM;
+}
+
+/*
+ * Returns 0 if handler found; error (-ENOMEM, -ENOENT) otherwise.
+ */
+int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
+{
+ int ret = 0;
+ const char *uuid = NULL;
+ char *reply_dso = NULL, *reply_uuid = NULL;
+ enum dm_event_mask reply_mask = 0;
+ struct dm_task *dmt = NULL;
+ struct dm_event_daemon_message msg = { 0, 0, NULL };
+ struct dm_info info;
+
+ if (!(dmt = _get_device_info(dmevh))) {
+ stack;
+ return 0;
+ }
+
+ uuid = dm_task_get_uuid(dmt);
+
+ if (!(ret = _do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
+ DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path,
+ &msg, dmevh->dso, uuid, dmevh->mask, 0))) {
+ /* FIXME this will probably horribly break if we get
+ ill-formatted reply */
+ ret = _parse_message(&msg, &reply_dso, &reply_uuid, &reply_mask);
+ } else {
+ ret = -ENOENT;
+ goto fail;
+ }
+
+ dm_task_destroy(dmt);
+ dmt = NULL;
+
+ dm_free(msg.data);
+ msg.data = NULL;
+
+ _dm_event_handler_clear_dev_info(dmevh);
+ dmevh->uuid = dm_strdup(reply_uuid);
+ if (!dmevh->uuid) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ if (!(dmt = _get_device_info(dmevh))) {
+ ret = -ENXIO; /* dmeventd probably gave us bogus uuid back */
+ goto fail;
+ }
+
+ dm_event_handler_set_dso(dmevh, reply_dso);
+ dm_event_handler_set_event_mask(dmevh, reply_mask);
+
+ dm_free(reply_dso);
+ reply_dso = NULL;
+
+ dm_free(reply_uuid);
+ reply_uuid = NULL;
+
+ dmevh->dev_name = dm_strdup(dm_task_get_name(dmt));
+ if (!dmevh->dev_name) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ if (!dm_task_get_info(dmt, &info)) {
+ ret = -1;
+ goto fail;
+ }
+
+ dmevh->major = info.major;
+ dmevh->minor = info.minor;
+
+ dm_task_destroy(dmt);
+
+ return ret;
+
+ fail:
+ dm_free(msg.data);
+ dm_free(reply_dso);
+ dm_free(reply_uuid);
+ _dm_event_handler_clear_dev_info(dmevh);
+ if (dmt)
+ dm_task_destroy(dmt);
+ return ret;
+}
+
+#if 0 /* left out for now */
+
+static char *_skip_string(char *src, const int delimiter)
+{
+ src = srtchr(src, delimiter);
+ if (src && *(src + 1))
+ return src + 1;
+ return NULL;
+}
+
+int dm_event_set_timeout(const char *device_path, uint32_t timeout)
+{
+ struct dm_event_daemon_message msg = { 0, 0, NULL };
+
+ if (!device_exists(device_path))
+ return -ENODEV;
+
+ return _do_event(DM_EVENT_CMD_SET_TIMEOUT, &msg,
+ NULL, device_path, 0, timeout);
+}
+
+int dm_event_get_timeout(const char *device_path, uint32_t *timeout)
+{
+ int ret;
+ struct dm_event_daemon_message msg = { 0, 0, NULL };
+
+ if (!device_exists(device_path))
+ return -ENODEV;
+ if (!(ret = _do_event(DM_EVENT_CMD_GET_TIMEOUT, &msg, NULL, device_path,
+ 0, 0))) {
+ char *p = _skip_string(msg.data, ' ');
+ if (!p) {
+ log_error("malformed reply from dmeventd '%s'\n",
+ msg.data);
+ return -EIO;
+ }
+ *timeout = atoi(p);
+ }
+ if (msg.data)
+ dm_free(msg.data);
+ return ret;
+}
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2005-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Note that this file is released only as part of a technology preview
+ * and its contents may change in future updates in ways that do not
+ * preserve compatibility.
+ */
+
+#ifndef LIB_DMEVENT_H
+#define LIB_DMEVENT_H
+
+#include <stdint.h>
+
+/*
+ * Event library interface.
+ */
+
+enum dm_event_mask {
+ DM_EVENT_SETTINGS_MASK = 0x0000FF,
+ DM_EVENT_SINGLE = 0x000001, /* Report multiple errors just once. */
+ DM_EVENT_MULTI = 0x000002, /* Report all of them. */
+
+ DM_EVENT_ERROR_MASK = 0x00FF00,
+ DM_EVENT_SECTOR_ERROR = 0x000100, /* Failure on a particular sector. */
+ DM_EVENT_DEVICE_ERROR = 0x000200, /* Device failure. */
+ DM_EVENT_PATH_ERROR = 0x000400, /* Failure on an io path. */
+ DM_EVENT_ADAPTOR_ERROR = 0x000800, /* Failure of a host adaptor. */
+
+ DM_EVENT_STATUS_MASK = 0xFF0000,
+ DM_EVENT_SYNC_STATUS = 0x010000, /* Mirror synchronization completed/failed. */
+ DM_EVENT_TIMEOUT = 0x020000, /* Timeout has occured */
+
+ DM_EVENT_REGISTRATION_PENDING = 0x1000000, /* Monitor thread is setting-up/shutting-down */
+};
+
+#define DM_EVENT_ALL_ERRORS DM_EVENT_ERROR_MASK
+
+struct dm_event_handler;
+
+struct dm_event_handler *dm_event_handler_create(void);
+void dm_event_handler_destroy(struct dm_event_handler *dmevh);
+
+/*
+ * Path of shared library to handle events.
+ *
+ * All of dmeventd, dso, device_name and uuid strings are duplicated so
+ * you do not need to keep the pointers valid after the call succeeds.
+ * They may return -ENOMEM though.
+ */
+int dm_event_handler_set_dso(struct dm_event_handler *dmevh, const char *path);
+
+/*
+ * Path of dmeventd binary.
+ */
+int dm_event_handler_set_dmeventd_path(struct dm_event_handler *dmevh, const char *dmeventd_path);
+
+/*
+ * Identify the device to monitor by exactly one of device_name, uuid or
+ * device number. String arguments are duplicated, see above.
+ */
+int dm_event_handler_set_dev_name(struct dm_event_handler *dmevh, const char *device_name);
+
+int dm_event_handler_set_uuid(struct dm_event_handler *dmevh, const char *uuid);
+
+void dm_event_handler_set_major(struct dm_event_handler *dmevh, int major);
+void dm_event_handler_set_minor(struct dm_event_handler *dmevh, int minor);
+void dm_event_handler_set_timeout(struct dm_event_handler *dmevh, int timeout);
+
+/*
+ * Specify mask for events to monitor.
+ */
+void dm_event_handler_set_event_mask(struct dm_event_handler *dmevh,
+ enum dm_event_mask evmask);
+
+const char *dm_event_handler_get_dso(const struct dm_event_handler *dmevh);
+const char *dm_event_handler_get_dev_name(const struct dm_event_handler *dmevh);
+const char *dm_event_handler_get_uuid(const struct dm_event_handler *dmevh);
+int dm_event_handler_get_major(const struct dm_event_handler *dmevh);
+int dm_event_handler_get_minor(const struct dm_event_handler *dmevh);
+int dm_event_handler_get_timeout(const struct dm_event_handler *dmevh);
+enum dm_event_mask dm_event_handler_get_event_mask(const struct dm_event_handler *dmevh);
+
+/* FIXME Review interface (what about this next thing?) */
+int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next);
+
+/*
+ * Initiate monitoring using dmeventd.
+ */
+int dm_event_register_handler(const struct dm_event_handler *dmevh);
+int dm_event_unregister_handler(const struct dm_event_handler *dmevh);
+
+/* Prototypes for DSO interface, see dmeventd.c, struct dso_data for
+ detailed descriptions. */
+void process_event(struct dm_task *dmt, enum dm_event_mask evmask, void **user);
+int register_device(const char *device_name, const char *uuid, int major, int minor, void **user);
+int unregister_device(const char *device_name, const char *uuid, int major,
+ int minor, void **user);
+
+#endif
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: devmapper-event
+Description: device-mapper event library
+Version: @DM_LIB_PATCHLEVEL@
+Cflags: -I${includedir}
+Libs: -L${libdir} -ldevmapper-event
+Requires.private: devmapper
--- /dev/null
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SUBDIRS += lvm2 mirror snapshot
+
+include $(top_builddir)/make.tmpl
+
+mirror: lvm2
+snapshot: lvm2
+
--- /dev/null
+dmeventd_lvm2_init
+dmeventd_lvm2_exit
+dmeventd_lvm2_lock
+dmeventd_lvm2_unlock
+dmeventd_lvm2_pool
+dmeventd_lvm2_run
--- /dev/null
+#
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+CLDFLAGS += -L$(top_builddir)/tools
+
+SOURCES = dmeventd_lvm.c
+
+LIB_SHARED = libdevmapper-event-lvm2.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+LIBS += @LVM2CMD_LIB@ -ldevmapper $(PTHREAD_LIBS)
+
+install_lvm2: install_lib_shared
+
+install: install_lvm2
+
+DISTCLEAN_TARGETS += .exported_symbols_generated
--- /dev/null
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "log.h"
+
+#include "lvm2cmd.h"
+#include "errors.h"
+#include "libdevmapper-event.h"
+#include "dmeventd_lvm.h"
+
+#include <pthread.h>
+#include <syslog.h>
+
+extern int dmeventd_debug;
+
+/*
+ * register_device() is called first and performs initialisation.
+ * Only one device may be registered or unregistered at a time.
+ */
+static pthread_mutex_t _register_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * Number of active registrations.
+ */
+static int _register_count = 0;
+static struct dm_pool *_mem_pool = NULL;
+static void *_lvm_handle = NULL;
+
+/*
+ * Currently only one event can be processed at a time.
+ */
+static pthread_mutex_t _event_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * FIXME Do not pass things directly to syslog, rather use the existing logging
+ * facilities to sort logging ... however that mechanism needs to be somehow
+ * configurable and we don't have that option yet
+ */
+static void _temporary_log_fn(int level,
+ const char *file __attribute__((unused)),
+ int line __attribute__((unused)),
+ int dm_errno __attribute__((unused)),
+ const char *message)
+{
+ level &= ~(_LOG_STDERR | _LOG_ONCE);
+
+ switch (level) {
+ case _LOG_DEBUG:
+ if (dmeventd_debug >= 3)
+ syslog(LOG_DEBUG, "%s", message);
+ break;
+ case _LOG_INFO:
+ if (dmeventd_debug >= 2)
+ syslog(LOG_INFO, "%s", message);
+ break;
+ case _LOG_NOTICE:
+ if (dmeventd_debug >= 1)
+ syslog(LOG_NOTICE, "%s", message);
+ break;
+ case _LOG_WARN:
+ syslog(LOG_WARNING, "%s", message);
+ break;
+ case _LOG_ERR:
+ syslog(LOG_ERR, "%s", message);
+ break;
+ default:
+ syslog(LOG_CRIT, "%s", message);
+ }
+}
+
+void dmeventd_lvm2_lock(void)
+{
+ if (pthread_mutex_trylock(&_event_mutex)) {
+ syslog(LOG_NOTICE, "Another thread is handling an event. Waiting...");
+ pthread_mutex_lock(&_event_mutex);
+ }
+}
+
+void dmeventd_lvm2_unlock(void)
+{
+ pthread_mutex_unlock(&_event_mutex);
+}
+
+int dmeventd_lvm2_init(void)
+{
+ int r = 0;
+
+ pthread_mutex_lock(&_register_mutex);
+
+ /*
+ * Need some space for allocations. 1024 should be more
+ * than enough for what we need (device mapper name splitting)
+ */
+ if (!_mem_pool && !(_mem_pool = dm_pool_create("mirror_dso", 1024)))
+ goto out;
+
+ if (!_lvm_handle) {
+ lvm2_log_fn(_temporary_log_fn);
+ if (!(_lvm_handle = lvm2_init())) {
+ dm_pool_destroy(_mem_pool);
+ _mem_pool = NULL;
+ goto out;
+ }
+ /* FIXME Temporary: move to dmeventd core */
+ lvm2_run(_lvm_handle, "_memlock_inc");
+ }
+
+ _register_count++;
+ r = 1;
+
+out:
+ pthread_mutex_unlock(&_register_mutex);
+ return r;
+}
+
+void dmeventd_lvm2_exit(void)
+{
+ pthread_mutex_lock(&_register_mutex);
+
+ if (!--_register_count) {
+ lvm2_run(_lvm_handle, "_memlock_dec");
+ dm_pool_destroy(_mem_pool);
+ _mem_pool = NULL;
+ lvm2_exit(_lvm_handle);
+ _lvm_handle = NULL;
+ }
+
+ pthread_mutex_unlock(&_register_mutex);
+}
+
+struct dm_pool *dmeventd_lvm2_pool(void)
+{
+ return _mem_pool;
+}
+
+int dmeventd_lvm2_run(const char *cmdline)
+{
+ return lvm2_run(_lvm_handle, cmdline);
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Wrappers around liblvm2cmd functions for dmeventd plug-ins.
+ *
+ * liblvm2cmd is not thread-safe so the locking in this library helps dmeventd
+ * threads to co-operate in sharing a single instance.
+ *
+ * FIXME Either support this properly as a generic liblvm2cmd wrapper or make
+ * liblvm2cmd thread-safe so this can go away.
+ */
+
+#include "libdevmapper.h"
+
+#ifndef _DMEVENTD_LVMWRAP_H
+#define _DMEVENTD_LVMWRAP_H
+
+int dmeventd_lvm2_init(void);
+void dmeventd_lvm2_exit(void);
+int dmeventd_lvm2_run(const char *cmdline);
+
+void dmeventd_lvm2_lock(void);
+void dmeventd_lvm2_unlock(void);
+
+struct dm_pool *dmeventd_lvm2_pool(void);
+
+#endif /* _DMEVENTD_LVMWRAP_H */
--- /dev/null
+process_event
+register_device
+unregister_device
--- /dev/null
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2005, 2008-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+INCLUDES += -I$(top_srcdir)/tools -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
+CLDFLAGS += -L$(top_builddir)/tools -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
+
+SOURCES = dmeventd_mirror.c
+
+LIB_NAME = libdevmapper-event-lvm2mirror
+LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+CFLOW_LIST = $(SOURCES)
+CFLOW_LIST_TARGET = $(LIB_NAME).cflow
+
+include $(top_builddir)/make.tmpl
+
+LIBS += -ldevmapper-event-lvm2 -ldevmapper
+
+install_lvm2: install_dm_plugin
+
+install: install_lvm2
+
+DISTCLEAN_TARGETS += .exported_symbols_generated
--- /dev/null
+/*
+ * Copyright (C) 2005-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+
+#include "lvm2cmd.h"
+#include "errors.h"
+#include "libdevmapper-event.h"
+#include "dmeventd_lvm.h"
+
+#include <syslog.h> /* FIXME Replace syslog with multilog */
+/* FIXME Missing openlog? */
+/* FIXME Replace most syslogs with log_error() style messages and add complete context. */
+/* FIXME Reformat to 80 char lines. */
+
+#define ME_IGNORE 0
+#define ME_INSYNC 1
+#define ME_FAILURE 2
+
+static int _process_status_code(const char status_code, const char *dev_name,
+ const char *dev_type, int r)
+{
+ /*
+ * A => Alive - No failures
+ * D => Dead - A write failure occurred leaving mirror out-of-sync
+ * F => Flush failed.
+ * S => Sync - A sychronization failure occurred, mirror out-of-sync
+ * R => Read - A read failure occurred, mirror data unaffected
+ * U => Unclassified failure (bug)
+ */
+ if (status_code == 'F') {
+ syslog(LOG_ERR, "%s device %s flush failed.",
+ dev_type, dev_name);
+ r = ME_FAILURE;
+ } else if (status_code == 'S')
+ syslog(LOG_ERR, "%s device %s sync failed.",
+ dev_type, dev_name);
+ else if (status_code == 'R')
+ syslog(LOG_ERR, "%s device %s read failed.",
+ dev_type, dev_name);
+ else if (status_code != 'A') {
+ syslog(LOG_ERR, "%s device %s has failed (%c).",
+ dev_type, dev_name, status_code);
+ r = ME_FAILURE;
+ }
+
+ return r;
+}
+
+static int _get_mirror_event(char *params)
+{
+ int i, r = ME_INSYNC;
+ char **args = NULL;
+ char *dev_status_str;
+ char *log_status_str;
+ char *sync_str;
+ char *p = NULL;
+ int log_argc, num_devs;
+
+ /*
+ * dm core parms: 0 409600 mirror
+ * Mirror core parms: 2 253:4 253:5 400/400
+ * New-style failure params: 1 AA
+ * New-style log params: 3 cluster 253:3 A
+ * or 3 disk 253:3 A
+ * or 1 core
+ */
+
+ /* number of devices */
+ if (!dm_split_words(params, 1, 0, &p))
+ goto out_parse;
+
+ if (!(num_devs = atoi(p)))
+ goto out_parse;
+ p += strlen(p) + 1;
+
+ /* devices names + "400/400" + "1 AA" + 1 or 3 log parms + NULL */
+ args = dm_malloc((num_devs + 7) * sizeof(char *));
+ if (!args || dm_split_words(p, num_devs + 7, 0, args) < num_devs + 5)
+ goto out_parse;
+
+ dev_status_str = args[2 + num_devs];
+ log_argc = atoi(args[3 + num_devs]);
+ log_status_str = args[3 + num_devs + log_argc];
+ sync_str = args[num_devs];
+
+ /* Check for bad mirror devices */
+ for (i = 0; i < num_devs; i++)
+ r = _process_status_code(dev_status_str[i], args[i],
+ i ? "Secondary mirror" : "Primary mirror", r);
+
+ /* Check for bad disk log device */
+ if (log_argc > 1)
+ r = _process_status_code(log_status_str[0],
+ args[2 + num_devs + log_argc],
+ "Log", r);
+
+ if (r == ME_FAILURE)
+ goto out;
+
+ p = strstr(sync_str, "/");
+ if (p) {
+ p[0] = '\0';
+ if (strcmp(sync_str, p+1))
+ r = ME_IGNORE;
+ p[0] = '/';
+ } else
+ goto out_parse;
+
+out:
+ dm_free(args);
+ return r;
+
+out_parse:
+ dm_free(args);
+ syslog(LOG_ERR, "Unable to parse mirror status string.");
+ return ME_IGNORE;
+}
+
+static int _remove_failed_devices(const char *device)
+{
+ int r;
+#define CMD_SIZE 256 /* FIXME Use system restriction */
+ char cmd_str[CMD_SIZE];
+ char *vg = NULL, *lv = NULL, *layer = NULL;
+
+ if (strlen(device) > 200) /* FIXME Use real restriction */
+ return -ENAMETOOLONG; /* FIXME These return code distinctions are not used so remove them! */
+
+ if (!dm_split_lvm_name(dmeventd_lvm2_pool(), device, &vg, &lv, &layer)) {
+ syslog(LOG_ERR, "Unable to determine VG name from %s.",
+ device);
+ return -ENOMEM; /* FIXME Replace with generic error return - reason for failure has already got logged */
+ }
+
+ /* strip off the mirror component designations */
+ layer = strstr(lv, "_mlog");
+ if (layer)
+ *layer = '\0';
+
+ /* FIXME Is any sanity-checking required on %s? */
+ if (CMD_SIZE <= snprintf(cmd_str, CMD_SIZE, "lvconvert --config devices{ignore_suspended_devices=1} --repair --use-policies %s/%s", vg, lv)) {
+ /* this error should be caught above, but doesn't hurt to check again */
+ syslog(LOG_ERR, "Unable to form LVM command: Device name too long.");
+ return -ENAMETOOLONG; /* FIXME Replace with generic error return - reason for failure has already got logged */
+ }
+
+ r = dmeventd_lvm2_run(cmd_str);
+
+ syslog(LOG_INFO, "Repair of mirrored LV %s/%s %s.", vg, lv,
+ (r == ECMD_PROCESSED) ? "finished successfully" : "failed");
+
+ return (r == ECMD_PROCESSED) ? 0 : -1;
+}
+
+void process_event(struct dm_task *dmt,
+ enum dm_event_mask event __attribute__((unused)),
+ void **unused __attribute__((unused)))
+{
+ void *next = NULL;
+ uint64_t start, length;
+ char *target_type = NULL;
+ char *params;
+ const char *device = dm_task_get_name(dmt);
+
+ dmeventd_lvm2_lock();
+
+ do {
+ next = dm_get_next_target(dmt, next, &start, &length,
+ &target_type, ¶ms);
+
+ if (!target_type) {
+ syslog(LOG_INFO, "%s mapping lost.", device);
+ continue;
+ }
+
+ if (strcmp(target_type, "mirror")) {
+ syslog(LOG_INFO, "%s has unmirrored portion.", device);
+ continue;
+ }
+
+ switch(_get_mirror_event(params)) {
+ case ME_INSYNC:
+ /* FIXME: all we really know is that this
+ _part_ of the device is in sync
+ Also, this is not an error
+ */
+ syslog(LOG_NOTICE, "%s is now in-sync.", device);
+ break;
+ case ME_FAILURE:
+ syslog(LOG_ERR, "Device failure in %s.", device);
+ if (_remove_failed_devices(device))
+ /* FIXME Why are all the error return codes unused? Get rid of them? */
+ syslog(LOG_ERR, "Failed to remove faulty devices in %s.",
+ device);
+ /* Should check before warning user that device is now linear
+ else
+ syslog(LOG_NOTICE, "%s is now a linear device.\n",
+ device);
+ */
+ break;
+ case ME_IGNORE:
+ break;
+ default:
+ /* FIXME Provide value then! */
+ syslog(LOG_INFO, "Unknown event received.");
+ }
+ } while (next);
+
+ dmeventd_lvm2_unlock();
+}
+
+int register_device(const char *device,
+ const char *uuid __attribute__((unused)),
+ int major __attribute__((unused)),
+ int minor __attribute__((unused)),
+ void **unused __attribute__((unused)))
+{
+ int r = dmeventd_lvm2_init();
+ syslog(LOG_INFO, "Monitoring mirror device %s for events.", device);
+ return r;
+}
+
+int unregister_device(const char *device,
+ const char *uuid __attribute__((unused)),
+ int major __attribute__((unused)),
+ int minor __attribute__((unused)),
+ void **unused __attribute__((unused)))
+{
+ syslog(LOG_INFO, "No longer monitoring mirror device %s for events.",
+ device);
+ dmeventd_lvm2_exit();
+ return 1;
+}
--- /dev/null
+process_event
+register_device
+unregister_device
--- /dev/null
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+INCLUDES += -I$(top_srcdir)/tools -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2
+CLDFLAGS += -L$(top_builddir)/tools -L$(top_builddir)/daemons/dmeventd/plugins/lvm2
+
+SOURCES = dmeventd_snapshot.c
+
+LIB_SHARED = libdevmapper-event-lvm2snapshot.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+LIBS += -ldevmapper-event-lvm2 -ldevmapper
+
+install_lvm2: install_dm_plugin
+
+install: install_lvm2
+
+DISTCLEAN_TARGETS += .exported_symbols_generated
--- /dev/null
+/*
+ * Copyright (C) 2007-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+
+#include "lvm2cmd.h"
+#include "errors.h"
+#include "libdevmapper-event.h"
+#include "dmeventd_lvm.h"
+
+#include "lvm-string.h"
+
+#include <sys/wait.h>
+#include <syslog.h> /* FIXME Replace syslog with multilog */
+/* FIXME Missing openlog? */
+
+/* First warning when snapshot is 80% full. */
+#define WARNING_THRESH 80
+/* Run a check every 5%. */
+#define CHECK_STEP 5
+/* Do not bother checking snapshots less than 50% full. */
+#define CHECK_MINIMUM 50
+
+#define UMOUNT_COMMAND "/bin/umount"
+
+struct snap_status {
+ int invalid;
+ int used;
+ int max;
+};
+
+/* FIXME possibly reconcile this with target_percent when we gain
+ access to regular LVM library here. */
+static void _parse_snapshot_params(char *params, struct snap_status *status)
+{
+ char *p;
+ /*
+ * xx/xx -- fractions used/max
+ * Invalid -- snapshot invalidated
+ * Unknown -- status unknown
+ */
+ status->used = status->max = 0;
+
+ if (!strncmp(params, "Invalid", 7)) {
+ status->invalid = 1;
+ return;
+ }
+
+ /*
+ * When we return without setting non-zero max, the parent is
+ * responsible for reporting errors.
+ */
+ if (!strncmp(params, "Unknown", 7))
+ return;
+
+ if (!(p = strstr(params, "/")))
+ return;
+
+ *p = '\0';
+ p++;
+
+ status->used = atoi(params);
+ status->max = atoi(p);
+}
+
+static int _run(const char *cmd, ...)
+{
+ va_list ap;
+ int argc = 1; /* for argv[0], i.e. cmd */
+ int i = 0;
+ const char **argv;
+ pid_t pid = fork();
+ int status;
+
+ if (pid == 0) { /* child */
+ va_start(ap, cmd);
+ while (va_arg(ap, const char *))
+ ++ argc;
+ va_end(ap);
+
+ /* + 1 for the terminating NULL */
+ argv = alloca(sizeof(const char *) * (argc + 1));
+
+ argv[0] = cmd;
+ va_start(ap, cmd);
+ while ((argv[++i] = va_arg(ap, const char *)));
+ va_end(ap);
+
+ execvp(cmd, (char **)argv);
+ syslog(LOG_ERR, "Failed to execute %s: %s.\n", cmd, strerror(errno));
+ exit(127);
+ }
+
+ if (pid > 0) { /* parent */
+ if (waitpid(pid, &status, 0) != pid)
+ return 0; /* waitpid failed */
+ if (!WIFEXITED(status) || WEXITSTATUS(status))
+ return 0; /* the child failed */
+ }
+
+ if (pid < 0)
+ return 0; /* fork failed */
+
+ return 1; /* all good */
+}
+
+static int _extend(const char *device)
+{
+ char *vg = NULL, *lv = NULL, *layer = NULL;
+ char cmd_str[1024];
+ int r = 0;
+
+ if (!dm_split_lvm_name(dmeventd_lvm2_pool(), device, &vg, &lv, &layer)) {
+ syslog(LOG_ERR, "Unable to determine VG name from %s.", device);
+ return 0;
+ }
+ if (sizeof(cmd_str) <= snprintf(cmd_str, sizeof(cmd_str),
+ "lvextend --use-policies %s/%s", vg, lv)) {
+ syslog(LOG_ERR, "Unable to form LVM command: Device name too long.");
+ return 0;
+ }
+
+ r = dmeventd_lvm2_run(cmd_str);
+ syslog(LOG_INFO, "Extension of snapshot %s/%s %s.", vg, lv,
+ (r == ECMD_PROCESSED) ? "finished successfully" : "failed");
+ return r == ECMD_PROCESSED;
+}
+
+static void _umount(const char *device, int major, int minor)
+{
+ FILE *mounts;
+ char buffer[4096];
+ char *words[3];
+ struct stat st;
+
+ if (!(mounts = fopen("/proc/mounts", "r"))) {
+ syslog(LOG_ERR, "Could not read /proc/mounts. Not umounting %s.\n", device);
+ return;
+ }
+
+ while (!feof(mounts)) {
+ /* read a line of /proc/mounts */
+ if (!fgets(buffer, sizeof(buffer), mounts))
+ break; /* eof, likely */
+
+ /* words[0] is the mount point and words[1] is the device path */
+ dm_split_words(buffer, 3, 0, words);
+
+ /* find the major/minor of the device */
+ if (stat(words[0], &st))
+ continue; /* can't stat, skip this one */
+
+ if (S_ISBLK(st.st_mode) &&
+ major(st.st_rdev) == major &&
+ minor(st.st_rdev) == minor) {
+ syslog(LOG_ERR, "Unmounting invalid snapshot %s from %s.", device, words[1]);
+ if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL))
+ syslog(LOG_ERR, "Failed to umount snapshot %s from %s: %s.",
+ device, words[1], strerror(errno));
+ }
+ }
+
+ if (fclose(mounts))
+ syslog(LOG_ERR, "Failed to close /proc/mounts.\n");
+}
+
+void process_event(struct dm_task *dmt,
+ enum dm_event_mask event __attribute__((unused)),
+ void **private)
+{
+ void *next = NULL;
+ uint64_t start, length;
+ char *target_type = NULL;
+ char *params;
+ struct snap_status status = { 0 };
+ const char *device = dm_task_get_name(dmt);
+ int percent, *percent_check = (int*)private;
+
+ /* No longer monitoring, waiting for remove */
+ if (!*percent_check)
+ return;
+
+ dmeventd_lvm2_lock();
+
+ dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
+ if (!target_type)
+ goto out;
+
+ _parse_snapshot_params(params, &status);
+
+ if (status.invalid) {
+ syslog(LOG_ERR, "Trying to umount invalid snapshot %s...\n", device);
+ struct dm_info info;
+ if (dm_task_get_info(dmt, &info)) {
+ dmeventd_lvm2_unlock();
+ _umount(device, info.major, info.minor);
+ return;
+ } /* else; too bad, but this is best-effort thing... */
+ }
+
+ /*
+ * If the snapshot has been invalidated or we failed to parse
+ * the status string. Report the full status string to syslog.
+ */
+ if (status.invalid || !status.max) {
+ syslog(LOG_ERR, "Snapshot %s changed state to: %s\n", device, params);
+ *percent_check = 0;
+ goto out;
+ }
+
+ percent = 100 * status.used / status.max;
+ if (percent >= *percent_check) {
+ /* Usage has raised more than CHECK_STEP since the last
+ time. Run actions. */
+ *percent_check = (percent / CHECK_STEP) * CHECK_STEP + CHECK_STEP;
+ if (percent >= WARNING_THRESH) /* Print a warning to syslog. */
+ syslog(LOG_WARNING, "Snapshot %s is now %i%% full.\n", device, percent);
+ /* Try to extend the snapshot, in accord with user-set policies */
+ if (!_extend(device))
+ syslog(LOG_ERR, "Failed to extend snapshot %s.", device);
+ }
+out:
+ dmeventd_lvm2_unlock();
+}
+
+int register_device(const char *device,
+ const char *uuid __attribute__((unused)),
+ int major __attribute__((unused)),
+ int minor __attribute__((unused)),
+ void **private)
+{
+ int *percent_check = (int*)private;
+ int r = dmeventd_lvm2_init();
+
+ *percent_check = CHECK_MINIMUM;
+
+ syslog(LOG_INFO, "Monitoring snapshot %s\n", device);
+ return r;
+}
+
+int unregister_device(const char *device,
+ const char *uuid __attribute__((unused)),
+ int major __attribute__((unused)),
+ int minor __attribute__((unused)),
+ void **unused __attribute__((unused)))
+{
+ syslog(LOG_INFO, "No longer monitoring snapshot %s\n",
+ device);
+ dmeventd_lvm2_exit();
+ return 1;
+}
--- /dev/null
+#
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+CONFSRC=example.conf
+CONFDEST=lvm.conf
+
+include $(top_builddir)/make.tmpl
+
+install_lvm2: $(CONFSRC)
+ @if [ ! -e $(confdir)/$(CONFDEST) ]; then \
+ echo "$(INSTALL_WDATA) -D $< $(confdir)/$(CONFDEST)"; \
+ $(INSTALL_WDATA) -D $< $(confdir)/$(CONFDEST); \
+ fi
+
+install: install_lvm2
+
+DISTCLEAN_TARGETS += $(CONFSRC)
--- /dev/null
+# This is an example configuration file for the LVM2 system.
+# It contains the default settings that would be used if there was no
+# @DEFAULT_SYS_DIR@/lvm.conf file.
+#
+# Refer to 'man lvm.conf' for further information including the file layout.
+#
+# To put this file in a different directory and override @DEFAULT_SYS_DIR@ set
+# the environment variable LVM_SYSTEM_DIR before running the tools.
+
+
+# This section allows you to configure which block devices should
+# be used by the LVM system.
+devices {
+
+ # Where do you want your volume groups to appear ?
+ dir = "/dev"
+
+ # An array of directories that contain the device nodes you wish
+ # to use with LVM2.
+ scan = [ "/dev" ]
+
+ # If several entries in the scanned directories correspond to the
+ # same block device and the tools need to display a name for device,
+ # all the pathnames are matched against each item in the following
+ # list of regular expressions in turn and the first match is used.
+ preferred_names = [ ]
+
+ # Try to avoid using undescriptive /dev/dm-N names, if present.
+ # preferred_names = [ "^/dev/mpath/", "^/dev/mapper/mpath", "^/dev/[hs]d" ]
+
+ # A filter that tells LVM2 to only use a restricted set of devices.
+ # The filter consists of an array of regular expressions. These
+ # expressions can be delimited by a character of your choice, and
+ # prefixed with either an 'a' (for accept) or 'r' (for reject).
+ # The first expression found to match a device name determines if
+ # the device will be accepted or rejected (ignored). Devices that
+ # don't match any patterns are accepted.
+
+ # Be careful if there there are symbolic links or multiple filesystem
+ # entries for the same device as each name is checked separately against
+ # the list of patterns. The effect is that if any name matches any 'a'
+ # pattern, the device is accepted; otherwise if any name matches any 'r'
+ # pattern it is rejected; otherwise it is accepted.
+
+ # Don't have more than one filter line active at once: only one gets used.
+
+ # Run vgscan after you change this parameter to ensure that
+ # the cache file gets regenerated (see below).
+ # If it doesn't do what you expect, check the output of 'vgscan -vvvv'.
+
+
+ # By default we accept every block device:
+ filter = [ "a/.*/" ]
+
+ # Exclude the cdrom drive
+ # filter = [ "r|/dev/cdrom|" ]
+
+ # When testing I like to work with just loopback devices:
+ # filter = [ "a/loop/", "r/.*/" ]
+
+ # Or maybe all loops and ide drives except hdc:
+ # filter =[ "a|loop|", "r|/dev/hdc|", "a|/dev/ide|", "r|.*|" ]
+
+ # Use anchors if you want to be really specific
+ # filter = [ "a|^/dev/hda8$|", "r/.*/" ]
+
+ # The results of the filtering are cached on disk to avoid
+ # rescanning dud devices (which can take a very long time).
+ # By default this cache is stored in the @DEFAULT_SYS_DIR@/@DEFAULT_CACHE_SUBDIR@ directory
+ # in a file called '.cache'.
+ # It is safe to delete the contents: the tools regenerate it.
+ # (The old setting 'cache' is still respected if neither of
+ # these new ones is present.)
+ cache_dir = "@DEFAULT_SYS_DIR@/@DEFAULT_CACHE_SUBDIR@"
+ cache_file_prefix = ""
+
+ # You can turn off writing this cache file by setting this to 0.
+ write_cache_state = 1
+
+ # Advanced settings.
+
+ # List of pairs of additional acceptable block device types found
+ # in /proc/devices with maximum (non-zero) number of partitions.
+ # types = [ "fd", 16 ]
+
+ # If sysfs is mounted (2.6 kernels) restrict device scanning to
+ # the block devices it believes are valid.
+ # 1 enables; 0 disables.
+ sysfs_scan = 1
+
+ # By default, LVM2 will ignore devices used as components of
+ # software RAID (md) devices by looking for md superblocks.
+ # 1 enables; 0 disables.
+ md_component_detection = 1
+
+ # By default, if a PV is placed directly upon an md device, LVM2
+ # will align its data blocks with the md device's stripe-width.
+ # 1 enables; 0 disables.
+ md_chunk_alignment = 1
+
+ # Default alignment of the start of a data area in MB. If set to 0,
+ # a value of 64KB will be used. Set to 1 for 1MiB, 2 for 2MiB, etc.
+ # default_data_alignment = @DEFAULT_DATA_ALIGNMENT@
+
+ # By default, the start of a PV's data area will be a multiple of
+ # the 'minimum_io_size' or 'optimal_io_size' exposed in sysfs.
+ # - minimum_io_size - the smallest request the device can perform
+ # w/o incurring a read-modify-write penalty (e.g. MD's chunk size)
+ # - optimal_io_size - the device's preferred unit of receiving I/O
+ # (e.g. MD's stripe width)
+ # minimum_io_size is used if optimal_io_size is undefined (0).
+ # If md_chunk_alignment is enabled, that detects the optimal_io_size.
+ # This setting takes precedence over md_chunk_alignment.
+ # 1 enables; 0 disables.
+ data_alignment_detection = 1
+
+ # Alignment (in KB) of start of data area when creating a new PV.
+ # md_chunk_alignment and data_alignment_detection are disabled if set.
+ # Set to 0 for the default alignment (see: data_alignment_default)
+ # or page size, if larger.
+ data_alignment = 0
+
+ # By default, the start of the PV's aligned data area will be shifted by
+ # the 'alignment_offset' exposed in sysfs. This offset is often 0 but
+ # may be non-zero; e.g.: certain 4KB sector drives that compensate for
+ # windows partitioning will have an alignment_offset of 3584 bytes
+ # (sector 7 is the lowest aligned logical block, the 4KB sectors start
+ # at LBA -1, and consequently sector 63 is aligned on a 4KB boundary).
+ # But note that pvcreate --dataalignmentoffset will skip this detection.
+ # 1 enables; 0 disables.
+ data_alignment_offset_detection = 1
+
+ # If, while scanning the system for PVs, LVM2 encounters a device-mapper
+ # device that has its I/O suspended, it waits for it to become accessible.
+ # Set this to 1 to skip such devices. This should only be needed
+ # in recovery situations.
+ ignore_suspended_devices = 0
+
+ # During each LVM operation errors received from each device are counted.
+ # If the counter of a particular device exceeds the limit set here, no
+ # further I/O is sent to that device for the remainder of the respective
+ # operation. Setting the parameter to 0 disables the counters altogether.
+ disable_after_error_count = 0
+
+ # Allow use of pvcreate --uuid without requiring --restorefile.
+ require_restorefile_with_uuid = 1
+}
+
+# This section allows you to configure the way in which LVM selects
+# free space for its Logical Volumes.
+#allocation {
+# When searching for free space to extend an LV, the "cling"
+# allocation policy will choose space on the same PVs as the last
+# segment of the existing LV. If there is insufficient space and a
+# list of tags is defined here, it will check whether any of them are
+# attached to the PVs concerned and then seek to match those PV tags
+# between existing extents and new extents.
+# Use the special tag "@*" as a wildcard to match any PV tag.
+#
+# Example: LVs are mirrored between two sites within a single VG.
+# PVs are tagged with either @site1 or @site2 to indicate where
+# they are situated.
+#
+# cling_tag_list = [ "@site1", "@site2" ]
+# cling_tag_list = [ "@*" ]
+#}
+
+# This section that allows you to configure the nature of the
+# information that LVM2 reports.
+log {
+
+ # Controls the messages sent to stdout or stderr.
+ # There are three levels of verbosity, 3 being the most verbose.
+ verbose = 0
+
+ # Should we send log messages through syslog?
+ # 1 is yes; 0 is no.
+ syslog = 1
+
+ # Should we log error and debug messages to a file?
+ # By default there is no log file.
+ #file = "/var/log/lvm2.log"
+
+ # Should we overwrite the log file each time the program is run?
+ # By default we append.
+ overwrite = 0
+
+ # What level of log messages should we send to the log file and/or syslog?
+ # There are 6 syslog-like log levels currently in use - 2 to 7 inclusive.
+ # 7 is the most verbose (LOG_DEBUG).
+ level = 0
+
+ # Format of output messages
+ # Whether or not (1 or 0) to indent messages according to their severity
+ indent = 1
+
+ # Whether or not (1 or 0) to display the command name on each line output
+ command_names = 0
+
+ # A prefix to use before the message text (but after the command name,
+ # if selected). Default is two spaces, so you can see/grep the severity
+ # of each message.
+ prefix = " "
+
+ # To make the messages look similar to the original LVM tools use:
+ # indent = 0
+ # command_names = 1
+ # prefix = " -- "
+
+ # Set this if you want log messages during activation.
+ # Don't use this in low memory situations (can deadlock).
+ # activation = 0
+}
+
+# Configuration of metadata backups and archiving. In LVM2 when we
+# talk about a 'backup' we mean making a copy of the metadata for the
+# *current* system. The 'archive' contains old metadata configurations.
+# Backups are stored in a human readeable text format.
+backup {
+
+ # Should we maintain a backup of the current metadata configuration ?
+ # Use 1 for Yes; 0 for No.
+ # Think very hard before turning this off!
+ backup = 1
+
+ # Where shall we keep it ?
+ # Remember to back up this directory regularly!
+ backup_dir = "@DEFAULT_SYS_DIR@/@DEFAULT_BACKUP_SUBDIR@"
+
+ # Should we maintain an archive of old metadata configurations.
+ # Use 1 for Yes; 0 for No.
+ # On by default. Think very hard before turning this off.
+ archive = 1
+
+ # Where should archived files go ?
+ # Remember to back up this directory regularly!
+ archive_dir = "@DEFAULT_SYS_DIR@/@DEFAULT_ARCHIVE_SUBDIR@"
+
+ # What is the minimum number of archive files you wish to keep ?
+ retain_min = 10
+
+ # What is the minimum time you wish to keep an archive file for ?
+ retain_days = 30
+}
+
+# Settings for the running LVM2 in shell (readline) mode.
+shell {
+
+ # Number of lines of history to store in ~/.lvm_history
+ history_size = 100
+}
+
+
+# Miscellaneous global LVM2 settings
+global {
+
+ # The file creation mask for any files and directories created.
+ # Interpreted as octal if the first digit is zero.
+ umask = 077
+
+ # Allow other users to read the files
+ #umask = 022
+
+ # Enabling test mode means that no changes to the on disk metadata
+ # will be made. Equivalent to having the -t option on every
+ # command. Defaults to off.
+ test = 0
+
+ # Default value for --units argument
+ units = "h"
+
+ # Since version 2.02.54, the tools distinguish between powers of
+ # 1024 bytes (e.g. KiB, MiB, GiB) and powers of 1000 bytes (e.g.
+ # KB, MB, GB).
+ # If you have scripts that depend on the old behaviour, set this to 0
+ # temporarily until you update them.
+ si_unit_consistency = 1
+
+ # Whether or not to communicate with the kernel device-mapper.
+ # Set to 0 if you want to use the tools to manipulate LVM metadata
+ # without activating any logical volumes.
+ # If the device-mapper kernel driver is not present in your kernel
+ # setting this to 0 should suppress the error messages.
+ activation = 1
+
+ # If we can't communicate with device-mapper, should we try running
+ # the LVM1 tools?
+ # This option only applies to 2.4 kernels and is provided to help you
+ # switch between device-mapper kernels and LVM1 kernels.
+ # The LVM1 tools need to be installed with .lvm1 suffices
+ # e.g. vgscan.lvm1 and they will stop working after you start using
+ # the new lvm2 on-disk metadata format.
+ # The default value is set when the tools are built.
+ # fallback_to_lvm1 = 0
+
+ # The default metadata format that commands should use - "lvm1" or "lvm2".
+ # The command line override is -M1 or -M2.
+ # Defaults to "lvm2".
+ # format = "lvm2"
+
+ # Location of proc filesystem
+ proc = "/proc"
+
+ # Type of locking to use. Defaults to local file-based locking (1).
+ # Turn locking off by setting to 0 (dangerous: risks metadata corruption
+ # if LVM2 commands get run concurrently).
+ # Type 2 uses the external shared library locking_library.
+ # Type 3 uses built-in clustered locking.
+ # Type 4 uses read-only locking which forbids any operations that might
+ # change metadata.
+ locking_type = 1
+
+ # Set to 0 to fail when a lock request cannot be satisfied immediately.
+ wait_for_locks = 1
+
+ # If using external locking (type 2) and initialisation fails,
+ # with this set to 1 an attempt will be made to use the built-in
+ # clustered locking.
+ # If you are using a customised locking_library you should set this to 0.
+ fallback_to_clustered_locking = 1
+
+ # If an attempt to initialise type 2 or type 3 locking failed, perhaps
+ # because cluster components such as clvmd are not running, with this set
+ # to 1 an attempt will be made to use local file-based locking (type 1).
+ # If this succeeds, only commands against local volume groups will proceed.
+ # Volume Groups marked as clustered will be ignored.
+ fallback_to_local_locking = 1
+
+ # Local non-LV directory that holds file-based locks while commands are
+ # in progress. A directory like /tmp that may get wiped on reboot is OK.
+ locking_dir = "@DEFAULT_LOCK_DIR@"
+
+ # Whenever there are competing read-only and read-write access requests for
+ # a volume group's metadata, instead of always granting the read-only
+ # requests immediately, delay them to allow the read-write requests to be
+ # serviced. Without this setting, write access may be stalled by a high
+ # volume of read-only requests.
+ # NB. This option only affects locking_type = 1 viz. local file-based
+ # locking.
+ prioritise_write_locks = 1
+
+ # Other entries can go here to allow you to load shared libraries
+ # e.g. if support for LVM1 metadata was compiled as a shared library use
+ # format_libraries = "liblvm2format1.so"
+ # Full pathnames can be given.
+
+ # Search this directory first for shared libraries.
+ # library_dir = "/lib"
+
+ # The external locking library to load if locking_type is set to 2.
+ # locking_library = "liblvm2clusterlock.so"
+
+ # Treat any internal errors as fatal errors, aborting the process that
+ # encountered the internal error. Please only enable for debugging.
+ abort_on_internal_errors = 0
+
+ # If set to 1, no operations that change on-disk metadata will be permitted.
+ # Additionally, read-only commands that encounter metadata in need of repair
+ # will still be allowed to proceed exactly as if the repair had been
+ # performed (except for the unchanged vg_seqno).
+ # Inappropriate use could mess up your system, so seek advice first!
+ metadata_read_only = 0
+}
+
+activation {
+ # Set to 0 to disable udev synchronisation (if compiled into the binaries).
+ # Processes will not wait for notification from udev.
+ # They will continue irrespective of any possible udev processing
+ # in the background. You should only use this if udev is not running
+ # or has rules that ignore the devices LVM2 creates.
+ # The command line argument --nodevsync takes precedence over this setting.
+ # If set to 1 when udev is not running, and there are LVM2 processes
+ # waiting for udev, run 'dmsetup udevcomplete_all' manually to wake them up.
+ udev_sync = 1
+
+ # Set to 0 to disable the udev rules installed by LVM2 (if built with
+ # --enable-udev_rules). LVM2 will then manage the /dev nodes and symlinks
+ # for active logical volumes directly itself.
+ # N.B. Manual intervention may be required if this setting is changed
+ # while any logical volumes are active.
+ udev_rules = 1
+
+ # How to fill in missing stripes if activating an incomplete volume.
+ # Using "error" will make inaccessible parts of the device return
+ # I/O errors on access. You can instead use a device path, in which
+ # case, that device will be used to in place of missing stripes.
+ # But note that using anything other than "error" with mirrored
+ # or snapshotted volumes is likely to result in data corruption.
+ missing_stripe_filler = "error"
+
+ # How much stack (in KB) to reserve for use while devices suspended
+ reserved_stack = 256
+
+ # How much memory (in KB) to reserve for use while devices suspended
+ reserved_memory = 8192
+
+ # Nice value used while devices suspended
+ process_priority = -18
+
+ # If volume_list is defined, each LV is only activated if there is a
+ # match against the list.
+ # "vgname" and "vgname/lvname" are matched exactly.
+ # "@tag" matches any tag set in the LV or VG.
+ # "@*" matches if any tag defined on the host is also set in the LV or VG
+ #
+ # volume_list = [ "vg1", "vg2/lvol1", "@tag1", "@*" ]
+
+ # Size (in KB) of each copy operation when mirroring
+ mirror_region_size = 512
+
+ # Setting to use when there is no readahead value stored in the metadata.
+ #
+ # "none" - Disable readahead.
+ # "auto" - Use default value chosen by kernel.
+ readahead = "auto"
+
+ # 'mirror_image_fault_policy' and 'mirror_log_fault_policy' define
+ # how a device failure affecting a mirror is handled.
+ # A mirror is composed of mirror images (copies) and a log.
+ # A disk log ensures that a mirror does not need to be re-synced
+ # (all copies made the same) every time a machine reboots or crashes.
+ #
+ # In the event of a failure, the specified policy will be used to determine
+ # what happens. This applies to automatic repairs (when the mirror is being
+ # monitored by dmeventd) and to manual lvconvert --repair when
+ # --use-policies is given.
+ #
+ # "remove" - Simply remove the faulty device and run without it. If
+ # the log device fails, the mirror would convert to using
+ # an in-memory log. This means the mirror will not
+ # remember its sync status across crashes/reboots and
+ # the entire mirror will be re-synced. If a
+ # mirror image fails, the mirror will convert to a
+ # non-mirrored device if there is only one remaining good
+ # copy.
+ #
+ # "allocate" - Remove the faulty device and try to allocate space on
+ # a new device to be a replacement for the failed device.
+ # Using this policy for the log is fast and maintains the
+ # ability to remember sync state through crashes/reboots.
+ # Using this policy for a mirror device is slow, as it
+ # requires the mirror to resynchronize the devices, but it
+ # will preserve the mirror characteristic of the device.
+ # This policy acts like "remove" if no suitable device and
+ # space can be allocated for the replacement.
+ #
+ # "allocate_anywhere" - Not yet implemented. Useful to place the log device
+ # temporarily on same physical volume as one of the mirror
+ # images. This policy is not recommended for mirror devices
+ # since it would break the redundant nature of the mirror. This
+ # policy acts like "remove" if no suitable device and space can
+ # be allocated for the replacement.
+
+ mirror_log_fault_policy = "allocate"
+ mirror_image_fault_policy = "remove"
+
+ # 'snapshot_autoextend_threshold' and 'snapshot_autoextend_percent' define
+ # how to handle automatic snapshot extension. The former defines when the
+ # snapshot should be extended: when its space usage exceeds this many
+ # percent. The latter defines how much extra space should be allocated for
+ # the snapshot, in percent of its current size.
+ #
+ # For example, if you set snapshot_autoextend_threshold to 70 and
+ # snapshot_autoextend_percent to 20, whenever a snapshot exceeds 70% usage,
+ # it will be extended by another 20%. For a 1G snapshot, using up 700M will
+ # trigger a resize to 1.2G. When the usage exceeds 840M, the snapshot will
+ # be extended to 1.44G, and so on.
+ #
+ # Setting snapshot_autoextend_threshold to 100 disables automatic
+ # extensions. The minimum value is 50 (A setting below 50 will be treated
+ # as 50).
+
+ snapshot_autoextend_threshold = 100
+ snapshot_autoextend_percent = 20
+
+ # While activating devices, I/O to devices being (re)configured is
+ # suspended, and as a precaution against deadlocks, LVM2 needs to pin
+ # any memory it is using so it is not paged out. Groups of pages that
+ # are known not to be accessed during activation need not be pinned
+ # into memory. Each string listed in this setting is compared against
+ # each line in /proc/self/maps, and the pages corresponding to any
+ # lines that match are not pinned. On some systems locale-archive was
+ # found to make up over 80% of the memory used by the process.
+ # mlock_filter = [ "locale/locale-archive", "gconv/gconv-modules.cache" ]
+
+ # Set to 1 to revert to the default behaviour prior to version 2.02.62
+ # which used mlockall() to pin the whole process's memory while activating
+ # devices.
+ use_mlockall = 0
+
+ # Monitoring is enabled by default when activating logical volumes.
+ # Set to 0 to disable monitoring or use the --ignoremonitoring option.
+ monitoring = 1
+
+ # When pvmove or lvconvert must wait for the kernel to finish
+ # synchronising or merging data, they check and report progress
+ # at intervals of this number of seconds. The default is 15 seconds.
+ # If this is set to 0 and there is only one thing to wait for, there
+ # are no progress reports, but the process is awoken immediately the
+ # operation is complete.
+ polling_interval = 15
+}
+
+
+####################
+# Advanced section #
+####################
+
+# Metadata settings
+#
+# metadata {
+ # Default number of copies of metadata to hold on each PV. 0, 1 or 2.
+ # You might want to override it from the command line with 0
+ # when running pvcreate on new PVs which are to be added to large VGs.
+
+ # pvmetadatacopies = 1
+
+ # Default number of copies of metadata to maintain for each VG.
+ # If set to a non-zero value, LVM automatically chooses which of
+ # the available metadata areas to use to achieve the requested
+ # number of copies of the VG metadata. If you set a value larger
+ # than the the total number of metadata areas available then
+ # metadata is stored in them all.
+ # The default value of 0 ("unmanaged") disables this automatic
+ # management and allows you to control which metadata areas
+ # are used at the individual PV level using 'pvchange
+ # --metadataignore y/n'.
+
+ # vgmetadatacopies = 0
+
+ # Approximate default size of on-disk metadata areas in sectors.
+ # You should increase this if you have large volume groups or
+ # you want to retain a large on-disk history of your metadata changes.
+
+ # pvmetadatasize = 255
+
+ # List of directories holding live copies of text format metadata.
+ # These directories must not be on logical volumes!
+ # It's possible to use LVM2 with a couple of directories here,
+ # preferably on different (non-LV) filesystems, and with no other
+ # on-disk metadata (pvmetadatacopies = 0). Or this can be in
+ # addition to on-disk metadata areas.
+ # The feature was originally added to simplify testing and is not
+ # supported under low memory situations - the machine could lock up.
+ #
+ # Never edit any files in these directories by hand unless you
+ # you are absolutely sure you know what you are doing! Use
+ # the supplied toolset to make changes (e.g. vgcfgrestore).
+
+ # dirs = [ "/etc/lvm/metadata", "/mnt/disk2/lvm/metadata2" ]
+#}
+
+# Event daemon
+#
+dmeventd {
+ # mirror_library is the library used when monitoring a mirror device.
+ #
+ # "libdevmapper-event-lvm2mirror.so" attempts to recover from
+ # failures. It removes failed devices from a volume group and
+ # reconfigures a mirror as necessary. If no mirror library is
+ # provided, mirrors are not monitored through dmeventd.
+
+ mirror_library = "libdevmapper-event-lvm2mirror.so"
+
+ # snapshot_library is the library used when monitoring a snapshot device.
+ #
+ # "libdevmapper-event-lvm2snapshot.so" monitors the filling of
+ # snapshots and emits a warning through syslog when the use of
+ # the snapshot exceeds 80%. The warning is repeated when 85%, 90% and
+ # 95% of the snapshot is filled.
+
+ snapshot_library = "libdevmapper-event-lvm2snapshot.so"
+
+ # Full path of the dmeventd binary.
+ #
+ # executable = "@DMEVENTD_PATH@"
+}
--- /dev/null
+/*
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lvm2cmd.h"
+#include <stdio.h>
+
+/* All output gets passed to this function line-by-line */
+void test_log_fn(int level, const char *file, int line,
+ int dm_errno, const char *format)
+{
+ /* Extract and process output here rather than printing it */
+
+ if (level != 4)
+ return;
+
+ printf("%s\n", format);
+ return;
+}
+
+int main(int argc, char **argv)
+{
+ void *handle;
+ int r;
+
+ lvm2_log_fn(test_log_fn);
+
+ handle = lvm2_init();
+
+ lvm2_log_level(handle, 1);
+ r = lvm2_run(handle, "vgs --noheadings vg1");
+
+ /* More commands here */
+
+ lvm2_exit(handle);
+
+ return r;
+}
+
--- /dev/null
+LVM device fault handling
+=========================
+
+Introduction
+------------
+This document is to serve as the definitive source for information
+regarding the policies and procedures surrounding device failures
+in LVM. It codifies LVM's responses to device failures as well as
+the responsibilities of administrators.
+
+Device failures can be permanent or transient. A permanent failure
+is one where a device becomes inaccessible and will never be
+revived. A transient failure is a failure that can be recovered
+from (e.g. a power failure, intermittent network outage, block
+relocation, etc). The policies for handling both types of failures
+is described herein.
+
+Available Operations During a Device Failure
+--------------------------------------------
+When there is a device failure, LVM behaves somewhat differently because
+only a subset of the available devices will be found for the particular
+volume group. The number of operations available to the administrator
+is diminished. It is not possible to create new logical volumes while
+PVs cannot be accessed, for example. Operations that create, convert, or
+resize logical volumes are disallowed, such as:
+- lvcreate
+- lvresize
+- lvreduce
+- lvextend
+- lvconvert (unless '--repair' is used)
+Operations that activate, deactivate, remove, report, or repair logical
+volumes are allowed, such as:
+- lvremove
+- vgremove (will remove all LVs, but not the VG until consistent)
+- pvs
+- vgs
+- lvs
+- lvchange -a [yn]
+- vgchange -a [yn]
+Operations specific to the handling of failed devices are allowed and
+are as follows:
+
+- 'vgreduce --removemissing <VG>': This action is designed to remove
+ the reference of a failed device from the LVM metadata stored on the
+ remaining devices. If there are (portions of) logical volumes on the
+ failed devices, the ability of the operation to proceed will depend
+ on the type of logical volumes found. If an image (i.e leg or side)
+ of a mirror is located on the device, that image/leg of the mirror
+ is eliminated along with the failed device. The result of such a
+ mirror reduction could be a no-longer-redundant linear device. If
+ a linear, stripe, or snapshot device is located on the failed device
+ the command will not proceed without a '--force' option. The result
+ of using the '--force' option is the entire removal and complete
+ loss of the non-redundant logical volume. Once this operation is
+ complete, the volume group will again have a complete and consistent
+ view of the devices it contains. Thus, all operations will be
+ permitted - including creation, conversion, and resizing operations.
+
+- 'lvconvert --repair <VG/LV>': This action is designed specifically
+ to operate on mirrored logical volumes. It is used on logical volumes
+ individually and does not remove the faulty device from the volume
+ group. If, for example, a failed device happened to contain the
+ images of four distinct mirrors, it would be necessary to run
+ 'lvconvert --repair' on each of them. The ultimate result is to leave
+ the faulty device in the volume group, but have no logical volumes
+ referencing it. In addition to removing mirror images that reside
+ on failed devices, 'lvconvert --repair' can also replace the failed
+ device if there are spare devices available in the volume group. The
+ user is prompted whether to simply remove the failed portions of the
+ mirror or to also allocate a replacement, if run from the command-line.
+ Optionally, the '--use-policies' flag can be specified which will
+ cause the operation not to prompt the user, but instead respect
+ the policies outlined in the LVM configuration file - usually,
+ /etc/lvm/lvm.conf. Once this operation is complete, mirrored logical
+ volumes will be consistent and I/O will be allowed to continue.
+ However, the volume group will still be inconsistent - due to the
+ refernced-but-missing device/PV - and operations will still be
+ restricted to the aformentioned actions until either the device is
+ restored or 'vgreduce --removemissing' is run.
+
+Device Revival (transient failures):
+------------------------------------
+During a device failure, the above section describes what limitations
+a user can expect. However, if the device returns after a period of
+time, what to expect will depend on what has happened during the time
+period when the device was failed. If no automated actions (described
+below) or user actions were necessary or performed, then no change in
+operations or logical volume layout will occur. However, if an
+automated action or one of the aforementioned repair commands was
+manually run, the returning device will be perceived as having stale
+LVM metadata. In this case, the user can expect to see a warning
+concerning inconsistent metadata. The metadata on the returning
+device will be automatically replaced with the latest copy of the
+LVM metadata - restoring consistency. Note, while most LVM commands
+will automatically update the metadata on a restored devices, the
+following possible exceptions exist:
+- pvs (when it does not read/update VG metadata)
+
+Automated Target Response to Failures:
+--------------------------------------
+The only LVM target type (i.e. "personality") that has an automated
+response to failures is a mirrored logical volume. The other target
+types (linear, stripe, snapshot, etc) will simply propagate the failure.
+[A snapshot becomes invalid if its underlying device fails, but the
+origin will remain valid - presuming the origin device has not failed.]
+There are three types of errors that a mirror can suffer - read, write,
+and resynchronization errors. Each is described in depth below.
+
+Mirror read failures:
+If a mirror is 'in-sync' (i.e. all images have been initialized and
+are identical), a read failure will only produce a warning. Data is
+simply pulled from one of the other images and the fault is recorded.
+Sometimes - like in the case of bad block relocation - read errors can
+be recovered from by the storage hardware. Therefore, it is up to the
+user to decide whether to reconfigure the mirror and remove the device
+that caused the error. Managing the composition of a mirror is done with
+'lvconvert' and removing a device from a volume group can be done with
+'vgreduce'.
+
+If a mirror is not 'in-sync', a read failure will produce an I/O error.
+This error will propagate all the way up to the applications above the
+logical volume (e.g. the file system). No automatic intervention will
+take place in this case either. It is up to the user to decide what
+can be done/salvaged in this senario. If the user is confident that the
+images of the mirror are the same (or they are willing to simply attempt
+to retreive whatever data they can), 'lvconvert' can be used to eliminate
+the failed image and proceed.
+
+Mirror resynchronization errors:
+A resynchronization error is one that occurs when trying to initialize
+all mirror images to be the same. It can happen due to a failure to
+read the primary image (the image considered to have the 'good' data), or
+due to a failure to write the secondary images. This type of failure
+only produces a warning, and it is up to the user to take action in this
+case. If the error is transient, the user can simply reactivate the
+mirrored logical volume to make another attempt at resynchronization.
+If attempts to finish resynchronization fail, 'lvconvert' can be used to
+remove the faulty device from the mirror.
+
+TODO...
+Some sort of response to this type of error could be automated.
+Since this document is the definitive source for how to handle device
+failures, the process should be defined here. If the process is defined
+but not implemented, it should be noted as such. One idea might be to
+make a single attempt to suspend/resume the mirror in an attempt to
+redo the sync operation that failed. On the other hand, if there is
+a permanent failure, it may simply be best to wait for the user or the
+automated response that is sure to follow from a write failure.
+...TODO
+
+Mirror write failures:
+When a write error occurs on a mirror constituent device, an attempt
+to handle the failure is automatically made. This is done by calling
+'lvconvert --repair --use-policies'. The policies implied by this
+command are set in the LVM configuration file. They are:
+- mirror_log_fault_policy: This defines what action should be taken
+ if the device containing the log fails. The available options are
+ "remove" and "allocate". Either of these options will cause the
+ faulty log device to be removed from the mirror. The "allocate"
+ policy will attempt the further action of trying to replace the
+ failed disk log by using space that might be available in the
+ volume group. If the allocation fails (or the "remove" policy
+ is specified), the mirror log will be maintained in memory. Should
+ the machine be rebooted or the logical volume deactivated, a
+ complete resynchronization of the mirror will be necessary upon
+ the follow activation - such is the nature of a mirror with a 'core'
+ log. The default policy for handling log failures is "allocate".
+ The service disruption incurred by replacing the failed log is
+ negligible, while the benefits of having persistent log is
+ pronounced.
+- mirror_image_fault_policy: This defines what action should be taken
+ if a device containing an image fails. Again, the available options
+ are "remove" and "allocate". Both of these options will cause the
+ faulty image device to be removed - adjusting the logical volume
+ accordingly. For example, if one image of a 2-way mirror fails, the
+ mirror will be converted to a linear device. If one image of a
+ 3-way mirror fails, the mirror will be converted to a 2-way mirror.
+ The "allocate" policy takes the further action of trying to replace
+ the failed image using space that is available in the volume group.
+ Replacing a failed mirror image will incure the cost of
+ resynchronizing - degrading the performance of the mirror. The
+ default policy for handling an image failure is "remove". This
+ allows the mirror to still function, but gives the administrator the
+ choice of when to incure the extra performance costs of replacing
+ the failed image.
+
+TODO...
+The appropriate time to take permanent corrective action on a mirror
+should be driven by policy. There should be a directive that takes
+a time or percentage argument. Something like the following:
+- mirror_fault_policy_WHEN = "10sec"/"10%"
+A time value would signal the amount of time to wait for transient
+failures to resolve themselves. The percentage value would signal the
+amount a mirror could become out-of-sync before the faulty device is
+removed.
+
+A mirror cannot be used unless /some/ corrective action is taken,
+however. One option is to replace the failed mirror image with an
+error target, forgo the use of 'handle_errors', and simply let the
+out-of-sync regions accumulate and be tracked by the log. Mirrors
+that have more than 2 images would have to "stack" to perform the
+tracking, as each failed image would have to be associated with a
+log. If the failure is transient, the device would replace the
+error target that was holding its spot and the log that was tracking
+the deltas would be used to quickly restore the portions that changed.
+
+One unresolved issue with the above scheme is how to know which
+regions of the mirror are out-of-sync when a problem occurs. When
+a write failure occurs in the kernel, the log will contain those
+regions that are not in-sync. If the log is a disk log, that log
+could continue to be used to track differences. However, if the
+log was a core log - or if the log device failed at the same time
+as an image device - there would be no way to determine which
+regions are out-of-sync to begin with as we start to track the
+deltas for the failed image. I don't have a solution for this
+problem other than to only be able to handle errors in this way
+if conditions are right. These issues will have to be ironed out
+before proceeding. This could be another case, where it is better
+to handle failures in the kernel by allowing the kernel to store
+updates in various metadata areas.
+...TODO
--- /dev/null
+Let's say we have an LV, made up of three segments of different PV's,
+I've also added in the device major:minor as this will be useful
+later:
+
++-----------------------------+
+| PV1 | PV2 | PV3 | 254:3
++----------+---------+--------+
+
+
+Now our hero decides to PV move PV2 to PV4:
+
+1. Suspend our LV (254:3), this starts queueing all io, and flushes
+ all pending io. Once the suspend has completed we are free to change
+ the mapping table.
+
+2. Set up *another* (254:4) device with the mapping table of our LV.
+
+3. Load a new mapping table into (254:3) that has identity targets for
+ parts that aren't moving, and a mirror target for parts that are.
+
+4. Unsuspend (254:3)
+
+So now we have:
+ destination of copy
+ +--------------------->--------------+
+ | |
++-----------------------------+ + -----------+
+| Identity | mirror | Ident. | 254:3 | PV4 |
++----------+---------+--------+ +------------+
+ | | |
+ \/ \/ \/
++-----------------------------+
+| PV1 | PV2 | PV3 | 254:4
++----------+---------+--------+
+
+Any writes to segment2 of the LV get intercepted by the mirror target
+who checks that that chunk has been copied to the new destination, if
+it hasn't it queues the initial copy and defers the current io until
+it has finished. Then the current io is written to *both* PV2 and the
+PV4.
+
+5. When the copying has completed 254:3 is suspended/pending flushed.
+
+6. 254:4 is taken down
+
+7. metadata is updated on disk
+
+8. 254:3 has new mapping table loaded:
+
++-----------------------------+
+| PV1 | PV4 | PV3 | 254:3
++----------+---------+--------+
--- /dev/null
+Tagging aims
+============
+ 1) Ability to attach an unordered list of tags to LVM metadata objects.
+ 2) Ability to add or remove tags easily.
+ 3) Ability to select LVM objects for processing according to presence/absence
+ of specific tags.
+ 4) Ability to control through the config file which VGs/LVs are activated
+ on different machines using names or tags.
+ 5) Ability to overlay settings from different config files e.g. override
+ some settings in a global config file locally.
+
+Clarifications
+==============
+ 1) Tag character set: A-Za-z0-9_+.-
+ Can't start with hyphen & max length is 128 (NAME_LEN).
+ 2) LVM object types that can be tagged:
+ VG, LV, LV segment
+ PV - tags are stored in VG metadata so disappear when PV becomes orphaned
+ Snapshots can't be tagged, but their origin may be.
+ 3) A tag can be used in place of any command line LVM object reference that
+ accepts (a) a list of objects; or (b) a single object as long as the
+ tag expands to a single object. This is not supported everywhere yet.
+ Duplicate arguments in a list after argument expansion may get removed
+ retaining the first copy of each argument.
+ 4) Wherever there may be ambiguity of argument type, a tag must be prefixed
+ by '@'; elsewhere an '@' prefix is optional.
+ 5) LVM1 objects cannot be tagged, as the disk format doesn't support it.
+ 6) Tags can be added or removed with --addtag or --deltag.
+
+Config file Extensions
+======================
+ To define host tags in config file:
+
+ tags {
+ # Set a tag with the hostname
+ hosttags = 1
+
+ tag1 { }
+
+ tag2 {
+ # If no exact match, tag is not set.
+ host_list = [ "hostname", "dbase" ]
+ }
+ }
+
+Activation config file example
+==============================
+ activation {
+ volume_list = [ "vg1/lvol0", "@database" ]
+ }
+
+ Matches against vgname, vgname/lvname or @tag set in *metadata*.
+ @* matches exactly against *any* tag set on the host.
+ The VG or LV only gets activated if a metadata tag matches.
+ The default if there is no match is not to activate.
+ If volume_list is not present and any tags are defined on the host
+ then it only activates if a host tag matches a metadata tag.
+ If volume_list is not present and no tags are defined on the host
+ then it does activate.
+
+Multiple config files
+=====================
+ (a) lvm.conf
+ (b) lvm_<host_tag>.conf
+
+ At startup, load lvm.conf.
+ Process tag settings.
+ If any host tags were defined, load lvm_tag.conf for each tag, if present.
+
+ When searching for a specific config file entry, search order is (b)
+ then (a), stopping at the first match.
+ Within (b) use reverse order tags got set, so file for last tag set is
+ searched first.
+ New tags set in (b) *do* trigger additional config file loads.
+
+Usage Examples
+==============
+ 1) Simple activation control via metadata with static config files
+
+ lvm.conf: (Identical on every machine - global settings)
+ tags {
+ hostname_tags = 1
+ }
+
+ From any machine in the cluster, add db1 to the list of machines that
+ activate vg1/lvol2:
+
+ lvchange --tag @db1 vg1/lvol2
+ (followed by lvchange -ay to actually activate it)
+
+
+ 2) Multiple hosts.
+
+ Activate vg1 only on the database hosts, db1 and db2.
+ Activate vg2 only on the fileserver host fs1.
+ Activate nothing initially on the fileserver backup host fsb1, but be
+ prepared for it to take over from fs1.
+
+ Option (i) - centralised admin, static configuration replicated between hosts
+ # Add @database tag to vg1's metadata
+ vgchange --tag @database vg1
+
+ # Add @fileserver tag to vg2's metadata
+ vgchange --tag @fileserver vg2
+
+ lvm.conf: (Identical on every machine)
+ tags {
+ database {
+ host_list = [ "db1", "db2" ]
+ }
+ fileserver {
+ host_list = [ "fs1" ]
+ }
+ fileserverbackup {
+ host_list = [ "fsb1" ]
+ }
+ }
+
+ activation {
+ # Only activate if host has a tag that matches a metadata tag
+ volume_list = [ "@*" ]
+ }
+
+ In the event of the fileserver host going down, vg2 can be brought up
+ on fsb1 by running *on any node* 'vgchange --tag @fileserverbackup vg2'
+ followed by 'vgchange -ay vg2'
+
+
+ Option (ii) - localised admin & configuation
+ (i.e. each host holds *locally* which classes of volumes to activate)
+ # Add @database tag to vg1's metadata
+ vgchange --tag @database vg1
+
+ # Add @fileserver tag to vg2's metadata
+ vgchange --tag @fileserver vg2
+
+ lvm.conf: (Identical on every machine - global settings)
+ tags {
+ hosttags = 1
+ }
+
+ lvm_db1.conf: (only needs to be on db1 - could be symlink to lvm_db.conf)
+ activation {
+ volume_list = [ "@database" ]
+ }
+
+ lvm_db2.conf: (only needs to be on db2 - could be symlink to lvm_db.conf)
+ activation {
+ volume_list = [ "@database" ]
+ }
+
+ lvm_fs1.conf: (only needs to be on fs1 - could be symlink to lvm_fs.conf)
+ activation {
+ volume_list = [ "@fileserver" ]
+ }
+
+ If fileserver goes down, to bring a spare machine fsb1 in as fileserver,
+ create lvm_fsb1.conf on fsb1 (or symlink to lvm_fs.conf):
+
+ activation {
+ volume_list = [ "@fileserver" ]
+ }
+
+ and run 'vgchange -ay vg2' or 'vgchange -ay @fileserver'
+
--- /dev/null
+Here's how I test new LVM2 builds without interfering with the stable
+LVM2 that is running the LV's on my development box.
+
+1) Create a set of loopback devices.
+
+2) Create a new directory to contain the LVM2 configuration files for
+ this setup. (I use /etc/lvm_loops)
+
+3) Write a suitable lvm.conf file, this goes in the directory you just
+ created. eg, my /etc/lvm_loops/lvm.conf looks like:
+
+ log {
+ file="/tmp/lvm2_loop.log"
+ level=9
+ verbose=0
+ overwrite=1
+ }
+
+ devices {
+ scan = "/dev"
+ filter = ["a/loop/", "r/.*/"]
+ }
+
+
+ The important thing to note is the devices section which makes sure
+ that only the loopback devices are considered for LVM2 operations.
+
+4) When you want to use this test setup just set the environment
+ variable LVM_SYSTEM_DIR to point to your config directory
+ (/etc/lvm_loops in my case).
+
+5) It's a good idea to do a vgscan to initialise the filters:
+
+ export LVM_SYSTEM_DIR=/etc/lvm_loops
+ ./lvm vgscan
+
+ where ./lvm is the new build of LVM2 that I'm trying out.
+
+7) Test away. Make sure that you are explicit about which lvm
+ executable you want to execute (eg, ./lvm if you are in
+ LVM2/tools).
--- /dev/null
+@top_srcdir@/daemons/clvmd/clvm.h
+@top_srcdir@/daemons/dmeventd/libdevmapper-event.h
+@top_srcdir@/liblvm/lvm2app.h
+@top_srcdir@/lib/activate/activate.h
+@top_srcdir@/lib/activate/targets.h
+@top_srcdir@/lib/cache/lvmcache.h
+@top_srcdir@/lib/commands/errors.h
+@top_srcdir@/lib/commands/toolcontext.h
+@top_srcdir@/lib/config/config.h
+@top_srcdir@/lib/config/defaults.h
+@top_srcdir@/lib/datastruct/btree.h
+@top_srcdir@/lib/datastruct/lvm-types.h
+@top_srcdir@/lib/datastruct/str_list.h
+@top_srcdir@/lib/device/dev-cache.h
+@top_srcdir@/lib/device/device.h
+@top_srcdir@/lib/display/display.h
+@top_srcdir@/lib/filters/filter-composite.h
+@top_srcdir@/lib/filters/filter-md.h
+@top_srcdir@/lib/filters/filter-persistent.h
+@top_srcdir@/lib/filters/filter-regex.h
+@top_srcdir@/lib/filters/filter-sysfs.h
+@top_srcdir@/lib/filters/filter.h
+@top_srcdir@/lib/format1/format1.h
+@top_srcdir@/lib/format_pool/format_pool.h
+@top_srcdir@/lib/format_text/archiver.h
+@top_srcdir@/lib/format_text/format-text.h
+@top_srcdir@/lib/format_text/text_export.h
+@top_srcdir@/lib/format_text/text_import.h
+@top_srcdir@/lib/label/label.h
+@top_srcdir@/lib/locking/locking.h
+@top_srcdir@/lib/log/log.h
+@top_srcdir@/lib/log/lvm-logging.h
+@top_srcdir@/lib/metadata/lv.h
+@top_srcdir@/lib/metadata/lv_alloc.h
+@top_srcdir@/lib/metadata/metadata.h
+@top_srcdir@/lib/metadata/metadata-exported.h
+@top_srcdir@/lib/metadata/pv.h
+@top_srcdir@/lib/metadata/pv_alloc.h
+@top_srcdir@/lib/metadata/segtype.h
+@top_srcdir@/lib/metadata/vg.h
+@top_srcdir@/lib/mm/memlock.h
+@top_srcdir@/lib/mm/xlate.h
+@top_builddir@/lib/misc/configure.h
+@top_srcdir@/lib/misc/crc.h
+@top_srcdir@/lib/misc/intl.h
+@top_srcdir@/lib/misc/util.h
+@top_srcdir@/lib/misc/last-path-component.h
+@top_srcdir@/lib/misc/lib.h
+@top_srcdir@/lib/misc/lvm-exec.h
+@top_srcdir@/lib/misc/lvm-file.h
+@top_srcdir@/lib/misc/lvm-globals.h
+@top_srcdir@/lib/misc/lvm-string.h
+@top_builddir@/lib/misc/lvm-version.h
+@top_srcdir@/lib/misc/lvm-wrappers.h
+@top_srcdir@/lib/misc/lvm-percent.h
+@top_srcdir@/lib/misc/sharedlib.h
+@top_srcdir@/lib/report/properties.h
+@top_srcdir@/lib/report/report.h
+@top_srcdir@/lib/uuid/uuid.h
+@top_srcdir@/libdm/libdevmapper.h
+@top_srcdir@/libdm/misc/dm-ioctl.h
+@top_srcdir@/libdm/misc/dm-logging.h
+@top_srcdir@/libdm/misc/dm-log-userspace.h
+@top_srcdir@/libdm/misc/dmlib.h
+@top_srcdir@/libdm/misc/kdev_t.h
+@top_srcdir@/po/pogen.h
+@top_srcdir@/tools/lvm2cmd.h
--- /dev/null
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+include $(top_builddir)/make.tmpl
+
+all: .symlinks_created
+
+.symlinks_created: .symlinks
+ find . -maxdepth 1 -type l -exec $(RM) \{\} \;
+ for i in `cat $<`; do $(LN_S) $$i ; done
+ touch $@
+
+pofile: all
+
+device-mapper: all
+
+cflow: all
+
+DISTCLEAN_TARGETS += $(shell find . -maxdepth 1 -type l)
+DISTCLEAN_TARGETS += .include_symlinks .symlinks_created .symlinks
--- /dev/null
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+ifeq ("@LVM1@", "shared")
+ SUBDIRS = format1
+endif
+
+ifeq ("@POOL@", "shared")
+ SUBDIRS += format_pool
+endif
+
+ifeq ("@SNAPSHOTS@", "shared")
+ SUBDIRS += snapshot
+endif
+
+ifeq ("@MIRRORS@", "shared")
+ SUBDIRS += mirror
+endif
+
+ifeq ("@REPLICATORS@", "shared")
+ SUBDIRS += replicator
+endif
+
+SOURCES =\
+ activate/activate.c \
+ cache/lvmcache.c \
+ commands/toolcontext.c \
+ config/config.c \
+ datastruct/btree.c \
+ datastruct/str_list.c \
+ device/dev-cache.c \
+ device/dev-io.c \
+ device/dev-md.c \
+ device/dev-swap.c \
+ device/dev-luks.c \
+ device/device.c \
+ display/display.c \
+ error/errseg.c \
+ unknown/unknown.c \
+ filters/filter-composite.c \
+ filters/filter-persistent.c \
+ filters/filter-regex.c \
+ filters/filter-sysfs.c \
+ filters/filter-md.c \
+ filters/filter.c \
+ format_text/archive.c \
+ format_text/archiver.c \
+ format_text/export.c \
+ format_text/flags.c \
+ format_text/format-text.c \
+ format_text/import.c \
+ format_text/import_vsn1.c \
+ format_text/tags.c \
+ format_text/text_label.c \
+ freeseg/freeseg.c \
+ label/label.c \
+ locking/file_locking.c \
+ locking/locking.c \
+ locking/no_locking.c \
+ log/log.c \
+ metadata/lv.c \
+ metadata/lv_manip.c \
+ metadata/merge.c \
+ metadata/metadata.c \
+ metadata/mirror.c \
+ metadata/pv.c \
+ metadata/pv_manip.c \
+ metadata/pv_map.c \
+ metadata/replicator_manip.c \
+ metadata/segtype.c \
+ metadata/snapshot_manip.c \
+ metadata/vg.c \
+ misc/crc.c \
+ misc/lvm-exec.c \
+ misc/lvm-file.c \
+ misc/lvm-globals.c \
+ misc/lvm-string.c \
+ misc/lvm-wrappers.c \
+ misc/lvm-percent.c \
+ misc/util.c \
+ mm/memlock.c \
+ report/properties.c \
+ report/report.c \
+ striped/striped.c \
+ uuid/uuid.c \
+ zero/zero.c
+
+ifeq ("@HAVE_REALTIME@", "yes")
+ SOURCES +=\
+ misc/timestamp.c
+endif
+
+ifeq ("@LVM1@", "internal")
+ SOURCES +=\
+ format1/disk-rep.c \
+ format1/format1.c \
+ format1/import-export.c \
+ format1/import-extents.c \
+ format1/layout.c \
+ format1/lvm1-label.c \
+ format1/vg_number.c
+endif
+
+ifeq ("@POOL@", "internal")
+ SOURCES +=\
+ format_pool/disk_rep.c \
+ format_pool/format_pool.c \
+ format_pool/import_export.c \
+ format_pool/pool_label.c
+endif
+
+ifeq ("@CLUSTER@", "internal")
+ SOURCES += locking/cluster_locking.c
+endif
+
+ifeq ("@CLUSTER@", "shared")
+ SUBDIRS += locking
+endif
+
+ifeq ("@SNAPSHOTS@", "internal")
+ SOURCES += snapshot/snapshot.c
+endif
+
+ifeq ("@MIRRORS@", "internal")
+ SOURCES += mirror/mirrored.c
+endif
+
+ifeq ("@REPLICATORS@", "internal")
+ SOURCES += replicator/replicator.c
+endif
+
+ifeq ("@DEVMAPPER@", "yes")
+ SOURCES +=\
+ activate/dev_manager.c \
+ activate/fs.c
+endif
+
+ifeq ("@HAVE_LIBDL@", "yes")
+ SOURCES +=\
+ locking/external_locking.c \
+ misc/sharedlib.c
+endif
+
+ifeq ("@DMEVENTD@", "yes")
+ CLDFLAGS += -L$(top_builddir)/daemons/dmeventd
+ LIBS += -ldevmapper-event
+endif
+
+LIB_NAME = liblvm-internal
+LIB_STATIC = $(LIB_NAME).a
+
+ifeq ($(MAKECMDGOALS),distclean)
+ SUBDIRS =\
+ format1 \
+ format_pool \
+ snapshot \
+ mirror \
+ replicator \
+ locking
+endif
+
+CFLOW_LIST = $(SOURCES)
+CFLOW_LIST_TARGET = $(LIB_NAME).cflow
+
+include $(top_builddir)/make.tmpl
+
+$(SUBDIRS): $(LIB_STATIC)
+
+DISTCLEAN_TARGETS += misc/configure.h misc/lvm-version.h
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "activate.h"
+#include "memlock.h"
+#include "display.h"
+#include "fs.h"
+#include "lvm-exec.h"
+#include "lvm-file.h"
+#include "lvm-string.h"
+#include "toolcontext.h"
+#include "dev_manager.h"
+#include "str_list.h"
+#include "config.h"
+#include "filter.h"
+#include "segtype.h"
+#include "sharedlib.h"
+
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define _skip(fmt, args...) log_very_verbose("Skipping: " fmt , ## args)
+
+int lvm1_present(struct cmd_context *cmd)
+{
+ char path[PATH_MAX];
+
+ if (dm_snprintf(path, sizeof(path), "%s/lvm/global", cmd->proc_dir)
+ < 0) {
+ log_error("LVM1 proc global snprintf failed");
+ return 0;
+ }
+
+ if (path_exists(path))
+ return 1;
+ else
+ return 0;
+}
+
+int list_segment_modules(struct dm_pool *mem, const struct lv_segment *seg,
+ struct dm_list *modules)
+{
+ unsigned int s;
+ struct lv_segment *seg2, *snap_seg;
+ struct dm_list *snh;
+
+ if (seg->segtype->ops->modules_needed &&
+ !seg->segtype->ops->modules_needed(mem, seg, modules)) {
+ log_error("module string allocation failed");
+ return 0;
+ }
+
+ if (lv_is_origin(seg->lv))
+ dm_list_iterate(snh, &seg->lv->snapshot_segs)
+ if (!list_lv_modules(mem,
+ dm_list_struct_base(snh,
+ struct lv_segment,
+ origin_list)->cow,
+ modules))
+ return_0;
+
+ if (lv_is_cow(seg->lv)) {
+ snap_seg = find_cow(seg->lv);
+ if (snap_seg->segtype->ops->modules_needed &&
+ !snap_seg->segtype->ops->modules_needed(mem, snap_seg,
+ modules)) {
+ log_error("snap_seg module string allocation failed");
+ return 0;
+ }
+ }
+
+ for (s = 0; s < seg->area_count; s++) {
+ switch (seg_type(seg, s)) {
+ case AREA_LV:
+ seg2 = find_seg_by_le(seg_lv(seg, s), seg_le(seg, s));
+ if (seg2 && !list_segment_modules(mem, seg2, modules))
+ return_0;
+ break;
+ case AREA_PV:
+ case AREA_UNASSIGNED:
+ ;
+ }
+ }
+
+ return 1;
+}
+
+int list_lv_modules(struct dm_pool *mem, const struct logical_volume *lv,
+ struct dm_list *modules)
+{
+ struct lv_segment *seg;
+
+ dm_list_iterate_items(seg, &lv->segments)
+ if (!list_segment_modules(mem, seg, modules))
+ return_0;
+
+ return 1;
+}
+
+#ifndef DEVMAPPER_SUPPORT
+void set_activation(int act)
+{
+ static int warned = 0;
+
+ if (warned || !act)
+ return;
+
+ log_error("Compiled without libdevmapper support. "
+ "Can't enable activation.");
+
+ warned = 1;
+}
+int activation(void)
+{
+ return 0;
+}
+int library_version(char *version, size_t size)
+{
+ return 0;
+}
+int driver_version(char *version, size_t size)
+{
+ return 0;
+}
+int target_version(const char *target_name, uint32_t *maj,
+ uint32_t *min, uint32_t *patchlevel)
+{
+ return 0;
+}
+int target_present(struct cmd_context *cmd, const char *target_name,
+ int use_modprobe)
+{
+ return 0;
+}
+int lv_info(struct cmd_context *cmd, const struct logical_volume *lv, unsigned origin_only,
+ struct lvinfo *info, int with_open_count, int with_read_ahead)
+{
+ return 0;
+}
+int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s,
+ unsigned origin_only,
+ struct lvinfo *info, int with_open_count, int with_read_ahead)
+{
+ return 0;
+}
+int lv_snapshot_percent(const struct logical_volume *lv, percent_t *percent)
+{
+ return 0;
+}
+int lv_mirror_percent(struct cmd_context *cmd, struct logical_volume *lv,
+ int wait, percent_t *percent, uint32_t *event_nr)
+{
+ return 0;
+}
+int lvs_in_vg_activated(struct volume_group *vg)
+{
+ return 0;
+}
+int lvs_in_vg_opened(struct volume_group *vg)
+{
+ return 0;
+}
+/******
+int lv_suspend(struct cmd_context *cmd, const char *lvid_s)
+{
+ return 1;
+}
+*******/
+int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s)
+{
+ return 1;
+}
+int lv_resume(struct cmd_context *cmd, const char *lvid_s)
+{
+ return 1;
+}
+int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s)
+{
+ return 1;
+}
+int lv_deactivate(struct cmd_context *cmd, const char *lvid_s)
+{
+ return 1;
+}
+int lv_activation_filter(struct cmd_context *cmd, const char *lvid_s,
+ int *activate_lv)
+{
+ return 1;
+}
+int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive)
+{
+ return 1;
+}
+int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive)
+{
+ return 1;
+}
+
+int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv)
+{
+ return 1;
+}
+
+int pv_uses_vg(struct physical_volume *pv,
+ struct volume_group *vg)
+{
+ return 0;
+}
+
+void activation_release(void)
+{
+ return;
+}
+
+void activation_exit(void)
+{
+ return;
+}
+
+#else /* DEVMAPPER_SUPPORT */
+
+static int _activation = 1;
+
+void set_activation(int act)
+{
+ if (act == _activation)
+ return;
+
+ _activation = act;
+ if (_activation)
+ log_verbose("Activation enabled. Device-mapper kernel "
+ "driver will be used.");
+ else
+ log_warn("WARNING: Activation disabled. No device-mapper "
+ "interaction will be attempted.");
+}
+
+int activation(void)
+{
+ return _activation;
+}
+
+static int _passes_activation_filter(struct cmd_context *cmd,
+ struct logical_volume *lv)
+{
+ const struct config_node *cn;
+ const struct config_value *cv;
+ const char *str;
+ char path[PATH_MAX];
+
+ if (!(cn = find_config_tree_node(cmd, "activation/volume_list"))) {
+ log_verbose("activation/volume_list configuration setting "
+ "not defined, checking only host tags for %s/%s",
+ lv->vg->name, lv->name);
+
+ /* If no host tags defined, activate */
+ if (dm_list_empty(&cmd->tags))
+ return 1;
+
+ /* If any host tag matches any LV or VG tag, activate */
+ if (str_list_match_list(&cmd->tags, &lv->tags, NULL) ||
+ str_list_match_list(&cmd->tags, &lv->vg->tags, NULL))
+ return 1;
+
+ log_verbose("No host tag matches %s/%s",
+ lv->vg->name, lv->name);
+
+ /* Don't activate */
+ return 0;
+ }
+ else
+ log_verbose("activation/volume_list configuration setting "
+ "defined, checking the list to match %s/%s",
+ lv->vg->name, lv->name);
+
+ for (cv = cn->v; cv; cv = cv->next) {
+ if (cv->type != CFG_STRING) {
+ log_error("Ignoring invalid string in config file "
+ "activation/volume_list");
+ continue;
+ }
+ str = cv->v.str;
+ if (!*str) {
+ log_error("Ignoring empty string in config file "
+ "activation/volume_list");
+ continue;
+ }
+
+
+ /* Tag? */
+ if (*str == '@') {
+ str++;
+ if (!*str) {
+ log_error("Ignoring empty tag in config file "
+ "activation/volume_list");
+ continue;
+ }
+ /* If any host tag matches any LV or VG tag, activate */
+ if (!strcmp(str, "*")) {
+ if (str_list_match_list(&cmd->tags, &lv->tags, NULL)
+ || str_list_match_list(&cmd->tags,
+ &lv->vg->tags, NULL))
+ return 1;
+ else
+ continue;
+ }
+ /* If supplied tag matches LV or VG tag, activate */
+ if (str_list_match_item(&lv->tags, str) ||
+ str_list_match_item(&lv->vg->tags, str))
+ return 1;
+ else
+ continue;
+ }
+ if (!strchr(str, '/')) {
+ /* vgname supplied */
+ if (!strcmp(str, lv->vg->name))
+ return 1;
+ else
+ continue;
+ }
+ /* vgname/lvname */
+ if (dm_snprintf(path, sizeof(path), "%s/%s", lv->vg->name,
+ lv->name) < 0) {
+ log_error("dm_snprintf error from %s/%s", lv->vg->name,
+ lv->name);
+ continue;
+ }
+ if (!strcmp(path, str))
+ return 1;
+ }
+
+ log_verbose("No item supplied in activation/volume_list configuration "
+ "setting matches %s/%s", lv->vg->name, lv->name);
+
+ return 0;
+}
+
+int library_version(char *version, size_t size)
+{
+ if (!activation())
+ return 0;
+
+ return dm_get_library_version(version, size);
+}
+
+int driver_version(char *version, size_t size)
+{
+ if (!activation())
+ return 0;
+
+ log_very_verbose("Getting driver version");
+
+ return dm_driver_version(version, size);
+}
+
+int target_version(const char *target_name, uint32_t *maj,
+ uint32_t *min, uint32_t *patchlevel)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ struct dm_versions *target, *last_target;
+
+ log_very_verbose("Getting target version for %s", target_name);
+ if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
+ return_0;
+
+ if (!dm_task_run(dmt)) {
+ log_debug("Failed to get %s target version", target_name);
+ /* Assume this was because LIST_VERSIONS isn't supported */
+ return 1;
+ }
+
+ target = dm_task_get_versions(dmt);
+
+ do {
+ last_target = target;
+
+ if (!strcmp(target_name, target->name)) {
+ r = 1;
+ *maj = target->version[0];
+ *min = target->version[1];
+ *patchlevel = target->version[2];
+ goto out;
+ }
+
+ target = (struct dm_versions *)((char *) target + target->next);
+ } while (last_target != target);
+
+ out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+int module_present(struct cmd_context *cmd, const char *target_name)
+{
+ int ret = 0;
+#ifdef MODPROBE_CMD
+ char module[128];
+ const char *argv[3];
+
+ if (dm_snprintf(module, sizeof(module), "dm-%s", target_name) < 0) {
+ log_error("module_present module name too long: %s",
+ target_name);
+ return 0;
+ }
+
+ argv[0] = MODPROBE_CMD;
+ argv[1] = module;
+ argv[2] = NULL;
+
+ ret = exec_cmd(cmd, argv, NULL);
+#endif
+ return ret;
+}
+
+int target_present(struct cmd_context *cmd, const char *target_name,
+ int use_modprobe)
+{
+ uint32_t maj, min, patchlevel;
+
+ if (!activation())
+ return 0;
+
+#ifdef MODPROBE_CMD
+ if (use_modprobe) {
+ if (target_version(target_name, &maj, &min, &patchlevel))
+ return 1;
+
+ if (!module_present(cmd, target_name))
+ return_0;
+ }
+#endif
+
+ return target_version(target_name, &maj, &min, &patchlevel);
+}
+
+/*
+ * Returns 1 if info structure populated, else 0 on failure.
+ */
+int lv_info(struct cmd_context *cmd, const struct logical_volume *lv, unsigned origin_only,
+ struct lvinfo *info, int with_open_count, int with_read_ahead)
+{
+ struct dm_info dminfo;
+
+ if (!activation())
+ return 0;
+
+ if (!dev_manager_info(lv->vg->cmd->mem, lv, origin_only ? "real" : NULL, with_open_count,
+ with_read_ahead, &dminfo, &info->read_ahead))
+ return_0;
+
+ info->exists = dminfo.exists;
+ info->suspended = dminfo.suspended;
+ info->open_count = dminfo.open_count;
+ info->major = dminfo.major;
+ info->minor = dminfo.minor;
+ info->read_only = dminfo.read_only;
+ info->live_table = dminfo.live_table;
+ info->inactive_table = dminfo.inactive_table;
+
+ return 1;
+}
+
+int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s,
+ unsigned origin_only,
+ struct lvinfo *info, int with_open_count, int with_read_ahead)
+{
+ int r;
+ struct logical_volume *lv;
+
+ if (!(lv = lv_from_lvid(cmd, lvid_s, 0)))
+ return 0;
+
+ if (!lv_is_origin(lv))
+ origin_only = 0;
+
+ r = lv_info(cmd, lv, origin_only, info, with_open_count, with_read_ahead);
+ free_vg(lv->vg);
+
+ return r;
+}
+
+/*
+ * Returns 1 if percent set, else 0 on failure.
+ */
+int lv_check_transient(struct logical_volume *lv)
+{
+ int r;
+ struct dev_manager *dm;
+
+ if (!activation())
+ return 0;
+
+ if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+ return_0;
+
+ if (!(r = dev_manager_transient(dm, lv)))
+ stack;
+
+ dev_manager_destroy(dm);
+
+ return r;
+}
+
+/*
+ * Returns 1 if percent set, else 0 on failure.
+ */
+int lv_snapshot_percent(const struct logical_volume *lv, percent_t *percent)
+{
+ int r;
+ struct dev_manager *dm;
+
+ if (!activation())
+ return 0;
+
+ if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+ return_0;
+
+ if (!(r = dev_manager_snapshot_percent(dm, lv, percent)))
+ stack;
+
+ dev_manager_destroy(dm);
+
+ return r;
+}
+
+/* FIXME Merge with snapshot_percent */
+int lv_mirror_percent(struct cmd_context *cmd, struct logical_volume *lv,
+ int wait, percent_t *percent, uint32_t *event_nr)
+{
+ int r;
+ struct dev_manager *dm;
+ struct lvinfo info;
+
+ /* If mirrored LV is temporarily shrinked to 1 area (= linear),
+ * it should be considered in-sync. */
+ if (dm_list_size(&lv->segments) == 1 && first_seg(lv)->area_count == 1) {
+ *percent = PERCENT_100;
+ return 1;
+ }
+
+ if (!activation())
+ return 0;
+
+ if (!lv_info(cmd, lv, 0, &info, 0, 0))
+ return_0;
+
+ if (!info.exists)
+ return 0;
+
+ if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+ return_0;
+
+ if (!(r = dev_manager_mirror_percent(dm, lv, wait, percent, event_nr)))
+ stack;
+
+ dev_manager_destroy(dm);
+
+ return r;
+}
+
+static int _lv_active(struct cmd_context *cmd, struct logical_volume *lv)
+{
+ struct lvinfo info;
+
+ if (!lv_info(cmd, lv, 0, &info, 0, 0)) {
+ stack;
+ return -1;
+ }
+
+ return info.exists;
+}
+
+static int _lv_open_count(struct cmd_context *cmd, struct logical_volume *lv)
+{
+ struct lvinfo info;
+
+ if (!lv_info(cmd, lv, 0, &info, 1, 0)) {
+ stack;
+ return -1;
+ }
+
+ return info.open_count;
+}
+
+static int _lv_activate_lv(struct logical_volume *lv, unsigned origin_only)
+{
+ int r;
+ struct dev_manager *dm;
+
+ if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+ return_0;
+
+ if (!(r = dev_manager_activate(dm, lv, origin_only)))
+ stack;
+
+ dev_manager_destroy(dm);
+ return r;
+}
+
+static int _lv_preload(struct logical_volume *lv, unsigned origin_only, int *flush_required)
+{
+ int r;
+ struct dev_manager *dm;
+
+ if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+ return_0;
+
+ if (!(r = dev_manager_preload(dm, lv, origin_only, flush_required)))
+ stack;
+
+ dev_manager_destroy(dm);
+ return r;
+}
+
+static int _lv_deactivate(struct logical_volume *lv)
+{
+ int r;
+ struct dev_manager *dm;
+
+ if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+ return_0;
+
+ if (!(r = dev_manager_deactivate(dm, lv)))
+ stack;
+
+ dev_manager_destroy(dm);
+ return r;
+}
+
+static int _lv_suspend_lv(struct logical_volume *lv, unsigned origin_only, int lockfs, int flush_required)
+{
+ int r;
+ struct dev_manager *dm;
+
+ if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name)))
+ return_0;
+
+ if (!(r = dev_manager_suspend(dm, lv, origin_only, lockfs, flush_required)))
+ stack;
+
+ dev_manager_destroy(dm);
+ return r;
+}
+
+/*
+ * These two functions return the number of visible LVs in the state,
+ * or -1 on error.
+ */
+int lvs_in_vg_activated(struct volume_group *vg)
+{
+ struct lv_list *lvl;
+ int count = 0;
+
+ if (!activation())
+ return 0;
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (lv_is_visible(lvl->lv))
+ count += (_lv_active(vg->cmd, lvl->lv) == 1);
+ }
+
+ return count;
+}
+
+int lvs_in_vg_opened(const struct volume_group *vg)
+{
+ const struct lv_list *lvl;
+ int count = 0;
+
+ if (!activation())
+ return 0;
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (lv_is_visible(lvl->lv))
+ count += (_lv_open_count(vg->cmd, lvl->lv) > 0);
+ }
+
+ return count;
+}
+
+/*
+ * Determine whether an LV is active locally or in a cluster.
+ * Assumes vg lock held.
+ * Returns:
+ * 0 - not active locally or on any node in cluster
+ * 1 - active either locally or some node in the cluster
+ */
+int lv_is_active(struct logical_volume *lv)
+{
+ int ret;
+
+ if (_lv_active(lv->vg->cmd, lv))
+ return 1;
+
+ if (!vg_is_clustered(lv->vg))
+ return 0;
+
+ if ((ret = remote_lock_held(lv->lvid.s)) >= 0)
+ return ret;
+
+ /*
+ * Old compatibility code if locking doesn't support lock query
+ * FIXME: check status to not deactivate already activate device
+ */
+ if (activate_lv_excl(lv->vg->cmd, lv)) {
+ if (!deactivate_lv(lv->vg->cmd, lv))
+ stack;
+ return 0;
+ }
+
+ /*
+ * Exclusive local activation failed so assume it is active elsewhere.
+ */
+ return 1;
+}
+
+#ifdef DMEVENTD
+static struct dm_event_handler *_create_dm_event_handler(struct cmd_context *cmd, const char *dmuuid, const char *dso,
+ const int timeout, enum dm_event_mask mask)
+{
+ struct dm_event_handler *dmevh;
+
+ if (!(dmevh = dm_event_handler_create()))
+ return_NULL;
+
+ if (dm_event_handler_set_dmeventd_path(dmevh, find_config_tree_str(cmd, "dmeventd/executable", NULL)))
+ goto_bad;
+
+ if (dm_event_handler_set_dso(dmevh, dso))
+ goto_bad;
+
+ if (dm_event_handler_set_uuid(dmevh, dmuuid))
+ goto_bad;
+
+ dm_event_handler_set_timeout(dmevh, timeout);
+ dm_event_handler_set_event_mask(dmevh, mask);
+
+ return dmevh;
+
+bad:
+ dm_event_handler_destroy(dmevh);
+ return NULL;
+}
+
+char *get_monitor_dso_path(struct cmd_context *cmd, const char *libpath)
+{
+ char *path;
+
+ if (!(path = dm_pool_alloc(cmd->mem, PATH_MAX))) {
+ log_error("Failed to allocate dmeventd library path.");
+ return NULL;
+ }
+
+ get_shared_library_path(cmd, libpath, path, PATH_MAX);
+
+ return path;
+}
+
+int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso,
+ struct logical_volume *lv, int *pending)
+{
+ char *uuid;
+ enum dm_event_mask evmask = 0;
+ struct dm_event_handler *dmevh;
+
+ *pending = 0;
+
+ if (!dso)
+ return_0;
+
+ /* We always monitor the "real" device, never the "snapshot-origin" itself. */
+ if (!(uuid = build_dm_uuid(cmd->mem, lv->lvid.s, lv_is_origin(lv) ? "real" : NULL)))
+ return_0;
+
+ if (!(dmevh = _create_dm_event_handler(cmd, uuid, dso, 0, DM_EVENT_ALL_ERRORS)))
+ return_0;
+
+ if (dm_event_get_registered_device(dmevh, 0)) {
+ dm_event_handler_destroy(dmevh);
+ return 0;
+ }
+
+ evmask = dm_event_handler_get_event_mask(dmevh);
+ if (evmask & DM_EVENT_REGISTRATION_PENDING) {
+ *pending = 1;
+ evmask &= ~DM_EVENT_REGISTRATION_PENDING;
+ }
+
+ dm_event_handler_destroy(dmevh);
+
+ return evmask;
+}
+
+int target_register_events(struct cmd_context *cmd, const char *dso, struct logical_volume *lv,
+ int evmask __attribute__((unused)), int set, int timeout)
+{
+ char *uuid;
+ struct dm_event_handler *dmevh;
+ int r;
+
+ if (!dso)
+ return_0;
+
+ /* We always monitor the "real" device, never the "snapshot-origin" itself. */
+ if (!(uuid = build_dm_uuid(cmd->mem, lv->lvid.s, lv_is_origin(lv) ? "real" : NULL)))
+ return_0;
+
+ if (!(dmevh = _create_dm_event_handler(cmd, uuid, dso, timeout,
+ DM_EVENT_ALL_ERRORS | (timeout ? DM_EVENT_TIMEOUT : 0))))
+ return_0;
+
+ r = set ? dm_event_register_handler(dmevh) : dm_event_unregister_handler(dmevh);
+
+ dm_event_handler_destroy(dmevh);
+
+ if (!r)
+ return_0;
+
+ log_info("%s %s for events", set ? "Monitored" : "Unmonitored", uuid);
+
+ return 1;
+}
+
+#endif
+
+/*
+ * Returns 0 if an attempt to (un)monitor the device failed.
+ * Returns 1 otherwise.
+ */
+int monitor_dev_for_events(struct cmd_context *cmd, struct logical_volume *lv,
+ unsigned origin_only, int monitor)
+{
+#ifdef DMEVENTD
+ int i, pending = 0, monitored;
+ int r = 1;
+ struct dm_list *tmp, *snh, *snht;
+ struct lv_segment *seg;
+ struct lv_segment *log_seg;
+ int (*monitor_fn) (struct lv_segment *s, int e);
+ uint32_t s;
+
+ /* skip dmeventd code altogether */
+ if (dmeventd_monitor_mode() == DMEVENTD_MONITOR_IGNORE)
+ return 1;
+
+ /*
+ * Nothing to do if dmeventd configured not to be used.
+ */
+ if (monitor && !dmeventd_monitor_mode())
+ return 1;
+
+ /*
+ * In case of a snapshot device, we monitor lv->snapshot->lv,
+ * not the actual LV itself.
+ */
+ if (lv_is_cow(lv) && !lv_is_merging_cow(lv))
+ return monitor_dev_for_events(cmd, lv->snapshot->lv, 0, monitor);
+
+ /*
+ * In case this LV is a snapshot origin, we instead monitor
+ * each of its respective snapshots. The origin itself may
+ * also need to be monitored if it is a mirror, for example.
+ */
+ if (!origin_only && lv_is_origin(lv))
+ dm_list_iterate_safe(snh, snht, &lv->snapshot_segs)
+ if (!monitor_dev_for_events(cmd, dm_list_struct_base(snh,
+ struct lv_segment, origin_list)->cow, 0, monitor))
+ r = 0;
+
+ /*
+ * If the volume is mirrored and its log is also mirrored, monitor
+ * the log volume as well.
+ */
+ if ((seg = first_seg(lv)) != NULL && seg->log_lv != NULL &&
+ (log_seg = first_seg(seg->log_lv)) != NULL &&
+ seg_is_mirrored(log_seg))
+ if (!monitor_dev_for_events(cmd, seg->log_lv, 0, monitor))
+ r = 0;
+
+ dm_list_iterate(tmp, &lv->segments) {
+ seg = dm_list_item(tmp, struct lv_segment);
+
+ /* Recurse for AREA_LV */
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) != AREA_LV)
+ continue;
+ if (!monitor_dev_for_events(cmd, seg_lv(seg, s), 0,
+ monitor)) {
+ log_error("Failed to %smonitor %s",
+ monitor ? "" : "un",
+ seg_lv(seg, s)->name);
+ r = 0;
+ }
+ }
+
+ if (!seg_monitored(seg) || (seg->status & PVMOVE))
+ continue;
+
+ monitor_fn = NULL;
+
+ /* Check monitoring status */
+ if (seg->segtype->ops->target_monitored)
+ monitored = seg->segtype->ops->target_monitored(seg, &pending);
+ else
+ continue; /* segtype doesn't support registration */
+
+ /*
+ * FIXME: We should really try again if pending
+ */
+ monitored = (pending) ? 0 : monitored;
+
+ if (monitor) {
+ if (monitored)
+ log_verbose("%s/%s already monitored.", lv->vg->name, lv->name);
+ else if (seg->segtype->ops->target_monitor_events)
+ monitor_fn = seg->segtype->ops->target_monitor_events;
+ } else {
+ if (!monitored)
+ log_verbose("%s/%s already not monitored.", lv->vg->name, lv->name);
+ else if (seg->segtype->ops->target_unmonitor_events)
+ monitor_fn = seg->segtype->ops->target_unmonitor_events;
+ }
+
+ /* Do [un]monitor */
+ if (!monitor_fn)
+ continue;
+
+ log_verbose("%sonitoring %s/%s%s", monitor ? "M" : "Not m", lv->vg->name, lv->name,
+ test_mode() ? " [Test mode: skipping this]" : "");
+
+ /* FIXME Test mode should really continue a bit further. */
+ if (test_mode())
+ continue;
+
+ /* FIXME specify events */
+ if (!monitor_fn(seg, 0)) {
+ log_error("%s/%s: %s segment monitoring function failed.",
+ lv->vg->name, lv->name, seg->segtype->name);
+ return 0;
+ }
+
+ /* Check [un]monitor results */
+ /* Try a couple times if pending, but not forever... */
+ for (i = 0; i < 10; i++) {
+ pending = 0;
+ monitored = seg->segtype->ops->target_monitored(seg, &pending);
+ if (pending ||
+ (!monitored && monitor) ||
+ (monitored && !monitor))
+ log_very_verbose("%s/%s %smonitoring still pending: waiting...",
+ lv->vg->name, lv->name, monitor ? "" : "un");
+ else
+ break;
+ sleep(1);
+ }
+
+ if (r)
+ r = (monitored && monitor) || (!monitored && !monitor);
+ }
+
+ return r;
+#else
+ return 1;
+#endif
+}
+
+static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s,
+ unsigned origin_only, int error_if_not_suspended)
+{
+ struct logical_volume *lv = NULL, *lv_pre = NULL;
+ struct lvinfo info;
+ int r = 0, lockfs = 0, flush_required = 0;
+
+ if (!activation())
+ return 1;
+
+ if (!(lv = lv_from_lvid(cmd, lvid_s, 0)))
+ goto_out;
+
+ /* Use precommitted metadata if present */
+ if (!(lv_pre = lv_from_lvid(cmd, lvid_s, 1)))
+ goto_out;
+
+ /* Ignore origin_only unless LV is origin in both old and new metadata */
+ if (!lv_is_origin(lv) || !lv_is_origin(lv_pre))
+ origin_only = 0;
+
+ if (test_mode()) {
+ _skip("Suspending %s%s.", lv->name, origin_only ? " origin without snapshots" : "");
+ r = 1;
+ goto out;
+ }
+
+ if (!lv_info(cmd, lv, origin_only, &info, 0, 0))
+ goto_out;
+
+ if (!info.exists || info.suspended) {
+ if (!error_if_not_suspended) {
+ r = 1;
+ if (info.suspended)
+ memlock_inc(cmd);
+ }
+ goto out;
+ }
+
+ if (!lv_read_replicator_vgs(lv))
+ goto_out;
+
+ lv_calculate_readahead(lv, NULL);
+
+ /* If VG was precommitted, preload devices for the LV */
+ if ((lv_pre->vg->status & PRECOMMITTED)) {
+ if (!_lv_preload(lv_pre, origin_only, &flush_required)) {
+ /* FIXME Revert preloading */
+ goto_out;
+ }
+ }
+
+ if (!monitor_dev_for_events(cmd, lv, origin_only, 0))
+ /* FIXME Consider aborting here */
+ stack;
+
+ memlock_inc(cmd);
+
+ if (!origin_only &&
+ (lv_is_origin(lv_pre) || lv_is_cow(lv_pre)))
+ lockfs = 1;
+
+ if (!_lv_suspend_lv(lv, origin_only, lockfs, flush_required)) {
+ memlock_dec(cmd);
+ fs_unlock();
+ goto out;
+ }
+
+ r = 1;
+out:
+ if (lv_pre)
+ free_vg(lv_pre->vg);
+ if (lv) {
+ lv_release_replicator_vgs(lv);
+ free_vg(lv->vg);
+ }
+
+ return r;
+}
+
+/* Returns success if the device is not active */
+int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only)
+{
+ return _lv_suspend(cmd, lvid_s, origin_only, 0);
+}
+
+/* No longer used */
+/***********
+int lv_suspend(struct cmd_context *cmd, const char *lvid_s)
+{
+ return _lv_suspend(cmd, lvid_s, 1);
+}
+***********/
+
+static int _lv_resume(struct cmd_context *cmd, const char *lvid_s,
+ unsigned origin_only,
+ int error_if_not_active)
+{
+ struct logical_volume *lv;
+ struct lvinfo info;
+ int r = 0;
+
+ if (!activation())
+ return 1;
+
+ if (!(lv = lv_from_lvid(cmd, lvid_s, 0)))
+ goto_out;
+
+ if (!lv_is_origin(lv))
+ origin_only = 0;
+
+ if (test_mode()) {
+ _skip("Resuming %s%s.", lv->name, origin_only ? " without snapshots" : "");
+ r = 1;
+ goto out;
+ }
+
+ if (!lv_info(cmd, lv, origin_only, &info, 0, 0))
+ goto_out;
+
+ if (!info.exists || !info.suspended) {
+ if (error_if_not_active)
+ goto_out;
+ r = 1;
+ goto out;
+ }
+
+ if (!_lv_activate_lv(lv, origin_only))
+ goto_out;
+
+ memlock_dec(cmd);
+ fs_unlock();
+
+ if (!monitor_dev_for_events(cmd, lv, origin_only, 1))
+ stack;
+
+ r = 1;
+out:
+ if (lv)
+ free_vg(lv->vg);
+
+ return r;
+}
+
+/* Returns success if the device is not active */
+int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only)
+{
+ return _lv_resume(cmd, lvid_s, origin_only, 0);
+}
+
+int lv_resume(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only)
+{
+ return _lv_resume(cmd, lvid_s, origin_only, 1);
+}
+
+static int _lv_has_open_snapshots(struct logical_volume *lv)
+{
+ struct lv_segment *snap_seg;
+ struct lvinfo info;
+ int r = 0;
+
+ dm_list_iterate_items_gen(snap_seg, &lv->snapshot_segs, origin_list) {
+ if (!lv_info(lv->vg->cmd, snap_seg->cow, 0, &info, 1, 0)) {
+ r = 1;
+ continue;
+ }
+
+ if (info.exists && info.open_count) {
+ log_error("LV %s/%s has open snapshot %s: "
+ "not deactivating", lv->vg->name, lv->name,
+ snap_seg->cow->name);
+ r = 1;
+ }
+ }
+
+ return r;
+}
+
+int lv_deactivate(struct cmd_context *cmd, const char *lvid_s)
+{
+ struct logical_volume *lv;
+ struct lvinfo info;
+ int r = 0;
+
+ if (!activation())
+ return 1;
+
+ if (!(lv = lv_from_lvid(cmd, lvid_s, 0)))
+ goto out;
+
+ if (test_mode()) {
+ _skip("Deactivating '%s'.", lv->name);
+ r = 1;
+ goto out;
+ }
+
+ if (!lv_info(cmd, lv, 0, &info, 1, 0))
+ goto_out;
+
+ if (!info.exists) {
+ r = 1;
+ goto out;
+ }
+
+ if (lv_is_visible(lv)) {
+ if (info.open_count) {
+ log_error("LV %s/%s in use: not deactivating",
+ lv->vg->name, lv->name);
+ goto out;
+ }
+ if (lv_is_origin(lv) && _lv_has_open_snapshots(lv))
+ goto_out;
+ }
+
+ if (!lv_read_replicator_vgs(lv))
+ goto_out;
+
+ lv_calculate_readahead(lv, NULL);
+
+ if (!monitor_dev_for_events(cmd, lv, 0, 0))
+ stack;
+
+ memlock_inc(cmd);
+ r = _lv_deactivate(lv);
+ memlock_dec(cmd);
+ fs_unlock();
+
+ if (!lv_info(cmd, lv, 0, &info, 1, 0) || info.exists)
+ r = 0;
+out:
+ if (lv) {
+ lv_release_replicator_vgs(lv);
+ free_vg(lv->vg);
+ }
+
+ return r;
+}
+
+/* Test if LV passes filter */
+int lv_activation_filter(struct cmd_context *cmd, const char *lvid_s,
+ int *activate_lv)
+{
+ struct logical_volume *lv;
+ int r = 0;
+
+ if (!activation()) {
+ *activate_lv = 1;
+ return 1;
+ }
+
+ if (!(lv = lv_from_lvid(cmd, lvid_s, 0)))
+ goto out;
+
+ if (!_passes_activation_filter(cmd, lv)) {
+ log_verbose("Not activating %s/%s since it does not pass "
+ "activation filter.", lv->vg->name, lv->name);
+ *activate_lv = 0;
+ } else
+ *activate_lv = 1;
+ r = 1;
+out:
+ if (lv)
+ free_vg(lv->vg);
+
+ return r;
+}
+
+static int _lv_activate(struct cmd_context *cmd, const char *lvid_s,
+ int exclusive, int filter)
+{
+ struct logical_volume *lv;
+ struct lvinfo info;
+ int r = 0;
+
+ if (!activation())
+ return 1;
+
+ if (!(lv = lv_from_lvid(cmd, lvid_s, 0)))
+ goto out;
+
+ if (filter && !_passes_activation_filter(cmd, lv)) {
+ log_error("Not activating %s/%s since it does not pass "
+ "activation filter.", lv->vg->name, lv->name);
+ goto out;
+ }
+
+ if ((!lv->vg->cmd->partial_activation) && (lv->status & PARTIAL_LV)) {
+ log_error("Refusing activation of partial LV %s. Use --partial to override.",
+ lv->name);
+ goto_out;
+ }
+
+ if (lv_has_unknown_segments(lv)) {
+ log_error("Refusing activation of LV %s containing "
+ "an unrecognised segment.", lv->name);
+ goto_out;
+ }
+
+ if (test_mode()) {
+ _skip("Activating '%s'.", lv->name);
+ r = 1;
+ goto out;
+ }
+
+ if (!lv_info(cmd, lv, 0, &info, 0, 0))
+ goto_out;
+
+ if (info.exists && !info.suspended && info.live_table) {
+ r = 1;
+ goto out;
+ }
+
+ if (!lv_read_replicator_vgs(lv))
+ goto_out;
+
+ lv_calculate_readahead(lv, NULL);
+
+ if (exclusive)
+ lv->status |= ACTIVATE_EXCL;
+
+ memlock_inc(cmd);
+ if (!(r = _lv_activate_lv(lv, 0)))
+ stack;
+ memlock_dec(cmd);
+ fs_unlock();
+
+ if (r && !monitor_dev_for_events(cmd, lv, 0, 1))
+ stack;
+
+out:
+ if (lv) {
+ lv_release_replicator_vgs(lv);
+ free_vg(lv->vg);
+ }
+
+ return r;
+}
+
+/* Activate LV */
+int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive)
+{
+ if (!_lv_activate(cmd, lvid_s, exclusive, 0))
+ return_0;
+
+ return 1;
+}
+
+/* Activate LV only if it passes filter */
+int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive)
+{
+ if (!_lv_activate(cmd, lvid_s, exclusive, 1))
+ return_0;
+
+ return 1;
+}
+
+int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv)
+{
+ int r = 1;
+
+ if (!lv) {
+ r = dm_mknodes(NULL);
+ fs_unlock();
+ return r;
+ }
+
+ if (!activation())
+ return 1;
+
+ r = dev_manager_mknodes(lv);
+
+ fs_unlock();
+
+ return r;
+}
+
+/*
+ * Does PV use VG somewhere in its construction?
+ * Returns 1 on failure.
+ */
+int pv_uses_vg(struct physical_volume *pv,
+ struct volume_group *vg)
+{
+ if (!activation())
+ return 0;
+
+ if (!dm_is_dm_major(MAJOR(pv->dev->dev)))
+ return 0;
+
+ return dev_manager_device_uses_vg(pv->dev, vg);
+}
+
+void activation_release(void)
+{
+ dev_manager_release();
+}
+
+void activation_exit(void)
+{
+ dev_manager_exit();
+}
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LVM_ACTIVATE_H
+#define LVM_ACTIVATE_H
+
+#include "metadata-exported.h"
+
+struct lvinfo {
+ int exists;
+ int suspended;
+ unsigned int open_count;
+ int major;
+ int minor;
+ int read_only;
+ int live_table;
+ int inactive_table;
+ uint32_t read_ahead;
+};
+
+/* target attribute flags */
+#define MIRROR_LOG_CLUSTERED 0x00000001U
+
+void set_activation(int activation);
+int activation(void);
+
+int driver_version(char *version, size_t size);
+int library_version(char *version, size_t size);
+int lvm1_present(struct cmd_context *cmd);
+
+int module_present(struct cmd_context *cmd, const char *target_name);
+int target_present(struct cmd_context *cmd, const char *target_name,
+ int use_modprobe);
+int target_version(const char *target_name, uint32_t *maj,
+ uint32_t *min, uint32_t *patchlevel);
+int list_segment_modules(struct dm_pool *mem, const struct lv_segment *seg,
+ struct dm_list *modules);
+int list_lv_modules(struct dm_pool *mem, const struct logical_volume *lv,
+ struct dm_list *modules);
+
+void activation_release(void);
+void activation_exit(void);
+
+/* int lv_suspend(struct cmd_context *cmd, const char *lvid_s); */
+int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only);
+int lv_resume(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only);
+int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only);
+int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive);
+int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s,
+ int exclusive);
+int lv_deactivate(struct cmd_context *cmd, const char *lvid_s);
+
+int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv);
+
+/*
+ * Returns 1 if info structure has been populated, else 0.
+ */
+int lv_info(struct cmd_context *cmd, const struct logical_volume *lv,
+ unsigned origin_only, struct lvinfo *info,
+ int with_open_count, int with_read_ahead);
+int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only,
+ struct lvinfo *info, int with_open_count, int with_read_ahead);
+
+/*
+ * Returns 1 if activate_lv has been set: 1 = activate; 0 = don't.
+ */
+int lv_activation_filter(struct cmd_context *cmd, const char *lvid_s,
+ int *activate_lv);
+
+int lv_check_transient(struct logical_volume *lv);
+/*
+ * Returns 1 if percent has been set, else 0.
+ */
+int lv_snapshot_percent(const struct logical_volume *lv, percent_t *percent);
+int lv_mirror_percent(struct cmd_context *cmd, struct logical_volume *lv,
+ int wait, percent_t *percent, uint32_t *event_nr);
+
+/*
+ * Return number of LVs in the VG that are active.
+ */
+int lvs_in_vg_activated(struct volume_group *vg);
+int lvs_in_vg_opened(const struct volume_group *vg);
+
+int lv_is_active(struct logical_volume *lv);
+
+int lv_has_target_type(struct dm_pool *mem, struct logical_volume *lv,
+ const char *layer, const char *target_type);
+
+int monitor_dev_for_events(struct cmd_context *cmd, struct logical_volume *lv,
+ unsigned origin_only, int do_reg);
+
+#ifdef DMEVENTD
+# include "libdevmapper-event.h"
+char *get_monitor_dso_path(struct cmd_context *cmd, const char *libpath);
+int target_registered_with_dmeventd(struct cmd_context *cmd, const char *libpath,
+ struct logical_volume *lv, int *pending);
+int target_register_events(struct cmd_context *cmd, const char *dso, struct logical_volume *lv,
+ int evmask __attribute__((unused)), int set, int timeout);
+#endif
+
+/*
+ * Returns 1 if PV has a dependency tree that uses anything in VG.
+ */
+int pv_uses_vg(struct physical_volume *pv,
+ struct volume_group *vg);
+
+/*
+ * Returns 1 if mapped device is not suspended.
+ */
+int device_is_usable(struct device *dev);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "str_list.h"
+#include "dev_manager.h"
+#include "lvm-string.h"
+#include "fs.h"
+#include "defaults.h"
+#include "segtype.h"
+#include "display.h"
+#include "toolcontext.h"
+#include "targets.h"
+#include "config.h"
+#include "filter.h"
+#include "activate.h"
+
+#include <limits.h>
+#include <dirent.h>
+
+#define MAX_TARGET_PARAMSIZE 50000
+
+typedef enum {
+ PRELOAD,
+ ACTIVATE,
+ DEACTIVATE,
+ SUSPEND,
+ SUSPEND_WITH_LOCKFS,
+ CLEAN
+} action_t;
+
+struct dev_manager {
+ struct dm_pool *mem;
+
+ struct cmd_context *cmd;
+
+ void *target_state;
+ uint32_t pvmove_mirror_count;
+ int flush_required;
+
+ char *vg_name;
+};
+
+struct lv_layer {
+ struct logical_volume *lv;
+ const char *old_name;
+};
+
+static int _read_only_lv(struct logical_volume *lv)
+{
+ return (!(lv->vg->status & LVM_WRITE) || !(lv->status & LVM_WRITE));
+}
+
+/*
+ * Low level device-layer operations.
+ */
+static struct dm_task *_setup_task(const char *name, const char *uuid,
+ uint32_t *event_nr, int task,
+ uint32_t major, uint32_t minor)
+{
+ struct dm_task *dmt;
+
+ if (!(dmt = dm_task_create(task)))
+ return_NULL;
+
+ if (name)
+ dm_task_set_name(dmt, name);
+
+ if (uuid && *uuid)
+ dm_task_set_uuid(dmt, uuid);
+
+ if (event_nr)
+ dm_task_set_event_nr(dmt, *event_nr);
+
+ if (major)
+ dm_task_set_major_minor(dmt, major, minor, 1);
+
+ return dmt;
+}
+
+static int _info_run(const char *name, const char *dlid, struct dm_info *info,
+ uint32_t *read_ahead, int mknodes, int with_open_count,
+ int with_read_ahead, uint32_t major, uint32_t minor)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ int dmtask;
+
+ dmtask = mknodes ? DM_DEVICE_MKNODES : DM_DEVICE_INFO;
+
+ if (!(dmt = _setup_task(mknodes ? name : NULL, dlid, 0, dmtask, major, minor)))
+ return_0;
+
+ if (!with_open_count)
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ if (!dm_task_run(dmt))
+ goto_out;
+
+ if (!dm_task_get_info(dmt, info))
+ goto_out;
+
+ if (with_read_ahead && info->exists) {
+ if (!dm_task_get_read_ahead(dmt, read_ahead))
+ goto_out;
+ } else if (read_ahead)
+ *read_ahead = DM_READ_AHEAD_NONE;
+
+ r = 1;
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+int device_is_usable(struct device *dev)
+{
+ struct dm_task *dmt;
+ struct dm_info info;
+ const char *name, *uuid;
+ uint64_t start, length;
+ char *target_type = NULL;
+ char *params, *vgname = NULL, *lvname, *layer;
+ void *next = NULL;
+ int only_error_target = 1;
+ int r = 0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_STATUS))) {
+ log_error("Failed to create dm_task struct to check dev status");
+ return 0;
+ }
+
+ if (!dm_task_set_major_minor(dmt, MAJOR(dev->dev), MINOR(dev->dev), 1))
+ goto_out;
+
+ if (!dm_task_run(dmt)) {
+ log_error("Failed to get state of mapped device");
+ goto out;
+ }
+
+ if (!dm_task_get_info(dmt, &info))
+ goto_out;
+
+ if (!info.exists)
+ goto out;
+
+ name = dm_task_get_name(dmt);
+ uuid = dm_task_get_uuid(dmt);
+
+ if (!info.target_count) {
+ log_debug("%s: Empty device %s not usable.", dev_name(dev), name);
+ goto out;
+ }
+
+ if (info.suspended && ignore_suspended_devices()) {
+ log_debug("%s: Suspended device %s not usable.", dev_name(dev), name);
+ goto out;
+ }
+
+ /* FIXME Also check for mirror block_on_error and mpath no paths */
+ /* For now, we exclude all mirrors */
+
+ do {
+ next = dm_get_next_target(dmt, next, &start, &length,
+ &target_type, ¶ms);
+ /* Skip if target type doesn't match */
+ if (target_type && !strcmp(target_type, "mirror") && ignore_suspended_devices()) {
+ log_debug("%s: Mirror device %s not usable.", dev_name(dev), name);
+ goto out;
+ }
+
+ /*
+ * Snapshot origin could be sitting on top of a mirror which
+ * could be blocking I/O. Skip snapshot origins entirely for
+ * now.
+ *
+ * FIXME: rather than skipping origin, check if mirror is
+ * underneath and if the mirror is blocking I/O.
+ */
+ if (target_type && !strcmp(target_type, "snapshot-origin") &&
+ ignore_suspended_devices()) {
+ log_debug("%s: Snapshot-origin device %s not usable.",
+ dev_name(dev), name);
+ goto out;
+ }
+
+ if (target_type && strcmp(target_type, "error"))
+ only_error_target = 0;
+ } while (next);
+
+ /* Skip devices consisting entirely of error targets. */
+ /* FIXME Deal with device stacked above error targets? */
+ if (only_error_target) {
+ log_debug("%s: Error device %s not usable.",
+ dev_name(dev), name);
+ goto out;
+ }
+
+ /* FIXME Also check dependencies? */
+
+ /* Check internal lvm devices */
+ if (uuid && !strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1)) {
+ if (!(vgname = dm_strdup(name)) ||
+ !dm_split_lvm_name(NULL, NULL, &vgname, &lvname, &layer))
+ goto_out;
+
+ if (lvname && (is_reserved_lvname(lvname) || *layer)) {
+ log_debug("%s: Reserved internal LV device %s/%s%s%s not usable.",
+ dev_name(dev), vgname, lvname, *layer ? "-" : "", layer);
+ goto out;
+ }
+ }
+
+ r = 1;
+
+ out:
+ dm_free(vgname);
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _info(const char *dlid, int with_open_count, int with_read_ahead,
+ struct dm_info *info, uint32_t *read_ahead)
+{
+ int r = 0;
+
+ if ((r = _info_run(NULL, dlid, info, read_ahead, 0, with_open_count,
+ with_read_ahead, 0, 0)) && info->exists)
+ return 1;
+ else if ((r = _info_run(NULL, dlid + sizeof(UUID_PREFIX) - 1, info,
+ read_ahead, 0, with_open_count,
+ with_read_ahead, 0, 0)) && info->exists)
+ return 1;
+
+ return r;
+}
+
+static int _info_by_dev(uint32_t major, uint32_t minor, struct dm_info *info)
+{
+ return _info_run(NULL, NULL, info, NULL, 0, 0, 0, major, minor);
+}
+
+int dev_manager_info(struct dm_pool *mem, const struct logical_volume *lv,
+ const char *layer,
+ int with_open_count, int with_read_ahead,
+ struct dm_info *info, uint32_t *read_ahead)
+{
+ char *dlid, *name;
+ int r;
+
+ if (!(name = build_dm_name(mem, lv->vg->name, lv->name, layer))) {
+ log_error("name build failed for %s", lv->name);
+ return 0;
+ }
+
+ if (!(dlid = build_dm_uuid(mem, lv->lvid.s, layer))) {
+ log_error("dlid build failed for %s", name);
+ return 0;
+ }
+
+ log_debug("Getting device info for %s [%s]", name, dlid);
+ r = _info(dlid, with_open_count, with_read_ahead, info, read_ahead);
+
+ dm_pool_free(mem, name);
+ return r;
+}
+
+static const struct dm_info *_cached_info(struct dm_pool *mem,
+ const struct logical_volume *lv,
+ struct dm_tree *dtree)
+{
+ const char *dlid;
+ struct dm_tree_node *dnode;
+ const struct dm_info *dinfo;
+
+ if (!(dlid = build_dm_uuid(mem, lv->lvid.s, NULL))) {
+ log_error("dlid build failed for %s", lv->name);
+ return NULL;
+ }
+
+ /* An activating merging origin won't have a node in the tree yet */
+ if (!(dnode = dm_tree_find_node_by_uuid(dtree, dlid)))
+ return NULL;
+
+ if (!(dinfo = dm_tree_node_get_info(dnode))) {
+ log_error("failed to get info from tree node for %s", lv->name);
+ return NULL;
+ }
+
+ if (!dinfo->exists)
+ return NULL;
+
+ return dinfo;
+}
+
+/* FIXME Interface must cope with multiple targets */
+static int _status_run(const char *name, const char *uuid,
+ unsigned long long *s, unsigned long long *l,
+ char **t, uint32_t t_size, char **p, uint32_t p_size)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ struct dm_info info;
+ void *next = NULL;
+ uint64_t start, length;
+ char *type = NULL;
+ char *params = NULL;
+
+ if (!(dmt = _setup_task(name, uuid, 0, DM_DEVICE_STATUS, 0, 0)))
+ return_0;
+
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ if (!dm_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,
+ &type, ¶ms);
+ if (type) {
+ *s = start;
+ *l = length;
+ /* Make sure things are null terminated */
+ strncpy(*t, type, t_size);
+ (*t)[t_size - 1] = '\0';
+ strncpy(*p, params, p_size);
+ (*p)[p_size - 1] = '\0';
+
+ r = 1;
+ /* FIXME Cope with multiple targets! */
+ break;
+ }
+
+ } while (next);
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _status(const char *name, const char *uuid,
+ unsigned long long *start, unsigned long long *length,
+ char **type, uint32_t type_size, char **params,
+ uint32_t param_size) __attribute__ ((unused));
+
+static int _status(const char *name, const char *uuid,
+ unsigned long long *start, unsigned long long *length,
+ char **type, uint32_t type_size, char **params,
+ uint32_t param_size)
+{
+ if (uuid && *uuid) {
+ if (_status_run(NULL, uuid, start, length, type,
+ type_size, params, param_size) &&
+ *params)
+ return 1;
+ else if (_status_run(NULL, uuid + sizeof(UUID_PREFIX) - 1, start,
+ length, type, type_size, params,
+ param_size) &&
+ *params)
+ return 1;
+ }
+
+ if (name && _status_run(name, NULL, start, length, type, type_size,
+ params, param_size))
+ return 1;
+
+ return 0;
+}
+
+int lv_has_target_type(struct dm_pool *mem, struct logical_volume *lv,
+ const char *layer, const char *target_type)
+{
+ int r = 0;
+ char *dlid;
+ struct dm_task *dmt;
+ struct dm_info info;
+ void *next = NULL;
+ uint64_t start, length;
+ char *type = NULL;
+ char *params = NULL;
+
+ if (!(dlid = build_dm_uuid(mem, lv->lvid.s, layer)))
+ return_0;
+
+ if (!(dmt = _setup_task(NULL, dlid, 0,
+ DM_DEVICE_STATUS, 0, 0)))
+ return_0;
+
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ if (!dm_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,
+ &type, ¶ms);
+ if (type && strncmp(type, target_type,
+ strlen(target_type)) == 0) {
+ if (info.live_table)
+ r = 1;
+ break;
+ }
+ } while (next);
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static percent_range_t _combine_percent(percent_t a, percent_t b,
+ uint32_t numerator, uint32_t denominator)
+{
+ if (a == PERCENT_INVALID || b == PERCENT_INVALID)
+ return PERCENT_INVALID;
+
+ if (a == PERCENT_100 && b == PERCENT_100)
+ return PERCENT_100;
+
+ if (a == PERCENT_0 && b == PERCENT_0)
+ return PERCENT_0;
+
+ return make_percent(numerator, denominator);
+}
+
+static int _percent_run(struct dev_manager *dm, const char *name,
+ const char *dlid,
+ const char *target_type, int wait,
+ const struct logical_volume *lv, percent_t *overall_percent,
+ uint32_t *event_nr, int fail_if_percent_unsupported)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ struct dm_info info;
+ void *next = NULL;
+ uint64_t start, length;
+ char *type = NULL;
+ char *params = NULL;
+ const struct dm_list *segh = &lv->segments;
+ struct lv_segment *seg = NULL;
+ struct segment_type *segtype;
+ int first_time = 1;
+ percent_t percent;
+
+ uint64_t total_numerator = 0, total_denominator = 0;
+
+ *overall_percent = PERCENT_INVALID;
+
+ if (!(dmt = _setup_task(name, dlid, event_nr,
+ wait ? DM_DEVICE_WAITEVENT : DM_DEVICE_STATUS, 0, 0)))
+ return_0;
+
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ if (!dm_task_run(dmt))
+ goto_out;
+
+ if (!dm_task_get_info(dmt, &info) || !info.exists)
+ goto_out;
+
+ if (event_nr)
+ *event_nr = info.event_nr;
+
+ do {
+ next = dm_get_next_target(dmt, next, &start, &length, &type,
+ ¶ms);
+ if (lv) {
+ if (!(segh = dm_list_next(&lv->segments, segh))) {
+ log_error("Number of segments in active LV %s "
+ "does not match metadata", lv->name);
+ goto out;
+ }
+ seg = dm_list_item(segh, struct lv_segment);
+ }
+
+ if (!type || !params)
+ continue;
+
+ if (!(segtype = get_segtype_from_string(dm->cmd, target_type)))
+ continue;
+
+ if (strcmp(type, target_type)) {
+ /* If kernel's type isn't an exact match is it compatible? */
+ if (!segtype->ops->target_status_compatible ||
+ !segtype->ops->target_status_compatible(type))
+ continue;
+ }
+
+ if (!segtype->ops->target_percent)
+ continue;
+
+ if (!segtype->ops->target_percent(&dm->target_state,
+ &percent, dm->mem,
+ dm->cmd, seg, params,
+ &total_numerator,
+ &total_denominator))
+ goto_out;
+
+ if (first_time) {
+ *overall_percent = percent;
+ first_time = 0;
+ } else
+ *overall_percent =
+ _combine_percent(*overall_percent, percent,
+ total_numerator, total_denominator);
+ } while (next);
+
+ if (lv && dm_list_next(&lv->segments, segh)) {
+ log_error("Number of segments in active LV %s does not "
+ "match metadata", lv->name);
+ goto out;
+ }
+
+ if (first_time) {
+ /* above ->target_percent() was not executed! */
+ /* FIXME why return PERCENT_100 et. al. in this case? */
+ *overall_percent = PERCENT_100;
+ if (fail_if_percent_unsupported)
+ goto_out;
+ }
+
+ log_debug("LV percent: %f", percent_to_float(*overall_percent));
+ r = 1;
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _percent(struct dev_manager *dm, const char *name, const char *dlid,
+ const char *target_type, int wait,
+ const struct logical_volume *lv, percent_t *percent,
+ uint32_t *event_nr, int fail_if_percent_unsupported)
+{
+ if (dlid && *dlid) {
+ if (_percent_run(dm, NULL, dlid, target_type, wait, lv, percent,
+ event_nr, fail_if_percent_unsupported))
+ return 1;
+ else if (_percent_run(dm, NULL, dlid + sizeof(UUID_PREFIX) - 1,
+ target_type, wait, lv, percent,
+ event_nr, fail_if_percent_unsupported))
+ return 1;
+ }
+
+ if (name && _percent_run(dm, name, NULL, target_type, wait, lv, percent,
+ event_nr, fail_if_percent_unsupported))
+ return 1;
+
+ return 0;
+}
+
+/* FIXME Merge with the percent function */
+int dev_manager_transient(struct dev_manager *dm, struct logical_volume *lv)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ struct dm_info info;
+ void *next = NULL;
+ uint64_t start, length;
+ char *type = NULL;
+ char *params = NULL;
+ char *dlid = NULL;
+ const char *layer = lv_is_origin(lv) ? "real" : NULL;
+ const struct dm_list *segh = &lv->segments;
+ struct lv_segment *seg = NULL;
+
+ if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer)))
+ return_0;
+
+ if (!(dmt = _setup_task(0, dlid, NULL, DM_DEVICE_STATUS, 0, 0)))
+ return_0;
+
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ if (!dm_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, &type,
+ ¶ms);
+
+ if (!(segh = dm_list_next(&lv->segments, segh))) {
+ log_error("Number of segments in active LV %s "
+ "does not match metadata", lv->name);
+ goto out;
+ }
+ seg = dm_list_item(segh, struct lv_segment);
+
+ if (!type || !params)
+ continue;
+
+ if (seg->segtype->ops->check_transient_status &&
+ !seg->segtype->ops->check_transient_status(seg, params))
+ goto_out;
+
+ } while (next);
+
+ if (dm_list_next(&lv->segments, segh)) {
+ log_error("Number of segments in active LV %s does not "
+ "match metadata", lv->name);
+ goto out;
+ }
+
+ r = 1;
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+/*
+ * dev_manager implementation.
+ */
+struct dev_manager *dev_manager_create(struct cmd_context *cmd,
+ const char *vg_name)
+{
+ struct dm_pool *mem;
+ struct dev_manager *dm;
+
+ if (!(mem = dm_pool_create("dev_manager", 16 * 1024)))
+ return_NULL;
+
+ if (!(dm = dm_pool_zalloc(mem, sizeof(*dm))))
+ goto_bad;
+
+ dm->cmd = cmd;
+ dm->mem = mem;
+
+ if (!(dm->vg_name = dm_pool_strdup(dm->mem, vg_name)))
+ goto_bad;
+
+ dm->target_state = NULL;
+
+ dm_udev_set_sync_support(cmd->current_settings.udev_sync);
+
+ return dm;
+
+ bad:
+ dm_pool_destroy(mem);
+ return NULL;
+}
+
+void dev_manager_destroy(struct dev_manager *dm)
+{
+ dm_pool_destroy(dm->mem);
+}
+
+void dev_manager_release(void)
+{
+ dm_lib_release();
+}
+
+void dev_manager_exit(void)
+{
+ dm_lib_exit();
+}
+
+int dev_manager_snapshot_percent(struct dev_manager *dm,
+ const struct logical_volume *lv,
+ percent_t *percent)
+{
+ char *name;
+ const char *dlid;
+ int fail_if_percent_unsupported = 0;
+
+ if (lv_is_merging_origin(lv)) {
+ /*
+ * Set 'fail_if_percent_unsupported', otherwise passing
+ * unsupported LV types to _percent will lead to a default
+ * successful return with percent_range as PERCENT_100.
+ * - For a merging origin, this will result in a polldaemon
+ * that runs infinitely (because completion is PERCENT_0)
+ * - We unfortunately don't yet _know_ if a snapshot-merge
+ * target is active (activation is deferred if dev is open);
+ * so we can't short-circuit origin devices based purely on
+ * existing LVM LV attributes.
+ */
+ fail_if_percent_unsupported = 1;
+ }
+
+ /*
+ * Build a name for the top layer.
+ */
+ if (!(name = build_dm_name(dm->mem, lv->vg->name, lv->name, NULL)))
+ return_0;
+
+ if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, NULL)))
+ return_0;
+
+ /*
+ * Try and get some info on this device.
+ */
+ log_debug("Getting device status percentage for %s", name);
+ if (!(_percent(dm, name, dlid, "snapshot", 0, NULL, percent,
+ NULL, fail_if_percent_unsupported)))
+ return_0;
+
+ /* FIXME dm_pool_free ? */
+
+ /* If the snapshot isn't available, percent will be -1 */
+ return 1;
+}
+
+/* FIXME Merge with snapshot_percent, auto-detecting target type */
+/* FIXME Cope with more than one target */
+int dev_manager_mirror_percent(struct dev_manager *dm,
+ const struct logical_volume *lv, int wait,
+ percent_t *percent, uint32_t *event_nr)
+{
+ char *name;
+ const char *dlid;
+ const char *layer = (lv_is_origin(lv)) ? "real" : NULL;
+
+ /*
+ * Build a name for the top layer.
+ */
+ if (!(name = build_dm_name(dm->mem, lv->vg->name, lv->name, layer)))
+ return_0;
+
+ /* FIXME dm_pool_free ? */
+
+ if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer))) {
+ log_error("dlid build failed for %s", lv->name);
+ return 0;
+ }
+
+ log_debug("Getting device mirror status percentage for %s", name);
+ if (!(_percent(dm, name, dlid, "mirror", wait, lv, percent,
+ event_nr, 0)))
+ return_0;
+
+ return 1;
+}
+
+#if 0
+ log_very_verbose("%s %s", sus ? "Suspending" : "Resuming", name);
+
+ log_verbose("Loading %s", dl->name);
+ log_very_verbose("Activating %s read-only", dl->name);
+ log_very_verbose("Activated %s %s %03u:%03u", dl->name,
+ dl->dlid, dl->info.major, dl->info.minor);
+
+ if (_get_flag(dl, VISIBLE))
+ log_verbose("Removing %s", dl->name);
+ else
+ log_very_verbose("Removing %s", dl->name);
+
+ log_debug("Adding target: %" PRIu64 " %" PRIu64 " %s %s",
+ extent_size * seg->le, extent_size * seg->len, target, params);
+
+ log_debug("Adding target: 0 %" PRIu64 " snapshot-origin %s",
+ dl->lv->size, params);
+ log_debug("Adding target: 0 %" PRIu64 " snapshot %s", size, params);
+ log_debug("Getting device info for %s", dl->name);
+
+ /* Rename? */
+ if ((suffix = strrchr(dl->dlid + sizeof(UUID_PREFIX) - 1, '-')))
+ suffix++;
+ new_name = build_dm_name(dm->mem, dm->vg_name, dl->lv->name,
+ suffix);
+
+static int _belong_to_vg(const char *vgname, const char *name)
+{
+ const char *v = vgname, *n = name;
+
+ while (*v) {
+ if ((*v != *n) || (*v == '-' && *(++n) != '-'))
+ return 0;
+ v++, n++;
+ }
+
+ if (*n == '-' && *(n + 1) != '-')
+ return 1;
+ else
+ return 0;
+}
+
+ if (!(snap_seg = find_cow(lv)))
+ return 1;
+
+ old_origin = snap_seg->origin;
+
+ /* Was this the last active snapshot with this origin? */
+ dm_list_iterate_items(lvl, active_head) {
+ active = lvl->lv;
+ if ((snap_seg = find_cow(active)) &&
+ snap_seg->origin == old_origin) {
+ return 1;
+ }
+ }
+
+#endif
+
+/*************************/
+/* NEW CODE STARTS HERE */
+/*************************/
+
+static int _dev_manager_lv_mknodes(const struct logical_volume *lv)
+{
+ char *name;
+
+ if (!(name = build_dm_name(lv->vg->cmd->mem, lv->vg->name,
+ lv->name, NULL)))
+ return_0;
+
+ return fs_add_lv(lv, name);
+}
+
+static int _dev_manager_lv_rmnodes(const struct logical_volume *lv)
+{
+ return fs_del_lv(lv);
+}
+
+int dev_manager_mknodes(const struct logical_volume *lv)
+{
+ struct dm_info dminfo;
+ char *name;
+ int r = 0;
+
+ if (!(name = build_dm_name(lv->vg->cmd->mem, lv->vg->name, lv->name, NULL)))
+ return_0;
+
+ if ((r = _info_run(name, NULL, &dminfo, NULL, 1, 0, 0, 0, 0))) {
+ if (dminfo.exists) {
+ if (lv_is_visible(lv))
+ r = _dev_manager_lv_mknodes(lv);
+ } else
+ r = _dev_manager_lv_rmnodes(lv);
+ }
+
+ dm_pool_free(lv->vg->cmd->mem, name);
+ return r;
+}
+
+static uint16_t _get_udev_flags(struct dev_manager *dm, struct logical_volume *lv,
+ const char *layer)
+{
+ uint16_t udev_flags = 0;
+
+ /*
+ * Is this top-level and visible device?
+ * If not, create just the /dev/mapper content.
+ */
+ if (layer || !lv_is_visible(lv))
+ udev_flags |= DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG |
+ DM_UDEV_DISABLE_DISK_RULES_FLAG |
+ DM_UDEV_DISABLE_OTHER_RULES_FLAG;
+ /*
+ * There's no need for other udev rules to touch special LVs with
+ * reserved names. We don't need to populate /dev/disk here either.
+ * Even if they happen to be visible and top-level.
+ */
+ else if (is_reserved_lvname(lv->name))
+ udev_flags |= DM_UDEV_DISABLE_DISK_RULES_FLAG |
+ DM_UDEV_DISABLE_OTHER_RULES_FLAG;
+
+ /*
+ * Snapshots and origins could have the same rule applied that will
+ * give symlinks exactly the same name (e.g. a name based on
+ * filesystem UUID). We give preference to origins to make such
+ * naming deterministic (e.g. symlinks in /dev/disk/by-uuid).
+ */
+ if (lv_is_cow(lv))
+ udev_flags |= DM_UDEV_LOW_PRIORITY_FLAG;
+
+ /*
+ * Finally, add flags to disable /dev/mapper and /dev/<vgname> content
+ * to be created by udev if it is requested by user's configuration.
+ * This is basically an explicit fallback to old node/symlink creation
+ * without udev.
+ */
+ if (!dm->cmd->current_settings.udev_rules)
+ udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG |
+ DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG;
+
+ return udev_flags;
+}
+
+static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
+ struct logical_volume *lv, const char *layer)
+{
+ char *dlid, *name;
+ struct dm_info info, info2;
+
+ if (!(name = build_dm_name(dm->mem, lv->vg->name, lv->name, layer)))
+ return_0;
+
+ if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer)))
+ return_0;
+
+ log_debug("Getting device info for %s [%s]", name, dlid);
+ if (!_info(dlid, 1, 0, &info, NULL)) {
+ log_error("Failed to get info for %s [%s].", name, dlid);
+ return 0;
+ }
+
+ /*
+ * For top level volumes verify that existing device match
+ * requested major/minor and that major/minor pair is available for use
+ */
+ if (!layer && lv->major != -1 && lv->minor != -1) {
+ /*
+ * FIXME compare info.major with lv->major if multiple major support
+ */
+ if (info.exists && (info.minor != lv->minor)) {
+ log_error("Volume %s (%" PRIu32 ":%" PRIu32")"
+ " differs from already active device "
+ "(%" PRIu32 ":%" PRIu32")",
+ lv->name, lv->major, lv->minor, info.major, info.minor);
+ return 0;
+ }
+ if (!info.exists && _info_by_dev(lv->major, lv->minor, &info2) &&
+ info2.exists) {
+ log_error("The requested major:minor pair "
+ "(%" PRIu32 ":%" PRIu32") is already used",
+ lv->major, lv->minor);
+ return 0;
+ }
+ }
+
+ if (info.exists && !dm_tree_add_dev_with_udev_flags(dtree, info.major, info.minor,
+ _get_udev_flags(dm, lv, layer))) {
+ log_error("Failed to add device (%" PRIu32 ":%" PRIu32") to dtree",
+ info.major, info.minor);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Add replicator devices
+ *
+ * Using _add_dev_to_dtree() directly instead of _add_lv_to_dtree()
+ * to avoid extra checks with extensions.
+ */
+static int _add_partial_replicator_to_dtree(struct dev_manager *dm,
+ struct dm_tree *dtree,
+ struct logical_volume *lv)
+{
+ struct logical_volume *rlv = first_seg(lv)->replicator;
+ struct replicator_device *rdev;
+ struct replicator_site *rsite;
+ struct dm_tree_node *rep_node, *rdev_node;
+ const char *uuid;
+
+ if (!lv_is_active_replicator_dev(lv)) {
+ if (!_add_dev_to_dtree(dm, dtree, lv->rdevice->lv,
+ NULL))
+ return_0;
+ return 1;
+ }
+
+ /* Add _rlog and replicator device */
+ if (!_add_dev_to_dtree(dm, dtree, first_seg(rlv)->rlog_lv, NULL))
+ return_0;
+
+ if (!_add_dev_to_dtree(dm, dtree, rlv, NULL))
+ return_0;
+
+ if (!(uuid = build_dm_uuid(dm->mem, rlv->lvid.s, NULL)))
+ return_0;
+
+ rep_node = dm_tree_find_node_by_uuid(dtree, uuid);
+
+ /* Add all related devices for replicator */
+ dm_list_iterate_items(rsite, &rlv->rsites)
+ dm_list_iterate_items(rdev, &rsite->rdevices) {
+ if (rsite->state == REPLICATOR_STATE_ACTIVE) {
+ /* Add _rimage LV */
+ if (!_add_dev_to_dtree(dm, dtree, rdev->lv, NULL))
+ return_0;
+
+ /* Add replicator-dev LV, except of the already added one */
+ if ((lv != rdev->replicator_dev->lv) &&
+ !_add_dev_to_dtree(dm, dtree,
+ rdev->replicator_dev->lv, NULL))
+ return_0;
+
+ /* If replicator exists - try connect existing heads */
+ if (rep_node) {
+ uuid = build_dm_uuid(dm->mem,
+ rdev->replicator_dev->lv->lvid.s,
+ NULL);
+ if (!uuid)
+ return_0;
+
+ rdev_node = dm_tree_find_node_by_uuid(dtree, uuid);
+ if (rdev_node)
+ dm_tree_node_set_presuspend_node(rdev_node,
+ rep_node);
+ }
+ }
+
+ if (!rdev->rsite->vg_name)
+ continue;
+
+ if (!_add_dev_to_dtree(dm, dtree, rdev->lv, NULL))
+ return_0;
+
+ if (rdev->slog &&
+ !_add_dev_to_dtree(dm, dtree, rdev->slog, NULL))
+ return_0;
+ }
+
+ return 1;
+}
+
+/*
+ * Add LV and any known dependencies
+ */
+static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, struct logical_volume *lv, unsigned origin_only)
+{
+ if (!origin_only && !_add_dev_to_dtree(dm, dtree, lv, NULL))
+ return_0;
+
+ /* FIXME Can we avoid doing this every time? */
+ if (!_add_dev_to_dtree(dm, dtree, lv, "real"))
+ return_0;
+
+ if (!origin_only && !_add_dev_to_dtree(dm, dtree, lv, "cow"))
+ return_0;
+
+ if ((lv->status & MIRRORED) && first_seg(lv)->log_lv &&
+ !_add_dev_to_dtree(dm, dtree, first_seg(lv)->log_lv, NULL))
+ return_0;
+
+ /* Adding LV head of replicator adds all other related devs */
+ if (lv_is_replicator_dev(lv) &&
+ !_add_partial_replicator_to_dtree(dm, dtree, lv))
+ return_0;
+
+ return 1;
+}
+
+static struct dm_tree *_create_partial_dtree(struct dev_manager *dm, struct logical_volume *lv, unsigned origin_only)
+{
+ struct dm_tree *dtree;
+ struct dm_list *snh, *snht;
+ struct lv_segment *seg;
+ uint32_t s;
+
+ if (!(dtree = dm_tree_create())) {
+ log_error("Partial dtree creation failed for %s.", lv->name);
+ return NULL;
+ }
+
+ if (!_add_lv_to_dtree(dm, dtree, lv, origin_only))
+ goto_bad;
+
+ /* Add any snapshots of this LV */
+ if (!origin_only)
+ dm_list_iterate_safe(snh, snht, &lv->snapshot_segs)
+ if (!_add_lv_to_dtree(dm, dtree, dm_list_struct_base(snh, struct lv_segment, origin_list)->cow, 0))
+ goto_bad;
+
+ /* Add any LVs used by segments in this LV */
+ dm_list_iterate_items(seg, &lv->segments)
+ for (s = 0; s < seg->area_count; s++)
+ if (seg_type(seg, s) == AREA_LV && seg_lv(seg, s)) {
+ if (!_add_lv_to_dtree(dm, dtree, seg_lv(seg, s), 0))
+ goto_bad;
+ }
+
+ return dtree;
+
+bad:
+ dm_tree_free(dtree);
+ return NULL;
+}
+
+static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree,
+ struct lv_segment *seg, int s)
+{
+ char *id, *name;
+ char errid[32];
+ struct dm_tree_node *node;
+ struct lv_segment *seg_i;
+ int segno = -1, i = 0;;
+ uint64_t size = seg->len * seg->lv->vg->extent_size;
+
+ dm_list_iterate_items(seg_i, &seg->lv->segments) {
+ if (seg == seg_i)
+ segno = i;
+ ++i;
+ }
+
+ if (segno < 0) {
+ log_error("_add_error_device called with bad segment");
+ return_NULL;
+ }
+
+ sprintf(errid, "missing_%d_%d", segno, s);
+
+ if (!(id = build_dm_uuid(dm->mem, seg->lv->lvid.s, errid)))
+ return_NULL;
+
+ if (!(name = build_dm_name(dm->mem, seg->lv->vg->name,
+ seg->lv->name, errid)))
+ return_NULL;
+ if (!(node = dm_tree_add_new_dev(dtree, name, id, 0, 0, 0, 0, 0)))
+ return_NULL;
+ if (!dm_tree_node_add_error_target(node, size))
+ return_NULL;
+
+ return id;
+}
+
+static int _add_error_area(struct dev_manager *dm, struct dm_tree_node *node,
+ struct lv_segment *seg, int s)
+{
+ char *dlid;
+ uint64_t extent_size = seg->lv->vg->extent_size;
+
+ if (!strcmp(dm->cmd->stripe_filler, "error")) {
+ /*
+ * FIXME, the tree pointer is first field of dm_tree_node, but
+ * we don't have the struct definition available.
+ */
+ struct dm_tree **tree = (struct dm_tree **) node;
+ dlid = _add_error_device(dm, *tree, seg, s);
+ if (!dlid)
+ return_0;
+ dm_tree_node_add_target_area(node, NULL, dlid,
+ extent_size * seg_le(seg, s));
+ } else
+ dm_tree_node_add_target_area(node,
+ dm->cmd->stripe_filler,
+ NULL, UINT64_C(0));
+
+ return 1;
+}
+
+int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
+ struct dm_tree_node *node, uint32_t start_area,
+ uint32_t areas)
+{
+ uint64_t extent_size = seg->lv->vg->extent_size;
+ uint32_t s;
+ char *dlid;
+
+ for (s = start_area; s < areas; s++) {
+ if ((seg_type(seg, s) == AREA_PV &&
+ (!seg_pvseg(seg, s) ||
+ !seg_pv(seg, s) ||
+ !seg_dev(seg, s))) ||
+ (seg_type(seg, s) == AREA_LV && !seg_lv(seg, s))) {
+ if (!_add_error_area(dm, node, seg, s))
+ return_0;
+ } else if (seg_type(seg, s) == AREA_PV)
+ dm_tree_node_add_target_area(node,
+ dev_name(seg_dev(seg, s)),
+ NULL,
+ (seg_pv(seg, s)->pe_start +
+ (extent_size * seg_pe(seg, s))));
+ else if (seg_type(seg, s) == AREA_LV) {
+ if (!(dlid = build_dm_uuid(dm->mem,
+ seg_lv(seg, s)->lvid.s,
+ NULL)))
+ return_0;
+ dm_tree_node_add_target_area(node, NULL, dlid,
+ extent_size * seg_le(seg, s));
+ } else {
+ log_error(INTERNAL_ERROR "Unassigned area found in LV %s.",
+ seg->lv->name);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int _add_origin_target_to_dtree(struct dev_manager *dm,
+ struct dm_tree_node *dnode,
+ struct logical_volume *lv)
+{
+ const char *real_dlid;
+
+ if (!(real_dlid = build_dm_uuid(dm->mem, lv->lvid.s, "real")))
+ return_0;
+
+ if (!dm_tree_node_add_snapshot_origin_target(dnode, lv->size, real_dlid))
+ return_0;
+
+ return 1;
+}
+
+static int _add_snapshot_merge_target_to_dtree(struct dev_manager *dm,
+ struct dm_tree_node *dnode,
+ struct logical_volume *lv)
+{
+ const char *origin_dlid, *cow_dlid, *merge_dlid;
+ struct lv_segment *merging_cow_seg = find_merging_cow(lv);
+
+ if (!(origin_dlid = build_dm_uuid(dm->mem, lv->lvid.s, "real")))
+ return_0;
+
+ if (!(cow_dlid = build_dm_uuid(dm->mem, merging_cow_seg->cow->lvid.s, "cow")))
+ return_0;
+
+ if (!(merge_dlid = build_dm_uuid(dm->mem, merging_cow_seg->cow->lvid.s, NULL)))
+ return_0;
+
+ if (!dm_tree_node_add_snapshot_merge_target(dnode, lv->size, origin_dlid,
+ cow_dlid, merge_dlid,
+ merging_cow_seg->chunk_size))
+ return_0;
+
+ return 1;
+}
+
+static int _add_snapshot_target_to_dtree(struct dev_manager *dm,
+ struct dm_tree_node *dnode,
+ struct logical_volume *lv)
+{
+ const char *origin_dlid;
+ const char *cow_dlid;
+ struct lv_segment *snap_seg;
+ uint64_t size;
+
+ if (!(snap_seg = find_cow(lv))) {
+ log_error("Couldn't find snapshot for '%s'.", lv->name);
+ return 0;
+ }
+
+ if (!(origin_dlid = build_dm_uuid(dm->mem, snap_seg->origin->lvid.s, "real")))
+ return_0;
+
+ if (!(cow_dlid = build_dm_uuid(dm->mem, snap_seg->cow->lvid.s, "cow")))
+ return_0;
+
+ size = (uint64_t) snap_seg->len * snap_seg->origin->vg->extent_size;
+
+ if (lv_is_merging_cow(lv)) {
+ /* cow is to be merged so load the error target */
+ if (!dm_tree_node_add_error_target(dnode, size))
+ return_0;
+ }
+ else if (!dm_tree_node_add_snapshot_target(dnode, size, origin_dlid,
+ cow_dlid, 1, snap_seg->chunk_size))
+ return_0;
+
+ return 1;
+}
+
+static int _add_target_to_dtree(struct dev_manager *dm,
+ struct dm_tree_node *dnode,
+ struct lv_segment *seg)
+{
+ uint64_t extent_size = seg->lv->vg->extent_size;
+
+ if (!seg->segtype->ops->add_target_line) {
+ log_error(INTERNAL_ERROR "_emit_target cannot handle "
+ "segment type %s", seg->segtype->name);
+ return 0;
+ }
+
+ return seg->segtype->ops->add_target_line(dm, dm->mem, dm->cmd,
+ &dm->target_state, seg,
+ dnode,
+ extent_size * seg->len,
+ &dm-> pvmove_mirror_count);
+}
+
+static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
+ struct logical_volume *lv, const char *layer);
+
+/* Add all replicators' LVs */
+static int _add_replicator_dev_target_to_dtree(struct dev_manager *dm,
+ struct dm_tree *dtree,
+ struct lv_segment *seg)
+{
+ struct replicator_device *rdev;
+ struct replicator_site *rsite;
+
+ /* For inactive replicator add linear mapping */
+ if (!lv_is_active_replicator_dev(seg->lv)) {
+ if (!_add_new_lv_to_dtree(dm, dtree, seg->lv->rdevice->lv, NULL))
+ return_0;
+ return 1;
+ }
+
+ /* Add rlog and replicator nodes */
+ if (!seg->replicator ||
+ !first_seg(seg->replicator)->rlog_lv ||
+ !_add_new_lv_to_dtree(dm, dtree,
+ first_seg(seg->replicator)->rlog_lv, NULL) ||
+ !_add_new_lv_to_dtree(dm, dtree, seg->replicator, NULL))
+ return_0;
+
+ /* Activation of one replicator_dev node activates all other nodes */
+ dm_list_iterate_items(rsite, &seg->replicator->rsites) {
+ dm_list_iterate_items(rdev, &rsite->rdevices) {
+ if (rdev->lv &&
+ !_add_new_lv_to_dtree(dm, dtree, rdev->lv, NULL))
+ return_0;
+
+ if (rdev->slog &&
+ !_add_new_lv_to_dtree(dm, dtree,
+ rdev->slog, NULL))
+ return_0;
+ }
+ }
+ /* Add remaining replicator-dev nodes in the second loop
+ * to avoid multiple retries for inserting all elements */
+ dm_list_iterate_items(rsite, &seg->replicator->rsites) {
+ if (rsite->state != REPLICATOR_STATE_ACTIVE)
+ continue;
+ dm_list_iterate_items(rdev, &rsite->rdevices) {
+ if (rdev->replicator_dev->lv == seg->lv)
+ continue;
+ if (!rdev->replicator_dev->lv ||
+ !_add_new_lv_to_dtree(dm, dtree,
+ rdev->replicator_dev->lv,
+ NULL))
+ return_0;
+ }
+ }
+
+ return 1;
+}
+
+static int _add_segment_to_dtree(struct dev_manager *dm,
+ struct dm_tree *dtree,
+ struct dm_tree_node *dnode,
+ struct lv_segment *seg,
+ const char *layer)
+{
+ uint32_t s;
+ struct dm_list *snh;
+ struct lv_segment *seg_present;
+ const char *target_name;
+
+ /* Ensure required device-mapper targets are loaded */
+ seg_present = find_cow(seg->lv) ? : seg;
+ target_name = (seg_present->segtype->ops->target_name ?
+ seg_present->segtype->ops->target_name(seg_present) :
+ seg_present->segtype->name);
+
+ log_debug("Checking kernel supports %s segment type for %s%s%s",
+ target_name, seg->lv->name,
+ layer ? "-" : "", layer ? : "");
+
+ if (seg_present->segtype->ops->target_present &&
+ !seg_present->segtype->ops->target_present(seg_present->lv->vg->cmd,
+ seg_present, NULL)) {
+ log_error("Can't process LV %s: %s target support missing "
+ "from kernel?", seg->lv->name, target_name);
+ return 0;
+ }
+
+ /* Add mirror log */
+ if (seg->log_lv &&
+ !_add_new_lv_to_dtree(dm, dtree, seg->log_lv, NULL))
+ return_0;
+
+ if (seg_is_replicator_dev(seg)) {
+ if (!_add_replicator_dev_target_to_dtree(dm, dtree, seg))
+ return_0;
+ /* If this is a snapshot origin, add real LV */
+ /* If this is a snapshot origin + merging snapshot, add cow + real LV */
+ } else if (lv_is_origin(seg->lv) && !layer) {
+ if (vg_is_clustered(seg->lv->vg)) {
+ log_error("Clustered snapshots are not yet supported");
+ return 0;
+ }
+ if (lv_is_merging_origin(seg->lv)) {
+ if (!_add_new_lv_to_dtree(dm, dtree,
+ find_merging_cow(seg->lv)->cow, "cow"))
+ return_0;
+ /*
+ * Must also add "real" LV for use when
+ * snapshot-merge target is added
+ */
+ }
+ if (!_add_new_lv_to_dtree(dm, dtree, seg->lv, "real"))
+ return_0;
+ } else if (lv_is_cow(seg->lv) && !layer) {
+ if (!_add_new_lv_to_dtree(dm, dtree, seg->lv, "cow"))
+ return_0;
+ } else {
+ /* Add any LVs used by this segment */
+ for (s = 0; s < seg->area_count; s++)
+ if ((seg_type(seg, s) == AREA_LV) &&
+ (!_add_new_lv_to_dtree(dm, dtree, seg_lv(seg, s),
+ NULL)))
+ return_0;
+ }
+
+ /* Now we've added its dependencies, we can add the target itself */
+ if (lv_is_origin(seg->lv) && !layer) {
+ if (!lv_is_merging_origin(seg->lv)) {
+ if (!_add_origin_target_to_dtree(dm, dnode, seg->lv))
+ return_0;
+ } else {
+ if (!_add_snapshot_merge_target_to_dtree(dm, dnode, seg->lv))
+ return_0;
+ }
+ } else if (lv_is_cow(seg->lv) && !layer) {
+ if (!_add_snapshot_target_to_dtree(dm, dnode, seg->lv))
+ return_0;
+ } else if (!_add_target_to_dtree(dm, dnode, seg))
+ return_0;
+
+ if (lv_is_origin(seg->lv) && !layer)
+ /* Add any snapshots of this LV */
+ dm_list_iterate(snh, &seg->lv->snapshot_segs)
+ if (!_add_new_lv_to_dtree(dm, dtree, dm_list_struct_base(snh, struct lv_segment, origin_list)->cow, NULL))
+ return_0;
+
+ return 1;
+}
+
+static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
+ struct logical_volume *lv, const char *layer)
+{
+ struct lv_segment *seg;
+ struct lv_layer *lvlayer;
+ struct dm_tree_node *dnode;
+ const struct dm_info *dinfo;
+ char *name, *dlid;
+ uint32_t max_stripe_size = UINT32_C(0);
+ uint32_t read_ahead = lv->read_ahead;
+ uint32_t read_ahead_flags = UINT32_C(0);
+
+ /* FIXME Seek a simpler way to lay out the snapshot-merge tree. */
+
+ if (lv_is_origin(lv) && lv_is_merging_origin(lv) && !layer) {
+ /*
+ * Clear merge attributes if merge isn't currently possible:
+ * either origin or merging snapshot are open
+ * - but use "snapshot-merge" if it is already in use
+ * - open_count is always retrieved (as of dm-ioctl 4.7.0)
+ * so just use the tree's existing nodes' info
+ */
+ if (((dinfo = _cached_info(dm->mem, lv,
+ dtree)) && dinfo->open_count) ||
+ ((dinfo = _cached_info(dm->mem, find_merging_cow(lv)->cow,
+ dtree)) && dinfo->open_count)) {
+ /* FIXME Is there anything simpler to check for instead? */
+ if (!lv_has_target_type(dm->mem, lv, NULL, "snapshot-merge"))
+ clear_snapshot_merge(lv);
+ }
+ }
+
+ if (!(name = build_dm_name(dm->mem, lv->vg->name, lv->name, layer)))
+ return_0;
+
+ if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer)))
+ return_0;
+
+ /* We've already processed this node if it already has a context ptr */
+ if ((dnode = dm_tree_find_node_by_uuid(dtree, dlid)) &&
+ dm_tree_node_get_context(dnode))
+ return 1;
+
+ if (!(lvlayer = dm_pool_alloc(dm->mem, sizeof(*lvlayer)))) {
+ log_error("_add_new_lv_to_dtree: pool alloc failed for %s %s.",
+ lv->name, layer);
+ return 0;
+ }
+
+ lvlayer->lv = lv;
+
+ /*
+ * Add LV to dtree.
+ * If we're working with precommitted metadata, clear any
+ * existing inactive table left behind.
+ * Major/minor settings only apply to the visible layer.
+ */
+ if (!(dnode = dm_tree_add_new_dev_with_udev_flags(dtree, name, dlid,
+ layer ? UINT32_C(0) : (uint32_t) lv->major,
+ layer ? UINT32_C(0) : (uint32_t) lv->minor,
+ _read_only_lv(lv),
+ (lv->vg->status & PRECOMMITTED) ? 1 : 0,
+ lvlayer,
+ _get_udev_flags(dm, lv, layer))))
+ return_0;
+
+ /* Store existing name so we can do rename later */
+ lvlayer->old_name = dm_tree_node_get_name(dnode);
+
+ /* Create table */
+ dm->pvmove_mirror_count = 0u;
+ dm_list_iterate_items(seg, &lv->segments) {
+ if (!_add_segment_to_dtree(dm, dtree, dnode, seg, layer))
+ return_0;
+ /* These aren't real segments in the LVM2 metadata */
+ if (lv_is_origin(lv) && !layer)
+ break;
+ if (lv_is_cow(lv) && !layer)
+ break;
+ if (max_stripe_size < seg->stripe_size * seg->area_count)
+ max_stripe_size = seg->stripe_size * seg->area_count;
+ }
+
+ if (read_ahead == DM_READ_AHEAD_AUTO) {
+ /* we need RA at least twice a whole stripe - see the comment in md/raid0.c */
+ read_ahead = max_stripe_size * 2;
+ if (!read_ahead)
+ lv_calculate_readahead(lv, &read_ahead);
+ read_ahead_flags = DM_READ_AHEAD_MINIMUM_FLAG;
+ }
+
+ dm_tree_node_set_read_ahead(dnode, read_ahead, read_ahead_flags);
+
+ return 1;
+}
+
+/* FIXME: symlinks should be created/destroyed at the same time
+ * as the kernel devices but we can't do that from within libdevmapper
+ * at present so we must walk the tree twice instead. */
+
+/*
+ * Create LV symlinks for children of supplied root node.
+ */
+static int _create_lv_symlinks(struct dev_manager *dm, struct dm_tree_node *root)
+{
+ void *handle = NULL;
+ struct dm_tree_node *child;
+ struct lv_layer *lvlayer;
+ char *old_vgname, *old_lvname, *old_layer;
+ char *new_vgname, *new_lvname, *new_layer;
+ const char *name;
+ int r = 1;
+
+ while ((child = dm_tree_next_child(&handle, root, 0))) {
+ if (!(lvlayer = dm_tree_node_get_context(child)))
+ continue;
+
+ /* Detect rename */
+ name = dm_tree_node_get_name(child);
+
+ if (name && lvlayer->old_name && *lvlayer->old_name && strcmp(name, lvlayer->old_name)) {
+ if (!dm_split_lvm_name(dm->mem, lvlayer->old_name, &old_vgname, &old_lvname, &old_layer)) {
+ log_error("_create_lv_symlinks: Couldn't split up old device name %s", lvlayer->old_name);
+ return 0;
+ }
+ if (!dm_split_lvm_name(dm->mem, name, &new_vgname, &new_lvname, &new_layer)) {
+ log_error("_create_lv_symlinks: Couldn't split up new device name %s", name);
+ return 0;
+ }
+ if (!fs_rename_lv(lvlayer->lv, name, old_vgname, old_lvname))
+ r = 0;
+ continue;
+ }
+ if (lv_is_visible(lvlayer->lv)) {
+ if (!_dev_manager_lv_mknodes(lvlayer->lv))
+ r = 0;
+ continue;
+ }
+ if (!_dev_manager_lv_rmnodes(lvlayer->lv))
+ r = 0;
+ }
+
+ return r;
+}
+
+/*
+ * Remove LV symlinks for children of supplied root node.
+ */
+static int _remove_lv_symlinks(struct dev_manager *dm, struct dm_tree_node *root)
+{
+ void *handle = NULL;
+ struct dm_tree_node *child;
+ char *vgname, *lvname, *layer;
+ int r = 1;
+
+ while ((child = dm_tree_next_child(&handle, root, 0))) {
+ if (!dm_split_lvm_name(dm->mem, dm_tree_node_get_name(child), &vgname, &lvname, &layer)) {
+ r = 0;
+ continue;
+ }
+
+ if (!*vgname)
+ continue;
+
+ /* only top level layer has symlinks */
+ if (*layer)
+ continue;
+
+ fs_del_lv_byname(dm->cmd->dev_dir, vgname, lvname,
+ dm->cmd->current_settings.udev_rules);
+ }
+
+ return r;
+}
+
+static int _clean_tree(struct dev_manager *dm, struct dm_tree_node *root, char *non_toplevel_tree_dlid)
+{
+ void *handle = NULL;
+ struct dm_tree_node *child;
+ char *vgname, *lvname, *layer;
+ const char *name, *uuid;
+ int r;
+
+ while ((child = dm_tree_next_child(&handle, root, 0))) {
+ if (!(name = dm_tree_node_get_name(child)))
+ continue;
+
+ if (!(uuid = dm_tree_node_get_uuid(child)))
+ continue;
+
+ if (!dm_split_lvm_name(dm->mem, name, &vgname, &lvname, &layer)) {
+ log_error("_clean_tree: Couldn't split up device name %s.", name);
+ return 0;
+ }
+
+ /* Not meant to be top level? */
+ if (!*layer)
+ continue;
+
+ /* If operation was performed on a partial tree, don't remove it */
+ if (non_toplevel_tree_dlid && !strcmp(non_toplevel_tree_dlid, uuid))
+ continue;
+
+ dm_tree_set_cookie(root, 0);
+ r = dm_tree_deactivate_children(root, uuid, strlen(uuid));
+ if (!dm_udev_wait(dm_tree_get_cookie(root)))
+ stack;
+
+ if (!r)
+ return_0;
+ }
+
+ return 1;
+}
+
+static int _tree_action(struct dev_manager *dm, struct logical_volume *lv,
+ unsigned origin_only, action_t action)
+{
+ struct dm_tree *dtree;
+ struct dm_tree_node *root;
+ char *dlid;
+ int r = 0;
+
+ if (!(dtree = _create_partial_dtree(dm, lv, origin_only)))
+ return_0;
+
+ if (!(root = dm_tree_find_node(dtree, 0, 0))) {
+ log_error("Lost dependency tree root node");
+ goto out;
+ }
+
+ if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, origin_only ? "real" : NULL)))
+ goto_out;
+
+ /* Only process nodes with uuid of "LVM-" plus VG id. */
+ switch(action) {
+ case CLEAN:
+ /* Deactivate any unused non-toplevel nodes */
+ if (!_clean_tree(dm, root, origin_only ? dlid : NULL))
+ goto_out;
+ break;
+ case DEACTIVATE:
+ /* Deactivate LV and all devices it references that nothing else has open. */
+ dm_tree_set_cookie(root, 0);
+ r = dm_tree_deactivate_children(root, dlid, ID_LEN + sizeof(UUID_PREFIX) - 1);
+ if (!dm_udev_wait(dm_tree_get_cookie(root)))
+ stack;
+ if (!r)
+ goto_out;
+ if (!_remove_lv_symlinks(dm, root))
+ log_error("Failed to remove all device symlinks associated with %s.", lv->name);
+ break;
+ case SUSPEND:
+ dm_tree_skip_lockfs(root);
+ if (!dm->flush_required && (lv->status & MIRRORED) && !(lv->status & PVMOVE))
+ dm_tree_use_no_flush_suspend(root);
+ case SUSPEND_WITH_LOCKFS:
+ if (!dm_tree_suspend_children(root, dlid, ID_LEN + sizeof(UUID_PREFIX) - 1))
+ goto_out;
+ break;
+ case PRELOAD:
+ case ACTIVATE:
+ /* Add all required new devices to tree */
+ if (!_add_new_lv_to_dtree(dm, dtree, lv, origin_only ? "real" : NULL))
+ goto_out;
+
+ /* Preload any devices required before any suspensions */
+ dm_tree_set_cookie(root, 0);
+ r = dm_tree_preload_children(root, dlid, ID_LEN + sizeof(UUID_PREFIX) - 1);
+ if (!dm_udev_wait(dm_tree_get_cookie(root)))
+ stack;
+ if (!r)
+ goto_out;
+
+ if (dm_tree_node_size_changed(root))
+ dm->flush_required = 1;
+
+ if (action == ACTIVATE) {
+ dm_tree_set_cookie(root, 0);
+ r = dm_tree_activate_children(root, dlid, ID_LEN + sizeof(UUID_PREFIX) - 1);
+ if (!dm_udev_wait(dm_tree_get_cookie(root)))
+ stack;
+ if (!r)
+ goto_out;
+ if (!_create_lv_symlinks(dm, root)) {
+ log_error("Failed to create symlinks for %s.", lv->name);
+ goto out;
+ }
+ }
+
+ break;
+ default:
+ log_error("_tree_action: Action %u not supported.", action);
+ goto out;
+ }
+
+ r = 1;
+
+out:
+ dm_tree_free(dtree);
+
+ return r;
+}
+
+/* origin_only may only be set if we are resuming (not activating) an origin LV */
+int dev_manager_activate(struct dev_manager *dm, struct logical_volume *lv, unsigned origin_only)
+{
+ if (!_tree_action(dm, lv, origin_only, ACTIVATE))
+ return_0;
+
+ return _tree_action(dm, lv, origin_only, CLEAN);
+}
+
+/* origin_only may only be set if we are resuming (not activating) an origin LV */
+int dev_manager_preload(struct dev_manager *dm, struct logical_volume *lv,
+ unsigned origin_only, int *flush_required)
+{
+ /* FIXME Update the pvmove implementation! */
+ if ((lv->status & PVMOVE) || (lv->status & LOCKED))
+ return 1;
+
+ if (!_tree_action(dm, lv, origin_only, PRELOAD))
+ return 0;
+
+ *flush_required = dm->flush_required;
+
+ return 1;
+}
+
+int dev_manager_deactivate(struct dev_manager *dm, struct logical_volume *lv)
+{
+ int r;
+
+ r = _tree_action(dm, lv, 0, DEACTIVATE);
+
+ return r;
+}
+
+int dev_manager_suspend(struct dev_manager *dm, struct logical_volume *lv,
+ unsigned origin_only, int lockfs, int flush_required)
+{
+ dm->flush_required = flush_required;
+
+ return _tree_action(dm, lv, origin_only, lockfs ? SUSPEND_WITH_LOCKFS : SUSPEND);
+}
+
+/*
+ * Does device use VG somewhere in its construction?
+ * Returns 1 if uncertain.
+ */
+int dev_manager_device_uses_vg(struct device *dev,
+ struct volume_group *vg)
+{
+ struct dm_tree *dtree;
+ struct dm_tree_node *root;
+ char dlid[sizeof(UUID_PREFIX) + sizeof(struct id) - 1] __attribute__((aligned(8)));
+ int r = 1;
+
+ if (!(dtree = dm_tree_create())) {
+ log_error("partial dtree creation failed");
+ return r;
+ }
+
+ if (!dm_tree_add_dev(dtree, (uint32_t) MAJOR(dev->dev), (uint32_t) MINOR(dev->dev))) {
+ log_error("Failed to add device %s (%" PRIu32 ":%" PRIu32") to dtree",
+ dev_name(dev), (uint32_t) MAJOR(dev->dev), (uint32_t) MINOR(dev->dev));
+ goto out;
+ }
+
+ memcpy(dlid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1);
+ memcpy(dlid + sizeof(UUID_PREFIX) - 1, &vg->id.uuid[0], sizeof(vg->id));
+
+ if (!(root = dm_tree_find_node(dtree, 0, 0))) {
+ log_error("Lost dependency tree root node");
+ goto out;
+ }
+
+ if (dm_tree_children_use_uuid(root, dlid, sizeof(UUID_PREFIX) + sizeof(vg->id) - 1))
+ goto_out;
+
+ r = 0;
+
+out:
+ dm_tree_free(dtree);
+ return r;
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_DEV_MANAGER_H
+#define _LVM_DEV_MANAGER_H
+
+#include "metadata-exported.h"
+
+struct logical_volume;
+struct volume_group;
+struct cmd_context;
+struct dev_manager;
+struct dm_info;
+struct device;
+
+/*
+ * Constructor and destructor.
+ */
+struct dev_manager *dev_manager_create(struct cmd_context *cmd,
+ const char *vg_name);
+void dev_manager_destroy(struct dev_manager *dm);
+void dev_manager_release(void);
+void dev_manager_exit(void);
+
+/*
+ * The device handler is responsible for creating all the layered
+ * dm devices, and ensuring that all constraints are maintained
+ * (eg, an origin is created before its snapshot, but is not
+ * unsuspended until the snapshot is also created.)
+ */
+int dev_manager_info(struct dm_pool *mem, const struct logical_volume *lv,
+ const char *layer,
+ int with_open_count, int with_read_ahead,
+ struct dm_info *info, uint32_t *read_ahead);
+int dev_manager_snapshot_percent(struct dev_manager *dm,
+ const struct logical_volume *lv,
+ percent_t *percent);
+int dev_manager_mirror_percent(struct dev_manager *dm,
+ const struct logical_volume *lv, int wait,
+ percent_t *percent, uint32_t *event_nr);
+int dev_manager_suspend(struct dev_manager *dm, struct logical_volume *lv,
+ unsigned origin_only, int lockfs, int flush_required);
+int dev_manager_activate(struct dev_manager *dm, struct logical_volume *lv, unsigned origin_only);
+int dev_manager_preload(struct dev_manager *dm, struct logical_volume *lv,
+ unsigned origin_only, int *flush_required);
+int dev_manager_deactivate(struct dev_manager *dm, struct logical_volume *lv);
+int dev_manager_transient(struct dev_manager *dm, struct logical_volume *lv) __attribute__((nonnull(1, 2)));
+
+int dev_manager_mknodes(const struct logical_volume *lv);
+
+/*
+ * Put the desired changes into effect.
+ */
+int dev_manager_execute(struct dev_manager *dm);
+
+int dev_manager_device_uses_vg(struct device *dev,
+ struct volume_group *vg);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "fs.h"
+#include "toolcontext.h"
+#include "lvm-string.h"
+#include "lvm-file.h"
+#include "memlock.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <dirent.h>
+
+static int _mk_dir(const char *dev_dir, const char *vg_name)
+{
+ char vg_path[PATH_MAX];
+ mode_t old_umask;
+
+ if (dm_snprintf(vg_path, sizeof(vg_path), "%s%s",
+ dev_dir, vg_name) == -1) {
+ log_error("Couldn't construct name of volume "
+ "group directory.");
+ return 0;
+ }
+
+ if (dir_exists(vg_path))
+ return 1;
+
+ log_very_verbose("Creating directory %s", vg_path);
+
+ (void) dm_prepare_selinux_context(vg_path, S_IFDIR);
+ old_umask = umask(DM_DEV_DIR_UMASK);
+ if (mkdir(vg_path, 0777)) {
+ log_sys_error("mkdir", vg_path);
+ umask(old_umask);
+ (void) dm_prepare_selinux_context(NULL, 0);
+ return 0;
+ }
+ umask(old_umask);
+ (void) dm_prepare_selinux_context(NULL, 0);
+
+ return 1;
+}
+
+static int _rm_dir(const char *dev_dir, const char *vg_name)
+{
+ char vg_path[PATH_MAX];
+
+ if (dm_snprintf(vg_path, sizeof(vg_path), "%s%s",
+ dev_dir, vg_name) == -1) {
+ log_error("Couldn't construct name of volume "
+ "group directory.");
+ return 0;
+ }
+
+ if (dir_exists(vg_path) && is_empty_dir(vg_path)) {
+ log_very_verbose("Removing directory %s", vg_path);
+ rmdir(vg_path);
+ }
+
+ return 1;
+}
+
+static void _rm_blks(const char *dir)
+{
+ const char *name;
+ char path[PATH_MAX];
+ struct dirent *dirent;
+ struct stat buf;
+ DIR *d;
+
+ if (!(d = opendir(dir))) {
+ log_sys_error("opendir", dir);
+ return;
+ }
+
+ while ((dirent = readdir(d))) {
+ name = dirent->d_name;
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ continue;
+
+ if (dm_snprintf(path, sizeof(path), "%s/%s", dir, name) == -1) {
+ log_error("Couldn't create path for %s", name);
+ continue;
+ }
+
+ if (!lstat(path, &buf)) {
+ if (!S_ISBLK(buf.st_mode))
+ continue;
+ log_very_verbose("Removing %s", path);
+ if (unlink(path) < 0)
+ log_sys_error("unlink", path);
+ }
+ }
+
+ if (closedir(d))
+ log_sys_error("closedir", dir);
+}
+
+static int _mk_link(const char *dev_dir, const char *vg_name,
+ const char *lv_name, const char *dev, int check_udev)
+{
+ char lv_path[PATH_MAX], link_path[PATH_MAX], lvm1_group_path[PATH_MAX];
+ char vg_path[PATH_MAX];
+ struct stat buf, buf_lp;
+
+ if (dm_snprintf(vg_path, sizeof(vg_path), "%s%s",
+ dev_dir, vg_name) == -1) {
+ log_error("Couldn't create path for volume group dir %s",
+ vg_name);
+ return 0;
+ }
+
+ if (dm_snprintf(lv_path, sizeof(lv_path), "%s/%s", vg_path,
+ lv_name) == -1) {
+ log_error("Couldn't create source pathname for "
+ "logical volume link %s", lv_name);
+ return 0;
+ }
+
+ if (dm_snprintf(link_path, sizeof(link_path), "%s/%s",
+ dm_dir(), dev) == -1) {
+ log_error("Couldn't create destination pathname for "
+ "logical volume link for %s", lv_name);
+ return 0;
+ }
+
+ if (dm_snprintf(lvm1_group_path, sizeof(lvm1_group_path), "%s/group",
+ vg_path) == -1) {
+ log_error("Couldn't create pathname for LVM1 group file for %s",
+ vg_name);
+ return 0;
+ }
+
+ /* To reach this point, the VG must have been locked.
+ * As locking fails if the VG is active under LVM1, it's
+ * now safe to remove any LVM1 devices we find here
+ * (as well as any existing LVM2 symlink). */
+ if (!lstat(lvm1_group_path, &buf)) {
+ if (!S_ISCHR(buf.st_mode)) {
+ log_error("Non-LVM1 character device found at %s",
+ lvm1_group_path);
+ } else {
+ _rm_blks(vg_path);
+
+ log_very_verbose("Removing %s", lvm1_group_path);
+ if (unlink(lvm1_group_path) < 0)
+ log_sys_error("unlink", lvm1_group_path);
+ }
+ }
+
+ if (!lstat(lv_path, &buf)) {
+ if (!S_ISLNK(buf.st_mode) && !S_ISBLK(buf.st_mode)) {
+ log_error("Symbolic link %s not created: file exists",
+ link_path);
+ return 0;
+ }
+
+ if (dm_udev_get_sync_support() && udev_checking() && check_udev) {
+ /* Check udev created the correct link. */
+ if (!stat(link_path, &buf_lp) &&
+ !stat(lv_path, &buf)) {
+ if (buf_lp.st_rdev == buf.st_rdev)
+ return 1;
+ else
+ log_warn("Symlink %s that should have been "
+ "created by udev does not have "
+ "correct target. Falling back to "
+ "direct link creation", lv_path);
+ } else
+ log_warn("Symlink %s that should have been "
+ "created by udev could not be checked "
+ "for its correctness. Falling back to "
+ "direct link creation.", lv_path);
+
+ }
+
+ log_very_verbose("Removing %s", lv_path);
+ if (unlink(lv_path) < 0) {
+ log_sys_error("unlink", lv_path);
+ return 0;
+ }
+ } else if (dm_udev_get_sync_support() && udev_checking() && check_udev)
+ log_warn("The link %s should had been created by udev "
+ "but it was not found. Falling back to "
+ "direct link creation.", lv_path);
+
+ log_very_verbose("Linking %s -> %s", lv_path, link_path);
+
+ (void) dm_prepare_selinux_context(lv_path, S_IFLNK);
+ if (symlink(link_path, lv_path) < 0) {
+ log_sys_error("symlink", lv_path);
+ (void) dm_prepare_selinux_context(NULL, 0);
+ return 0;
+ }
+ (void) dm_prepare_selinux_context(NULL, 0);
+
+ return 1;
+}
+
+static int _rm_link(const char *dev_dir, const char *vg_name,
+ const char *lv_name, int check_udev)
+{
+ struct stat buf;
+ char lv_path[PATH_MAX];
+
+ if (dm_snprintf(lv_path, sizeof(lv_path), "%s%s/%s",
+ dev_dir, vg_name, lv_name) == -1) {
+ log_error("Couldn't determine link pathname.");
+ return 0;
+ }
+
+ if (lstat(lv_path, &buf) && errno == ENOENT)
+ return 1;
+ else if (dm_udev_get_sync_support() && udev_checking() && check_udev)
+ log_warn("The link %s should have been removed by udev "
+ "but it is still present. Falling back to "
+ "direct link removal.", lv_path);
+
+ if (!S_ISLNK(buf.st_mode)) {
+ log_error("%s not symbolic link - not removing", lv_path);
+ return 0;
+ }
+
+ log_very_verbose("Removing link %s", lv_path);
+ if (unlink(lv_path) < 0) {
+ log_sys_error("unlink", lv_path);
+ return 0;
+ }
+
+ return 1;
+}
+
+typedef enum {
+ FS_ADD,
+ FS_DEL,
+ FS_RENAME
+} fs_op_t;
+
+static int _do_fs_op(fs_op_t type, const char *dev_dir, const char *vg_name,
+ const char *lv_name, const char *dev,
+ const char *old_lv_name, int check_udev)
+{
+ switch (type) {
+ case FS_ADD:
+ if (!_mk_dir(dev_dir, vg_name) ||
+ !_mk_link(dev_dir, vg_name, lv_name, dev, check_udev))
+ return_0;
+ break;
+ case FS_DEL:
+ if (!_rm_link(dev_dir, vg_name, lv_name, check_udev) ||
+ !_rm_dir(dev_dir, vg_name))
+ return_0;
+ break;
+ /* FIXME Use rename() */
+ case FS_RENAME:
+ if (old_lv_name && !_rm_link(dev_dir, vg_name, old_lv_name,
+ check_udev))
+ stack;
+
+ if (!_mk_link(dev_dir, vg_name, lv_name, dev, check_udev))
+ stack;
+ }
+
+ return 1;
+}
+
+static DM_LIST_INIT(_fs_ops);
+
+struct fs_op_parms {
+ struct dm_list list;
+ fs_op_t type;
+ int check_udev;
+ char *dev_dir;
+ char *vg_name;
+ char *lv_name;
+ char *dev;
+ char *old_lv_name;
+ char names[0];
+};
+
+static void _store_str(char **pos, char **ptr, const char *str)
+{
+ strcpy(*pos, str);
+ *ptr = *pos;
+ *pos += strlen(*ptr) + 1;
+}
+
+static int _stack_fs_op(fs_op_t type, const char *dev_dir, const char *vg_name,
+ const char *lv_name, const char *dev,
+ const char *old_lv_name, int check_udev)
+{
+ struct fs_op_parms *fsp;
+ size_t len = strlen(dev_dir) + strlen(vg_name) + strlen(lv_name) +
+ strlen(dev) + strlen(old_lv_name) + 5;
+ char *pos;
+
+ if (!(fsp = dm_malloc(sizeof(*fsp) + len))) {
+ log_error("No space to stack fs operation");
+ return 0;
+ }
+
+ pos = fsp->names;
+ fsp->type = type;
+ fsp->check_udev = check_udev;
+
+ _store_str(&pos, &fsp->dev_dir, dev_dir);
+ _store_str(&pos, &fsp->vg_name, vg_name);
+ _store_str(&pos, &fsp->lv_name, lv_name);
+ _store_str(&pos, &fsp->dev, dev);
+ _store_str(&pos, &fsp->old_lv_name, old_lv_name);
+
+ dm_list_add(&_fs_ops, &fsp->list);
+
+ return 1;
+}
+
+static void _pop_fs_ops(void)
+{
+ struct dm_list *fsph, *fspht;
+ struct fs_op_parms *fsp;
+
+ dm_list_iterate_safe(fsph, fspht, &_fs_ops) {
+ fsp = dm_list_item(fsph, struct fs_op_parms);
+ _do_fs_op(fsp->type, fsp->dev_dir, fsp->vg_name, fsp->lv_name,
+ fsp->dev, fsp->old_lv_name, fsp->check_udev);
+ dm_list_del(&fsp->list);
+ dm_free(fsp);
+ }
+}
+
+static int _fs_op(fs_op_t type, const char *dev_dir, const char *vg_name,
+ const char *lv_name, const char *dev, const char *old_lv_name,
+ int check_udev)
+{
+ if (memlock()) {
+ if (!_stack_fs_op(type, dev_dir, vg_name, lv_name, dev,
+ old_lv_name, check_udev))
+ return_0;
+ return 1;
+ }
+
+ return _do_fs_op(type, dev_dir, vg_name, lv_name, dev,
+ old_lv_name, check_udev);
+}
+
+int fs_add_lv(const struct logical_volume *lv, const char *dev)
+{
+ return _fs_op(FS_ADD, lv->vg->cmd->dev_dir, lv->vg->name, lv->name,
+ dev, "", lv->vg->cmd->current_settings.udev_rules);
+}
+
+int fs_del_lv(const struct logical_volume *lv)
+{
+ return _fs_op(FS_DEL, lv->vg->cmd->dev_dir, lv->vg->name, lv->name,
+ "", "", lv->vg->cmd->current_settings.udev_rules);
+}
+
+int fs_del_lv_byname(const char *dev_dir, const char *vg_name,
+ const char *lv_name, int check_udev)
+{
+ return _fs_op(FS_DEL, dev_dir, vg_name, lv_name, "", "", check_udev);
+}
+
+int fs_rename_lv(struct logical_volume *lv, const char *dev,
+ const char *old_vgname, const char *old_lvname)
+{
+ if (strcmp(old_vgname, lv->vg->name)) {
+ return
+ (_fs_op(FS_DEL, lv->vg->cmd->dev_dir, old_vgname,
+ old_lvname, "", "", lv->vg->cmd->current_settings.udev_rules) &&
+ _fs_op(FS_ADD, lv->vg->cmd->dev_dir, lv->vg->name,
+ lv->name, dev, "", lv->vg->cmd->current_settings.udev_rules));
+ }
+ else
+ return _fs_op(FS_RENAME, lv->vg->cmd->dev_dir, lv->vg->name, lv->name,
+ dev, old_lvname, lv->vg->cmd->current_settings.udev_rules);
+}
+
+void fs_unlock(void)
+{
+ if (!memlock()) {
+ dm_lib_release();
+ _pop_fs_ops();
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_FS_H
+#define _LVM_FS_H
+
+#include "metadata.h"
+
+/*
+ * These calls, private to the activate unit, set
+ * up the volume group directory in /dev and the
+ * symbolic links to the dm device.
+ */
+int fs_add_lv(const struct logical_volume *lv, const char *dev);
+int fs_del_lv(const struct logical_volume *lv);
+int fs_del_lv_byname(const char *dev_dir, const char *vg_name,
+ const char *lv_name, int check_udev);
+int fs_rename_lv(struct logical_volume *lv, const char *dev,
+ const char *old_vgname, const char *old_lvname);
+void fs_unlock(void);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_TARGETS_H
+#define _LVM_TARGETS_H
+
+struct dev_manager;
+struct lv_segment;
+
+int compose_areas_line(struct dev_manager *dm, struct lv_segment *seg,
+ char *params, size_t paramsize, int *pos,
+ int start_area, int areas);
+
+int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
+ struct dm_tree_node *node, uint32_t start_area, uint32_t areas);
+
+int build_dev_string(struct dev_manager *dm, char *dlid, char *devbuf,
+ size_t bufsize, const char *desc);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "lvmcache.h"
+#include "toolcontext.h"
+#include "dev-cache.h"
+#include "locking.h"
+#include "metadata.h"
+#include "filter.h"
+#include "filter-persistent.h"
+#include "memlock.h"
+#include "str_list.h"
+#include "format-text.h"
+#include "format_pool.h"
+#include "format1.h"
+
+static struct dm_hash_table *_pvid_hash = NULL;
+static struct dm_hash_table *_vgid_hash = NULL;
+static struct dm_hash_table *_vgname_hash = NULL;
+static struct dm_hash_table *_lock_hash = NULL;
+static DM_LIST_INIT(_vginfos);
+static int _scanning_in_progress = 0;
+static int _has_scanned = 0;
+static int _vgs_locked = 0;
+static int _vg_global_lock_held = 0; /* Global lock held when cache wiped? */
+
+int lvmcache_init(void)
+{
+ /*
+ * FIXME add a proper lvmcache_locking_reset() that
+ * resets the cache so no previous locks are locked
+ */
+ _vgs_locked = 0;
+
+ dm_list_init(&_vginfos);
+
+ if (!(_vgname_hash = dm_hash_create(128)))
+ return 0;
+
+ if (!(_vgid_hash = dm_hash_create(128)))
+ return 0;
+
+ if (!(_pvid_hash = dm_hash_create(128)))
+ return 0;
+
+ if (!(_lock_hash = dm_hash_create(128)))
+ return 0;
+
+ /*
+ * Reinitialising the cache clears the internal record of
+ * which locks are held. The global lock can be held during
+ * this operation so its state must be restored afterwards.
+ */
+ if (_vg_global_lock_held) {
+ lvmcache_lock_vgname(VG_GLOBAL, 0);
+ _vg_global_lock_held = 0;
+ }
+
+ return 1;
+}
+
+/* Volume Group metadata cache functions */
+static void _free_cached_vgmetadata(struct lvmcache_vginfo *vginfo)
+{
+ if (!vginfo || !vginfo->vgmetadata)
+ return;
+
+ dm_free(vginfo->vgmetadata);
+
+ vginfo->vgmetadata = NULL;
+
+ log_debug("Metadata cache: VG %s wiped.", vginfo->vgname);
+}
+
+/*
+ * Cache VG metadata against the vginfo with matching vgid.
+ */
+static void _store_metadata(struct volume_group *vg, unsigned precommitted)
+{
+ char uuid[64] __attribute__((aligned(8)));
+ struct lvmcache_vginfo *vginfo;
+ int size;
+
+ if (!(vginfo = vginfo_from_vgid((const char *)&vg->id))) {
+ stack;
+ return;
+ }
+
+ if (vginfo->vgmetadata)
+ _free_cached_vgmetadata(vginfo);
+
+ if (!(size = export_vg_to_buffer(vg, &vginfo->vgmetadata))) {
+ stack;
+ return;
+ }
+
+ vginfo->precommitted = precommitted;
+
+ if (!id_write_format((const struct id *)vginfo->vgid, uuid, sizeof(uuid))) {
+ stack;
+ return;
+ }
+
+ log_debug("Metadata cache: VG %s (%s) stored (%d bytes%s).",
+ vginfo->vgname, uuid, size,
+ precommitted ? ", precommitted" : "");
+}
+
+static void _update_cache_info_lock_state(struct lvmcache_info *info,
+ int locked,
+ int *cached_vgmetadata_valid)
+{
+ int was_locked = (info->status & CACHE_LOCKED) ? 1 : 0;
+
+ /*
+ * Cache becomes invalid whenever lock state changes unless
+ * exclusive VG_GLOBAL is held (i.e. while scanning).
+ */
+ if (!vgname_is_locked(VG_GLOBAL) && (was_locked != locked)) {
+ info->status |= CACHE_INVALID;
+ *cached_vgmetadata_valid = 0;
+ }
+
+ if (locked)
+ info->status |= CACHE_LOCKED;
+ else
+ info->status &= ~CACHE_LOCKED;
+}
+
+static void _update_cache_vginfo_lock_state(struct lvmcache_vginfo *vginfo,
+ int locked)
+{
+ struct lvmcache_info *info;
+ int cached_vgmetadata_valid = 1;
+
+ dm_list_iterate_items(info, &vginfo->infos)
+ _update_cache_info_lock_state(info, locked,
+ &cached_vgmetadata_valid);
+
+ if (!cached_vgmetadata_valid)
+ _free_cached_vgmetadata(vginfo);
+}
+
+static void _update_cache_lock_state(const char *vgname, int locked)
+{
+ struct lvmcache_vginfo *vginfo;
+
+ if (!(vginfo = vginfo_from_vgname(vgname, NULL)))
+ return;
+
+ _update_cache_vginfo_lock_state(vginfo, locked);
+}
+
+static void _drop_metadata(const char *vgname, int drop_precommitted)
+{
+ struct lvmcache_vginfo *vginfo;
+ struct lvmcache_info *info;
+
+ if (!(vginfo = vginfo_from_vgname(vgname, NULL)))
+ return;
+
+ /*
+ * Invalidate cached PV labels.
+ * If cached precommitted metadata exists that means we
+ * already invalidated the PV labels (before caching it)
+ * and we must not do it again.
+ */
+ if (!drop_precommitted && vginfo->precommitted && !vginfo->vgmetadata)
+ log_error(INTERNAL_ERROR "metadata commit (or revert) missing before "
+ "dropping metadata from cache.");
+
+ if (drop_precommitted || !vginfo->precommitted)
+ dm_list_iterate_items(info, &vginfo->infos)
+ info->status |= CACHE_INVALID;
+
+ _free_cached_vgmetadata(vginfo);
+}
+
+/*
+ * Remote node uses this to upgrade precommited metadata to commited state
+ * when receives vg_commit notification.
+ * (Note that devices can be suspended here, if so, precommited metadata are already read.)
+ */
+void lvmcache_commit_metadata(const char *vgname)
+{
+ struct lvmcache_vginfo *vginfo;
+
+ if (!(vginfo = vginfo_from_vgname(vgname, NULL)))
+ return;
+
+ if (vginfo->precommitted) {
+ log_debug("Precommitted metadata cache: VG %s upgraded to committed.",
+ vginfo->vgname);
+ vginfo->precommitted = 0;
+ }
+}
+
+void lvmcache_drop_metadata(const char *vgname, int drop_precommitted)
+{
+ /* For VG_ORPHANS, we need to invalidate all labels on orphan PVs. */
+ if (!strcmp(vgname, VG_ORPHANS)) {
+ _drop_metadata(FMT_TEXT_ORPHAN_VG_NAME, 0);
+ _drop_metadata(FMT_LVM1_ORPHAN_VG_NAME, 0);
+ _drop_metadata(FMT_POOL_ORPHAN_VG_NAME, 0);
+
+ /* Indicate that PVs could now be missing from the cache */
+ init_full_scan_done(0);
+ } else if (!vgname_is_locked(VG_GLOBAL))
+ _drop_metadata(vgname, drop_precommitted);
+}
+
+/*
+ * Ensure vgname2 comes after vgname1 alphabetically.
+ * Orphan locks come last.
+ * VG_GLOBAL comes first.
+ */
+static int _vgname_order_correct(const char *vgname1, const char *vgname2)
+{
+ if (is_global_vg(vgname1))
+ return 1;
+
+ if (is_global_vg(vgname2))
+ return 0;
+
+ if (is_orphan_vg(vgname1))
+ return 0;
+
+ if (is_orphan_vg(vgname2))
+ return 1;
+
+ if (strcmp(vgname1, vgname2) < 0)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Ensure VG locks are acquired in alphabetical order.
+ */
+int lvmcache_verify_lock_order(const char *vgname)
+{
+ struct dm_hash_node *n;
+ const char *vgname2;
+
+ if (!_lock_hash)
+ return_0;
+
+ dm_hash_iterate(n, _lock_hash) {
+ if (!dm_hash_get_data(_lock_hash, n))
+ return_0;
+
+ vgname2 = dm_hash_get_key(_lock_hash, n);
+
+ if (!_vgname_order_correct(vgname2, vgname)) {
+ log_errno(EDEADLK, INTERNAL_ERROR "VG lock %s must "
+ "be requested before %s, not after.",
+ vgname, vgname2);
+ return_0;
+ }
+ }
+
+ return 1;
+}
+
+void lvmcache_lock_vgname(const char *vgname, int read_only __attribute__((unused)))
+{
+ if (!_lock_hash && !lvmcache_init()) {
+ log_error("Internal cache initialisation failed");
+ return;
+ }
+
+ if (dm_hash_lookup(_lock_hash, vgname))
+ log_error(INTERNAL_ERROR "Nested locking attempted on VG %s.",
+ vgname);
+
+ if (!dm_hash_insert(_lock_hash, vgname, (void *) 1))
+ log_error("Cache locking failure for %s", vgname);
+
+ _update_cache_lock_state(vgname, 1);
+
+ if (strcmp(vgname, VG_GLOBAL))
+ _vgs_locked++;
+}
+
+int vgname_is_locked(const char *vgname)
+{
+ if (!_lock_hash)
+ return 0;
+
+ return dm_hash_lookup(_lock_hash, is_orphan_vg(vgname) ? VG_ORPHANS : vgname) ? 1 : 0;
+}
+
+void lvmcache_unlock_vgname(const char *vgname)
+{
+ if (!dm_hash_lookup(_lock_hash, vgname))
+ log_error(INTERNAL_ERROR "Attempt to unlock unlocked VG %s.",
+ vgname);
+
+ _update_cache_lock_state(vgname, 0);
+
+ dm_hash_remove(_lock_hash, vgname);
+
+ /* FIXME Do this per-VG */
+ if (strcmp(vgname, VG_GLOBAL) && !--_vgs_locked)
+ dev_close_all();
+}
+
+int vgs_locked(void)
+{
+ return _vgs_locked;
+}
+
+static void _vginfo_attach_info(struct lvmcache_vginfo *vginfo,
+ struct lvmcache_info *info)
+{
+ if (!vginfo)
+ return;
+
+ info->vginfo = vginfo;
+ dm_list_add(&vginfo->infos, &info->list);
+}
+
+static void _vginfo_detach_info(struct lvmcache_info *info)
+{
+ if (!dm_list_empty(&info->list)) {
+ dm_list_del(&info->list);
+ dm_list_init(&info->list);
+ }
+
+ info->vginfo = NULL;
+}
+
+/* If vgid supplied, require a match. */
+struct lvmcache_vginfo *vginfo_from_vgname(const char *vgname, const char *vgid)
+{
+ struct lvmcache_vginfo *vginfo;
+
+ if (!vgname)
+ return vginfo_from_vgid(vgid);
+
+ if (!_vgname_hash)
+ return NULL;
+
+ if (!(vginfo = dm_hash_lookup(_vgname_hash, vgname)))
+ return NULL;
+
+ if (vgid)
+ do
+ if (!strncmp(vgid, vginfo->vgid, ID_LEN))
+ return vginfo;
+ while ((vginfo = vginfo->next));
+
+ return vginfo;
+}
+
+const struct format_type *fmt_from_vgname(const char *vgname, const char *vgid, unsigned revalidate_labels)
+{
+ struct lvmcache_vginfo *vginfo;
+ struct lvmcache_info *info;
+ struct label *label;
+ struct dm_list *devh, *tmp;
+ struct dm_list devs;
+ struct device_list *devl;
+ char vgid_found[ID_LEN + 1] __attribute__((aligned(8)));
+
+ if (!(vginfo = vginfo_from_vgname(vgname, vgid)))
+ return NULL;
+
+ /*
+ * If this function is called repeatedly, only the first one needs to revalidate.
+ */
+ if (!revalidate_labels)
+ goto out;
+
+ /*
+ * This function is normally called before reading metadata so
+ * we check cached labels here. Unfortunately vginfo is volatile.
+ */
+ dm_list_init(&devs);
+ dm_list_iterate_items(info, &vginfo->infos) {
+ if (!(devl = dm_malloc(sizeof(*devl)))) {
+ log_error("device_list element allocation failed");
+ return NULL;
+ }
+ devl->dev = info->dev;
+ dm_list_add(&devs, &devl->list);
+ }
+
+ memcpy(vgid_found, vginfo->vgid, sizeof(vgid_found));
+
+ dm_list_iterate_safe(devh, tmp, &devs) {
+ devl = dm_list_item(devh, struct device_list);
+ label_read(devl->dev, &label, UINT64_C(0));
+ dm_list_del(&devl->list);
+ dm_free(devl);
+ }
+
+ /* If vginfo changed, caller needs to rescan */
+ if (!(vginfo = vginfo_from_vgname(vgname, vgid_found)) ||
+ strncmp(vginfo->vgid, vgid_found, ID_LEN))
+ return NULL;
+
+out:
+ return vginfo->fmt;
+}
+
+struct lvmcache_vginfo *vginfo_from_vgid(const char *vgid)
+{
+ struct lvmcache_vginfo *vginfo;
+ char id[ID_LEN + 1] __attribute__((aligned(8)));
+
+ if (!_vgid_hash || !vgid)
+ return NULL;
+
+ /* vgid not necessarily NULL-terminated */
+ strncpy(&id[0], vgid, ID_LEN);
+ id[ID_LEN] = '\0';
+
+ if (!(vginfo = dm_hash_lookup(_vgid_hash, id)))
+ return NULL;
+
+ return vginfo;
+}
+
+const char *vgname_from_vgid(struct dm_pool *mem, const char *vgid)
+{
+ struct lvmcache_vginfo *vginfo;
+ const char *vgname = NULL;
+
+ if ((vginfo = vginfo_from_vgid(vgid)))
+ vgname = vginfo->vgname;
+
+ if (mem && vgname)
+ return dm_pool_strdup(mem, vgname);
+
+ return vgname;
+}
+
+static int _info_is_valid(struct lvmcache_info *info)
+{
+ if (info->status & CACHE_INVALID)
+ return 0;
+
+ /*
+ * The caller must hold the VG lock to manipulate metadata.
+ * In a cluster, remote nodes sometimes read metadata in the
+ * knowledge that the controlling node is holding the lock.
+ * So if the VG appears to be unlocked here, it should be safe
+ * to use the cached value.
+ */
+ if (info->vginfo && !vgname_is_locked(info->vginfo->vgname))
+ return 1;
+
+ if (!(info->status & CACHE_LOCKED))
+ return 0;
+
+ return 1;
+}
+
+static int _vginfo_is_valid(struct lvmcache_vginfo *vginfo)
+{
+ struct lvmcache_info *info;
+
+ /* Invalid if any info is invalid */
+ dm_list_iterate_items(info, &vginfo->infos)
+ if (!_info_is_valid(info))
+ return 0;
+
+ return 1;
+}
+
+/* vginfo is invalid if it does not contain at least one valid info */
+static int _vginfo_is_invalid(struct lvmcache_vginfo *vginfo)
+{
+ struct lvmcache_info *info;
+
+ dm_list_iterate_items(info, &vginfo->infos)
+ if (_info_is_valid(info))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * If valid_only is set, data will only be returned if the cached data is
+ * known still to be valid.
+ */
+struct lvmcache_info *info_from_pvid(const char *pvid, int valid_only)
+{
+ struct lvmcache_info *info;
+ char id[ID_LEN + 1] __attribute__((aligned(8)));
+
+ if (!_pvid_hash || !pvid)
+ return NULL;
+
+ strncpy(&id[0], pvid, ID_LEN);
+ id[ID_LEN] = '\0';
+
+ if (!(info = dm_hash_lookup(_pvid_hash, id)))
+ return NULL;
+
+ if (valid_only && !_info_is_valid(info))
+ return NULL;
+
+ return info;
+}
+
+char *lvmcache_vgname_from_pvid(struct cmd_context *cmd, const char *pvid)
+{
+ struct lvmcache_info *info;
+ char *vgname;
+
+ if (!device_from_pvid(cmd, (const struct id *)pvid, NULL)) {
+ log_error("Couldn't find device with uuid %s.", pvid);
+ return NULL;
+ }
+
+ info = info_from_pvid(pvid, 0);
+ if (!info)
+ return_NULL;
+
+ if (!(vgname = dm_pool_strdup(cmd->mem, info->vginfo->vgname))) {
+ log_errno(ENOMEM, "vgname allocation failed");
+ return NULL;
+ }
+ return vgname;
+}
+
+static void _rescan_entry(struct lvmcache_info *info)
+{
+ struct label *label;
+
+ if (info->status & CACHE_INVALID)
+ label_read(info->dev, &label, UINT64_C(0));
+}
+
+static int _scan_invalid(void)
+{
+ dm_hash_iter(_pvid_hash, (dm_hash_iterate_fn) _rescan_entry);
+
+ return 1;
+}
+
+int lvmcache_label_scan(struct cmd_context *cmd, int full_scan)
+{
+ struct label *label;
+ struct dev_iter *iter;
+ struct device *dev;
+ struct format_type *fmt;
+
+ int r = 0;
+
+ /* Avoid recursion when a PVID can't be found! */
+ if (_scanning_in_progress)
+ return 0;
+
+ _scanning_in_progress = 1;
+
+ if (!_vgname_hash && !lvmcache_init()) {
+ log_error("Internal cache initialisation failed");
+ goto out;
+ }
+
+ if (_has_scanned && !full_scan) {
+ r = _scan_invalid();
+ goto out;
+ }
+
+ if (full_scan == 2 && !cmd->filter->use_count && !refresh_filters(cmd)) {
+ log_error("refresh filters failed");
+ goto out;
+ }
+
+ if (!(iter = dev_iter_create(cmd->filter, (full_scan == 2) ? 1 : 0))) {
+ log_error("dev_iter creation failed");
+ goto out;
+ }
+
+ while ((dev = dev_iter_get(iter)))
+ label_read(dev, &label, UINT64_C(0));
+
+ dev_iter_destroy(iter);
+
+ _has_scanned = 1;
+
+ /* Perform any format-specific scanning e.g. text files */
+ if (cmd->independent_metadata_areas)
+ dm_list_iterate_items(fmt, &cmd->formats)
+ if (fmt->ops->scan && !fmt->ops->scan(fmt, NULL))
+ goto out;
+
+ /*
+ * If we are a long-lived process, write out the updated persistent
+ * device cache for the benefit of short-lived processes.
+ */
+ if (full_scan == 2 && cmd->is_long_lived && cmd->dump_filter)
+ persistent_filter_dump(cmd->filter, 0);
+
+ r = 1;
+
+ out:
+ _scanning_in_progress = 0;
+
+ return r;
+}
+
+struct volume_group *lvmcache_get_vg(const char *vgid, unsigned precommitted)
+{
+ struct lvmcache_vginfo *vginfo;
+ struct volume_group *vg;
+ struct format_instance *fid;
+
+ if (!vgid || !(vginfo = vginfo_from_vgid(vgid)) || !vginfo->vgmetadata)
+ return NULL;
+
+ if (!_vginfo_is_valid(vginfo))
+ return NULL;
+
+ /*
+ * Don't return cached data if either:
+ * (i) precommitted metadata is requested but we don't have it cached
+ * - caller should read it off disk;
+ * (ii) live metadata is requested but we have precommitted metadata cached
+ * and no devices are suspended so caller may read it off disk.
+ *
+ * If live metadata is requested but we have precommitted metadata cached
+ * and devices are suspended, we assume this precommitted metadata has
+ * already been preloaded and committed so it's OK to return it as live.
+ * Note that we do not clear the PRECOMMITTED flag.
+ */
+ if ((precommitted && !vginfo->precommitted) ||
+ (!precommitted && vginfo->precommitted && !memlock()))
+ return NULL;
+
+ if (!(fid = vginfo->fmt->ops->create_instance(vginfo->fmt,
+ vginfo->vgname,
+ vgid, NULL)))
+ return_NULL;
+
+ if (!(vg = import_vg_from_buffer(vginfo->vgmetadata, fid))) {
+ _free_cached_vgmetadata(vginfo);
+ free_vg(vg);
+ return_NULL;
+ }
+
+ log_debug("Using cached %smetadata for VG %s.",
+ vginfo->precommitted ? "pre-committed" : "", vginfo->vgname);
+
+ return vg;
+}
+
+struct dm_list *lvmcache_get_vgids(struct cmd_context *cmd,
+ int include_internal)
+{
+ struct dm_list *vgids;
+ struct lvmcache_vginfo *vginfo;
+
+ lvmcache_label_scan(cmd, 0);
+
+ if (!(vgids = str_list_create(cmd->mem))) {
+ log_error("vgids list allocation failed");
+ return NULL;
+ }
+
+ dm_list_iterate_items(vginfo, &_vginfos) {
+ if (!include_internal && is_orphan_vg(vginfo->vgname))
+ continue;
+
+ if (!str_list_add(cmd->mem, vgids,
+ dm_pool_strdup(cmd->mem, vginfo->vgid))) {
+ log_error("strlist allocation failed");
+ return NULL;
+ }
+ }
+
+ return vgids;
+}
+
+struct dm_list *lvmcache_get_vgnames(struct cmd_context *cmd,
+ int include_internal)
+{
+ struct dm_list *vgnames;
+ struct lvmcache_vginfo *vginfo;
+
+ lvmcache_label_scan(cmd, 0);
+
+ if (!(vgnames = str_list_create(cmd->mem))) {
+ log_errno(ENOMEM, "vgnames list allocation failed");
+ return NULL;
+ }
+
+ dm_list_iterate_items(vginfo, &_vginfos) {
+ if (!include_internal && is_orphan_vg(vginfo->vgname))
+ continue;
+
+ if (!str_list_add(cmd->mem, vgnames,
+ dm_pool_strdup(cmd->mem, vginfo->vgname))) {
+ log_errno(ENOMEM, "strlist allocation failed");
+ return NULL;
+ }
+ }
+
+ return vgnames;
+}
+
+struct dm_list *lvmcache_get_pvids(struct cmd_context *cmd, const char *vgname,
+ const char *vgid)
+{
+ struct dm_list *pvids;
+ struct lvmcache_vginfo *vginfo;
+ struct lvmcache_info *info;
+
+ if (!(pvids = str_list_create(cmd->mem))) {
+ log_error("pvids list allocation failed");
+ return NULL;
+ }
+
+ if (!(vginfo = vginfo_from_vgname(vgname, vgid)))
+ return pvids;
+
+ dm_list_iterate_items(info, &vginfo->infos) {
+ if (!str_list_add(cmd->mem, pvids,
+ dm_pool_strdup(cmd->mem, info->dev->pvid))) {
+ log_error("strlist allocation failed");
+ return NULL;
+ }
+ }
+
+ return pvids;
+}
+
+struct device *device_from_pvid(struct cmd_context *cmd, const struct id *pvid,
+ unsigned *scan_done_once)
+{
+ struct label *label;
+ struct lvmcache_info *info;
+
+ /* Already cached ? */
+ if ((info = info_from_pvid((const char *) pvid, 0))) {
+ if (label_read(info->dev, &label, UINT64_C(0))) {
+ info = (struct lvmcache_info *) label->info;
+ if (id_equal(pvid, (struct id *) &info->dev->pvid))
+ return info->dev;
+ }
+ }
+
+ lvmcache_label_scan(cmd, 0);
+
+ /* Try again */
+ if ((info = info_from_pvid((const char *) pvid, 0))) {
+ if (label_read(info->dev, &label, UINT64_C(0))) {
+ info = (struct lvmcache_info *) label->info;
+ if (id_equal(pvid, (struct id *) &info->dev->pvid))
+ return info->dev;
+ }
+ }
+
+ if (memlock() || (scan_done_once && *scan_done_once))
+ return NULL;
+
+ lvmcache_label_scan(cmd, 2);
+ if (scan_done_once)
+ *scan_done_once = 1;
+
+ /* Try again */
+ if ((info = info_from_pvid((const char *) pvid, 0))) {
+ if (label_read(info->dev, &label, UINT64_C(0))) {
+ info = (struct lvmcache_info *) label->info;
+ if (id_equal(pvid, (struct id *) &info->dev->pvid))
+ return info->dev;
+ }
+ }
+
+ return NULL;
+}
+
+const char *pvid_from_devname(struct cmd_context *cmd,
+ const char *devname)
+{
+ struct device *dev;
+ struct label *label;
+
+ if (!(dev = dev_cache_get(devname, cmd->filter))) {
+ log_error("%s: Couldn't find device. Check your filters?",
+ devname);
+ return NULL;
+ }
+
+ if (!(label_read(dev, &label, UINT64_C(0))))
+ return NULL;
+
+ return dev->pvid;
+}
+
+
+static int _free_vginfo(struct lvmcache_vginfo *vginfo)
+{
+ struct lvmcache_vginfo *primary_vginfo, *vginfo2;
+ int r = 1;
+
+ _free_cached_vgmetadata(vginfo);
+
+ vginfo2 = primary_vginfo = vginfo_from_vgname(vginfo->vgname, NULL);
+
+ if (vginfo == primary_vginfo) {
+ dm_hash_remove(_vgname_hash, vginfo->vgname);
+ if (vginfo->next && !dm_hash_insert(_vgname_hash, vginfo->vgname,
+ vginfo->next)) {
+ log_error("_vgname_hash re-insertion for %s failed",
+ vginfo->vgname);
+ r = 0;
+ }
+ } else do
+ if (vginfo2->next == vginfo) {
+ vginfo2->next = vginfo->next;
+ break;
+ }
+ while ((vginfo2 = primary_vginfo->next));
+
+ if (vginfo->vgname)
+ dm_free(vginfo->vgname);
+
+ if (vginfo->creation_host)
+ dm_free(vginfo->creation_host);
+
+ if (*vginfo->vgid && _vgid_hash &&
+ vginfo_from_vgid(vginfo->vgid) == vginfo)
+ dm_hash_remove(_vgid_hash, vginfo->vgid);
+
+ dm_list_del(&vginfo->list);
+
+ dm_free(vginfo);
+
+ return r;
+}
+
+/*
+ * vginfo must be info->vginfo unless info is NULL
+ */
+static int _drop_vginfo(struct lvmcache_info *info, struct lvmcache_vginfo *vginfo)
+{
+ if (info)
+ _vginfo_detach_info(info);
+
+ /* vginfo still referenced? */
+ if (!vginfo || is_orphan_vg(vginfo->vgname) ||
+ !dm_list_empty(&vginfo->infos))
+ return 1;
+
+ if (!_free_vginfo(vginfo))
+ return_0;
+
+ return 1;
+}
+
+/* Unused
+void lvmcache_del(struct lvmcache_info *info)
+{
+ if (info->dev->pvid[0] && _pvid_hash)
+ dm_hash_remove(_pvid_hash, info->dev->pvid);
+
+ _drop_vginfo(info, info->vginfo);
+
+ info->label->labeller->ops->destroy_label(info->label->labeller,
+ info->label);
+ dm_free(info);
+
+ return;
+} */
+
+static int _lvmcache_update_pvid(struct lvmcache_info *info, const char *pvid)
+{
+ /*
+ * Nothing to do if already stored with same pvid.
+ */
+ if (((dm_hash_lookup(_pvid_hash, pvid)) == info) &&
+ !strcmp(info->dev->pvid, pvid))
+ return 1;
+ if (*info->dev->pvid)
+ dm_hash_remove(_pvid_hash, info->dev->pvid);
+ strncpy(info->dev->pvid, pvid, sizeof(info->dev->pvid));
+ if (!dm_hash_insert(_pvid_hash, pvid, info)) {
+ log_error("_lvmcache_update: pvid insertion failed: %s", pvid);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * vginfo must be info->vginfo unless info is NULL (orphans)
+ */
+static int _lvmcache_update_vgid(struct lvmcache_info *info,
+ struct lvmcache_vginfo *vginfo,
+ const char *vgid)
+{
+ if (!vgid || !vginfo ||
+ !strncmp(vginfo->vgid, vgid, ID_LEN))
+ return 1;
+
+ if (vginfo && *vginfo->vgid)
+ dm_hash_remove(_vgid_hash, vginfo->vgid);
+ if (!vgid) {
+ log_debug("lvmcache: %s: clearing VGID", info ? dev_name(info->dev) : vginfo->vgname);
+ return 1;
+ }
+
+ strncpy(vginfo->vgid, vgid, ID_LEN);
+ vginfo->vgid[ID_LEN] = '\0';
+ if (!dm_hash_insert(_vgid_hash, vginfo->vgid, vginfo)) {
+ log_error("_lvmcache_update: vgid hash insertion failed: %s",
+ vginfo->vgid);
+ return 0;
+ }
+
+ if (!is_orphan_vg(vginfo->vgname))
+ log_debug("lvmcache: %s: setting %s VGID to %s",
+ dev_name(info->dev), vginfo->vgname,
+ vginfo->vgid);
+
+ return 1;
+}
+
+static int _insert_vginfo(struct lvmcache_vginfo *new_vginfo, const char *vgid,
+ uint32_t vgstatus, const char *creation_host,
+ struct lvmcache_vginfo *primary_vginfo)
+{
+ struct lvmcache_vginfo *last_vginfo = primary_vginfo;
+ char uuid_primary[64] __attribute__((aligned(8)));
+ char uuid_new[64] __attribute__((aligned(8)));
+ int use_new = 0;
+
+ /* Pre-existing VG takes precedence. Unexported VG takes precedence. */
+ if (primary_vginfo) {
+ if (!id_write_format((const struct id *)vgid, uuid_new, sizeof(uuid_new)))
+ return_0;
+
+ if (!id_write_format((const struct id *)&primary_vginfo->vgid, uuid_primary,
+ sizeof(uuid_primary)))
+ return_0;
+
+ /*
+ * If Primary not exported, new exported => keep
+ * Else Primary exported, new not exported => change
+ * Else Primary has hostname for this machine => keep
+ * Else Primary has no hostname, new has one => change
+ * Else New has hostname for this machine => change
+ * Else Keep primary.
+ */
+ if (!(primary_vginfo->status & EXPORTED_VG) &&
+ (vgstatus & EXPORTED_VG))
+ log_warn("WARNING: Duplicate VG name %s: "
+ "Existing %s takes precedence over "
+ "exported %s", new_vginfo->vgname,
+ uuid_primary, uuid_new);
+ else if ((primary_vginfo->status & EXPORTED_VG) &&
+ !(vgstatus & EXPORTED_VG)) {
+ log_warn("WARNING: Duplicate VG name %s: "
+ "%s takes precedence over exported %s",
+ new_vginfo->vgname, uuid_new,
+ uuid_primary);
+ use_new = 1;
+ } else if (primary_vginfo->creation_host &&
+ !strcmp(primary_vginfo->creation_host,
+ primary_vginfo->fmt->cmd->hostname))
+ log_warn("WARNING: Duplicate VG name %s: "
+ "Existing %s (created here) takes precedence "
+ "over %s", new_vginfo->vgname, uuid_primary,
+ uuid_new);
+ else if (!primary_vginfo->creation_host && creation_host) {
+ log_warn("WARNING: Duplicate VG name %s: "
+ "%s (with creation_host) takes precedence over %s",
+ new_vginfo->vgname, uuid_new,
+ uuid_primary);
+ use_new = 1;
+ } else if (creation_host &&
+ !strcmp(creation_host,
+ primary_vginfo->fmt->cmd->hostname)) {
+ log_warn("WARNING: Duplicate VG name %s: "
+ "%s (created here) takes precedence over %s",
+ new_vginfo->vgname, uuid_new,
+ uuid_primary);
+ use_new = 1;
+ }
+
+ if (!use_new) {
+ while (last_vginfo->next)
+ last_vginfo = last_vginfo->next;
+ last_vginfo->next = new_vginfo;
+ return 1;
+ }
+
+ dm_hash_remove(_vgname_hash, primary_vginfo->vgname);
+ }
+
+ if (!dm_hash_insert(_vgname_hash, new_vginfo->vgname, new_vginfo)) {
+ log_error("cache_update: vg hash insertion failed: %s",
+ new_vginfo->vgname);
+ return 0;
+ }
+
+ if (primary_vginfo)
+ new_vginfo->next = primary_vginfo;
+
+ return 1;
+}
+
+static int _lvmcache_update_vgname(struct lvmcache_info *info,
+ const char *vgname, const char *vgid,
+ uint32_t vgstatus, const char *creation_host,
+ const struct format_type *fmt)
+{
+ struct lvmcache_vginfo *vginfo, *primary_vginfo, *orphan_vginfo;
+ struct lvmcache_info *info2, *info3;
+ char mdabuf[32];
+ // struct lvmcache_vginfo *old_vginfo, *next;
+
+ if (!vgname || (info && info->vginfo && !strcmp(info->vginfo->vgname, vgname)))
+ return 1;
+
+ /* Remove existing vginfo entry */
+ if (info)
+ _drop_vginfo(info, info->vginfo);
+
+ /* Get existing vginfo or create new one */
+ if (!(vginfo = vginfo_from_vgname(vgname, vgid))) {
+/*** FIXME - vginfo ends up duplicated instead of renamed.
+ // Renaming? This lookup fails.
+ if ((vginfo = vginfo_from_vgid(vgid))) {
+ next = vginfo->next;
+ old_vginfo = vginfo_from_vgname(vginfo->vgname, NULL);
+ if (old_vginfo == vginfo) {
+ dm_hash_remove(_vgname_hash, old_vginfo->vgname);
+ if (old_vginfo->next) {
+ if (!dm_hash_insert(_vgname_hash, old_vginfo->vgname, old_vginfo->next)) {
+ log_error("vg hash re-insertion failed: %s",
+ old_vginfo->vgname);
+ return 0;
+ }
+ }
+ } else do {
+ if (old_vginfo->next == vginfo) {
+ old_vginfo->next = vginfo->next;
+ break;
+ }
+ } while ((old_vginfo = old_vginfo->next));
+ vginfo->next = NULL;
+
+ dm_free(vginfo->vgname);
+ if (!(vginfo->vgname = dm_strdup(vgname))) {
+ log_error("cache vgname alloc failed for %s", vgname);
+ return 0;
+ }
+
+ // Rename so can assume new name does not already exist
+ if (!dm_hash_insert(_vgname_hash, vginfo->vgname, vginfo->next)) {
+ log_error("vg hash re-insertion failed: %s",
+ vginfo->vgname);
+ return 0;
+ }
+ } else {
+***/
+ if (!(vginfo = dm_zalloc(sizeof(*vginfo)))) {
+ log_error("lvmcache_update_vgname: list alloc failed");
+ return 0;
+ }
+ if (!(vginfo->vgname = dm_strdup(vgname))) {
+ dm_free(vginfo);
+ log_error("cache vgname alloc failed for %s", vgname);
+ return 0;
+ }
+ dm_list_init(&vginfo->infos);
+
+ /*
+ * If we're scanning and there's an invalidated entry, remove it.
+ * Otherwise we risk bogus warnings of duplicate VGs.
+ */
+ while ((primary_vginfo = vginfo_from_vgname(vgname, NULL)) &&
+ _scanning_in_progress && _vginfo_is_invalid(primary_vginfo))
+ dm_list_iterate_items_safe(info2, info3, &primary_vginfo->infos) {
+ orphan_vginfo = vginfo_from_vgname(primary_vginfo->fmt->orphan_vg_name, NULL);
+ if (!orphan_vginfo) {
+ log_error(INTERNAL_ERROR "Orphan vginfo %s lost from cache.",
+ primary_vginfo->fmt->orphan_vg_name);
+ dm_free(vginfo->vgname);
+ dm_free(vginfo);
+ return 0;
+ }
+ _drop_vginfo(info2, primary_vginfo);
+ _vginfo_attach_info(orphan_vginfo, info2);
+ if (info2->mdas.n)
+ sprintf(mdabuf, " with %u mdas",
+ dm_list_size(&info2->mdas));
+ else
+ mdabuf[0] = '\0';
+ log_debug("lvmcache: %s: now in VG %s%s%s%s%s",
+ dev_name(info2->dev),
+ vgname, orphan_vginfo->vgid[0] ? " (" : "",
+ orphan_vginfo->vgid[0] ? orphan_vginfo->vgid : "",
+ orphan_vginfo->vgid[0] ? ")" : "", mdabuf);
+ }
+
+ if (!_insert_vginfo(vginfo, vgid, vgstatus, creation_host,
+ primary_vginfo)) {
+ dm_free(vginfo->vgname);
+ dm_free(vginfo);
+ return 0;
+ }
+ /* Ensure orphans appear last on list_iterate */
+ if (is_orphan_vg(vgname))
+ dm_list_add(&_vginfos, &vginfo->list);
+ else
+ dm_list_add_h(&_vginfos, &vginfo->list);
+/***
+ }
+***/
+ }
+
+ if (info)
+ _vginfo_attach_info(vginfo, info);
+ else if (!_lvmcache_update_vgid(NULL, vginfo, vgid)) /* Orphans */
+ return_0;
+
+ _update_cache_vginfo_lock_state(vginfo, vgname_is_locked(vgname));
+
+ /* FIXME Check consistency of list! */
+ vginfo->fmt = fmt;
+
+ if (info) {
+ if (info->mdas.n)
+ sprintf(mdabuf, " with %u mdas", dm_list_size(&info->mdas));
+ else
+ mdabuf[0] = '\0';
+ log_debug("lvmcache: %s: now in VG %s%s%s%s%s",
+ dev_name(info->dev),
+ vgname, vginfo->vgid[0] ? " (" : "",
+ vginfo->vgid[0] ? vginfo->vgid : "",
+ vginfo->vgid[0] ? ")" : "", mdabuf);
+ } else
+ log_debug("lvmcache: initialised VG %s", vgname);
+
+ return 1;
+}
+
+static int _lvmcache_update_vgstatus(struct lvmcache_info *info, uint32_t vgstatus,
+ const char *creation_host)
+{
+ if (!info || !info->vginfo)
+ return 1;
+
+ if ((info->vginfo->status & EXPORTED_VG) != (vgstatus & EXPORTED_VG))
+ log_debug("lvmcache: %s: VG %s %s exported",
+ dev_name(info->dev), info->vginfo->vgname,
+ vgstatus & EXPORTED_VG ? "now" : "no longer");
+
+ info->vginfo->status = vgstatus;
+
+ if (!creation_host)
+ return 1;
+
+ if (info->vginfo->creation_host && !strcmp(creation_host,
+ info->vginfo->creation_host))
+ return 1;
+
+ if (info->vginfo->creation_host)
+ dm_free(info->vginfo->creation_host);
+
+ if (!(info->vginfo->creation_host = dm_strdup(creation_host))) {
+ log_error("cache creation host alloc failed for %s",
+ creation_host);
+ return 0;
+ }
+
+ log_debug("lvmcache: %s: VG %s: Set creation host to %s.",
+ dev_name(info->dev), info->vginfo->vgname, creation_host);
+
+ return 1;
+}
+
+int lvmcache_add_orphan_vginfo(const char *vgname, struct format_type *fmt)
+{
+ if (!_lock_hash && !lvmcache_init()) {
+ log_error("Internal cache initialisation failed");
+ return 0;
+ }
+
+ return _lvmcache_update_vgname(NULL, vgname, vgname, 0, "", fmt);
+}
+
+int lvmcache_update_vgname_and_id(struct lvmcache_info *info,
+ const char *vgname, const char *vgid,
+ uint32_t vgstatus, const char *creation_host)
+{
+ if (!vgname && !info->vginfo) {
+ log_error(INTERNAL_ERROR "NULL vgname handed to cache");
+ /* FIXME Remove this */
+ vgname = info->fmt->orphan_vg_name;
+ vgid = vgname;
+ }
+
+ /* If PV without mdas is already in a real VG, don't make it orphan */
+ if (is_orphan_vg(vgname) && info->vginfo &&
+ mdas_empty_or_ignored(&info->mdas) &&
+ !is_orphan_vg(info->vginfo->vgname) && memlock())
+ return 1;
+
+ /* If moving PV from orphan to real VG, always mark it valid */
+ if (!is_orphan_vg(vgname))
+ info->status &= ~CACHE_INVALID;
+
+ if (!_lvmcache_update_vgname(info, vgname, vgid, vgstatus,
+ creation_host, info->fmt) ||
+ !_lvmcache_update_vgid(info, info->vginfo, vgid) ||
+ !_lvmcache_update_vgstatus(info, vgstatus, creation_host))
+ return_0;
+
+ return 1;
+}
+
+int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted)
+{
+ struct pv_list *pvl;
+ struct lvmcache_info *info;
+ char pvid_s[ID_LEN + 1] __attribute__((aligned(8)));
+
+ pvid_s[sizeof(pvid_s) - 1] = '\0';
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ strncpy(pvid_s, (char *) &pvl->pv->id, sizeof(pvid_s) - 1);
+ /* FIXME Could pvl->pv->dev->pvid ever be different? */
+ if ((info = info_from_pvid(pvid_s, 0)) &&
+ !lvmcache_update_vgname_and_id(info, vg->name,
+ (char *) &vg->id,
+ vg->status, NULL))
+ return_0;
+ }
+
+ /* store text representation of vg to cache */
+ if (vg->cmd->current_settings.cache_vgmetadata)
+ _store_metadata(vg, precommitted);
+
+ return 1;
+}
+
+struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
+ struct device *dev,
+ const char *vgname, const char *vgid,
+ uint32_t vgstatus)
+{
+ struct label *label;
+ struct lvmcache_info *existing, *info;
+ char pvid_s[ID_LEN + 1] __attribute__((aligned(8)));
+
+ if (!_vgname_hash && !lvmcache_init()) {
+ log_error("Internal cache initialisation failed");
+ return NULL;
+ }
+
+ strncpy(pvid_s, pvid, sizeof(pvid_s));
+ pvid_s[sizeof(pvid_s) - 1] = '\0';
+
+ if (!(existing = info_from_pvid(pvid_s, 0)) &&
+ !(existing = info_from_pvid(dev->pvid, 0))) {
+ if (!(label = label_create(labeller)))
+ return_NULL;
+ if (!(info = dm_zalloc(sizeof(*info)))) {
+ log_error("lvmcache_info allocation failed");
+ label_destroy(label);
+ return NULL;
+ }
+
+ label->info = info;
+ info->label = label;
+ dm_list_init(&info->list);
+ info->dev = dev;
+ } else {
+ if (existing->dev != dev) {
+ /* Is the existing entry a duplicate pvid e.g. md ? */
+ if (dev_subsystem_part_major(existing->dev) &&
+ !dev_subsystem_part_major(dev)) {
+ log_very_verbose("Ignoring duplicate PV %s on "
+ "%s - using %s %s",
+ pvid, dev_name(dev),
+ dev_subsystem_name(existing->dev),
+ dev_name(existing->dev));
+ return NULL;
+ } else if (dm_is_dm_major(MAJOR(existing->dev->dev)) &&
+ !dm_is_dm_major(MAJOR(dev->dev))) {
+ log_very_verbose("Ignoring duplicate PV %s on "
+ "%s - using dm %s",
+ pvid, dev_name(dev),
+ dev_name(existing->dev));
+ return NULL;
+ } else if (!dev_subsystem_part_major(existing->dev) &&
+ dev_subsystem_part_major(dev))
+ log_very_verbose("Duplicate PV %s on %s - "
+ "using %s %s", pvid,
+ dev_name(existing->dev),
+ dev_subsystem_name(existing->dev),
+ dev_name(dev));
+ else if (!dm_is_dm_major(MAJOR(existing->dev->dev)) &&
+ dm_is_dm_major(MAJOR(dev->dev)))
+ log_very_verbose("Duplicate PV %s on %s - "
+ "using dm %s", pvid,
+ dev_name(existing->dev),
+ dev_name(dev));
+ /* FIXME If both dm, check dependencies */
+ //else if (dm_is_dm_major(MAJOR(existing->dev->dev)) &&
+ //dm_is_dm_major(MAJOR(dev->dev)))
+ //
+ else if (!strcmp(pvid_s, existing->dev->pvid))
+ log_error("Found duplicate PV %s: using %s not "
+ "%s", pvid, dev_name(dev),
+ dev_name(existing->dev));
+ }
+ if (strcmp(pvid_s, existing->dev->pvid))
+ log_debug("Updating pvid cache to %s (%s) from %s (%s)",
+ pvid_s, dev_name(dev),
+ existing->dev->pvid, dev_name(existing->dev));
+ /* Switch over to new preferred device */
+ existing->dev = dev;
+ info = existing;
+ /* Has labeller changed? */
+ if (info->label->labeller != labeller) {
+ label_destroy(info->label);
+ if (!(info->label = label_create(labeller)))
+ /* FIXME leaves info without label! */
+ return_NULL;
+ info->label->info = info;
+ }
+ label = info->label;
+ }
+
+ info->fmt = (const struct format_type *) labeller->private;
+ info->status |= CACHE_INVALID;
+
+ if (!_lvmcache_update_pvid(info, pvid_s)) {
+ if (!existing) {
+ dm_free(info);
+ label_destroy(label);
+ }
+ return NULL;
+ }
+
+ if (!lvmcache_update_vgname_and_id(info, vgname, vgid, vgstatus, NULL)) {
+ if (!existing) {
+ dm_hash_remove(_pvid_hash, pvid_s);
+ strcpy(info->dev->pvid, "");
+ dm_free(info);
+ label_destroy(label);
+ }
+ return NULL;
+ }
+
+ return info;
+}
+
+static void _lvmcache_destroy_entry(struct lvmcache_info *info)
+{
+ _vginfo_detach_info(info);
+ strcpy(info->dev->pvid, "");
+ label_destroy(info->label);
+ dm_free(info);
+}
+
+static void _lvmcache_destroy_vgnamelist(struct lvmcache_vginfo *vginfo)
+{
+ struct lvmcache_vginfo *next;
+
+ do {
+ next = vginfo->next;
+ if (!_free_vginfo(vginfo))
+ stack;
+ } while ((vginfo = next));
+}
+
+static void _lvmcache_destroy_lockname(struct dm_hash_node *n)
+{
+ char *vgname;
+
+ if (!dm_hash_get_data(_lock_hash, n))
+ return;
+
+ vgname = dm_hash_get_key(_lock_hash, n);
+
+ if (!strcmp(vgname, VG_GLOBAL))
+ _vg_global_lock_held = 1;
+ else
+ log_error(INTERNAL_ERROR "Volume Group %s was not unlocked",
+ dm_hash_get_key(_lock_hash, n));
+}
+
+void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans)
+{
+ struct dm_hash_node *n;
+ log_verbose("Wiping internal VG cache");
+
+ _has_scanned = 0;
+
+ if (_vgid_hash) {
+ dm_hash_destroy(_vgid_hash);
+ _vgid_hash = NULL;
+ }
+
+ if (_pvid_hash) {
+ dm_hash_iter(_pvid_hash, (dm_hash_iterate_fn) _lvmcache_destroy_entry);
+ dm_hash_destroy(_pvid_hash);
+ _pvid_hash = NULL;
+ }
+
+ if (_vgname_hash) {
+ dm_hash_iter(_vgname_hash,
+ (dm_hash_iterate_fn) _lvmcache_destroy_vgnamelist);
+ dm_hash_destroy(_vgname_hash);
+ _vgname_hash = NULL;
+ }
+
+ if (_lock_hash) {
+ dm_hash_iterate(n, _lock_hash)
+ _lvmcache_destroy_lockname(n);
+ dm_hash_destroy(_lock_hash);
+ _lock_hash = NULL;
+ }
+
+ if (!dm_list_empty(&_vginfos))
+ log_error(INTERNAL_ERROR "_vginfos list should be empty");
+ dm_list_init(&_vginfos);
+
+ if (retain_orphans)
+ init_lvmcache_orphans(cmd);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_CACHE_H
+#define _LVM_CACHE_H
+
+#include "dev-cache.h"
+#include "uuid.h"
+#include "label.h"
+#include "locking.h"
+
+#define ORPHAN_PREFIX VG_ORPHANS
+#define ORPHAN_VG_NAME(fmt) ORPHAN_PREFIX "_" fmt
+
+#define CACHE_INVALID 0x00000001
+#define CACHE_LOCKED 0x00000002
+
+/* LVM specific per-volume info */
+/* Eventual replacement for struct physical_volume perhaps? */
+
+struct cmd_context;
+struct format_type;
+struct volume_group;
+
+/* One per VG */
+struct lvmcache_vginfo {
+ struct dm_list list; /* Join these vginfos together */
+ struct dm_list infos; /* List head for lvmcache_infos */
+ const struct format_type *fmt;
+ char *vgname; /* "" == orphan */
+ uint32_t status;
+ char vgid[ID_LEN + 1];
+ char _padding[7];
+ struct lvmcache_vginfo *next; /* Another VG with same name? */
+ char *creation_host;
+ char *vgmetadata; /* Copy of VG metadata as format_text string */
+ unsigned precommitted; /* Is vgmetadata live or precommitted? */
+};
+
+/* One per device */
+struct lvmcache_info {
+ struct dm_list list; /* Join VG members together */
+ struct dm_list mdas; /* list head for metadata areas */
+ struct dm_list das; /* list head for data areas */
+ struct lvmcache_vginfo *vginfo; /* NULL == unknown */
+ struct label *label;
+ const struct format_type *fmt;
+ struct device *dev;
+ uint64_t device_size; /* Bytes */
+ uint32_t status;
+};
+
+int lvmcache_init(void);
+void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans);
+
+/* Set full_scan to 1 to reread every filtered device label or
+ * 2 to rescan /dev for new devices */
+int lvmcache_label_scan(struct cmd_context *cmd, int full_scan);
+
+/* Add/delete a device */
+struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
+ struct device *dev,
+ const char *vgname, const char *vgid,
+ uint32_t vgstatus);
+int lvmcache_add_orphan_vginfo(const char *vgname, struct format_type *fmt);
+void lvmcache_del(struct lvmcache_info *info);
+
+/* Update things */
+int lvmcache_update_vgname_and_id(struct lvmcache_info *info,
+ const char *vgname, const char *vgid,
+ uint32_t vgstatus, const char *hostname);
+int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted);
+
+void lvmcache_lock_vgname(const char *vgname, int read_only);
+void lvmcache_unlock_vgname(const char *vgname);
+int lvmcache_verify_lock_order(const char *vgname);
+
+/* Queries */
+const struct format_type *fmt_from_vgname(const char *vgname, const char *vgid, unsigned revalidate_labels);
+struct lvmcache_vginfo *vginfo_from_vgname(const char *vgname,
+ const char *vgid);
+struct lvmcache_vginfo *vginfo_from_vgid(const char *vgid);
+struct lvmcache_info *info_from_pvid(const char *pvid, int valid_only);
+const char *vgname_from_vgid(struct dm_pool *mem, const char *vgid);
+struct device *device_from_pvid(struct cmd_context *cmd, const struct id *pvid,
+ unsigned *scan_done_once);
+const char *pvid_from_devname(struct cmd_context *cmd,
+ const char *dev_name);
+char *lvmcache_vgname_from_pvid(struct cmd_context *cmd, const char *pvid);
+int vgs_locked(void);
+int vgname_is_locked(const char *vgname);
+
+/* Returns list of struct str_lists containing pool-allocated copy of vgnames */
+/* If include_internal is not set, return only proper vg names. */
+struct dm_list *lvmcache_get_vgnames(struct cmd_context *cmd,
+ int include_internal);
+
+/* Returns list of struct str_lists containing pool-allocated copy of vgids */
+/* If include_internal is not set, return only proper vg ids. */
+struct dm_list *lvmcache_get_vgids(struct cmd_context *cmd,
+ int include_internal);
+
+/* Returns list of struct str_lists containing pool-allocated copy of pvids */
+struct dm_list *lvmcache_get_pvids(struct cmd_context *cmd, const char *vgname,
+ const char *vgid);
+
+/* Returns cached volume group metadata. */
+struct volume_group *lvmcache_get_vg(const char *vgid, unsigned precommitted);
+void lvmcache_drop_metadata(const char *vgname, int drop_precommitted);
+void lvmcache_commit_metadata(const char *vgname);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_ERRORS_H
+#define _LVM_ERRORS_H
+
+#define ECMD_PROCESSED 1
+#define ENO_SUCH_CMD 2
+#define EINVALID_CMD_LINE 3
+#define ECMD_FAILED 5
+
+/* FIXME Also returned by cmdlib. */
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "metadata.h"
+#include "defaults.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "filter.h"
+#include "filter-composite.h"
+#include "filter-md.h"
+#include "filter-persistent.h"
+#include "filter-regex.h"
+#include "filter-sysfs.h"
+#include "label.h"
+#include "lvm-file.h"
+#include "format-text.h"
+#include "display.h"
+#include "memlock.h"
+#include "str_list.h"
+#include "segtype.h"
+#include "lvmcache.h"
+#include "dev-cache.h"
+#include "archiver.h"
+
+#ifdef HAVE_LIBDL
+#include "sharedlib.h"
+#endif
+
+#ifdef LVM1_INTERNAL
+#include "format1.h"
+#endif
+
+#ifdef POOL_INTERNAL
+#include "format_pool.h"
+#endif
+
+#include <locale.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <syslog.h>
+#include <time.h>
+
+#ifdef linux
+# include <malloc.h>
+#endif
+
+static int _get_env_vars(struct cmd_context *cmd)
+{
+ const char *e;
+
+ /* Set to "" to avoid using any system directory */
+ if ((e = getenv("LVM_SYSTEM_DIR"))) {
+ if (dm_snprintf(cmd->system_dir, sizeof(cmd->system_dir),
+ "%s", e) < 0) {
+ log_error("LVM_SYSTEM_DIR environment variable "
+ "is too long.");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void _get_sysfs_dir(struct cmd_context *cmd)
+{
+ static char proc_mounts[PATH_MAX];
+ static char *split[4], buffer[PATH_MAX + 16];
+ FILE *fp;
+ char *sys_mnt = NULL;
+
+ cmd->sysfs_dir[0] = '\0';
+ if (!*cmd->proc_dir) {
+ log_debug("No proc filesystem found: skipping sysfs detection");
+ return;
+ }
+
+ if (dm_snprintf(proc_mounts, sizeof(proc_mounts),
+ "%s/mounts", cmd->proc_dir) < 0) {
+ log_error("Failed to create /proc/mounts string for sysfs detection");
+ return;
+ }
+
+ if (!(fp = fopen(proc_mounts, "r"))) {
+ log_sys_error("_get_sysfs_dir: fopen %s", proc_mounts);
+ return;
+ }
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (dm_split_words(buffer, 4, 0, split) == 4 &&
+ !strcmp(split[2], "sysfs")) {
+ sys_mnt = split[1];
+ break;
+ }
+ }
+
+ if (fclose(fp))
+ log_sys_error("fclose", proc_mounts);
+
+ if (!sys_mnt) {
+ log_error("Failed to find sysfs mount point");
+ return;
+ }
+
+ strncpy(cmd->sysfs_dir, sys_mnt, sizeof(cmd->sysfs_dir));
+}
+
+static void _init_logging(struct cmd_context *cmd)
+{
+ int append = 1;
+ time_t t;
+
+ const char *log_file;
+ char timebuf[26];
+
+ /* Syslog */
+ cmd->default_settings.syslog =
+ find_config_tree_int(cmd, "log/syslog", DEFAULT_SYSLOG);
+ if (cmd->default_settings.syslog != 1)
+ fin_syslog();
+
+ if (cmd->default_settings.syslog > 1)
+ init_syslog(cmd->default_settings.syslog);
+
+ /* Debug level for log file output */
+ cmd->default_settings.debug =
+ find_config_tree_int(cmd, "log/level", DEFAULT_LOGLEVEL);
+ init_debug(cmd->default_settings.debug);
+
+ /* Verbose level for tty output */
+ cmd->default_settings.verbose =
+ find_config_tree_int(cmd, "log/verbose", DEFAULT_VERBOSE);
+ init_verbose(cmd->default_settings.verbose + VERBOSE_BASE_LEVEL);
+
+ /* Log message formatting */
+ init_indent(find_config_tree_int(cmd, "log/indent",
+ DEFAULT_INDENT));
+ init_abort_on_internal_errors(find_config_tree_int(cmd, "global/abort_on_internal_errors",
+ DEFAULT_ABORT_ON_INTERNAL_ERRORS));
+
+ cmd->default_settings.msg_prefix = find_config_tree_str(cmd,
+ "log/prefix",
+ DEFAULT_MSG_PREFIX);
+ init_msg_prefix(cmd->default_settings.msg_prefix);
+
+ cmd->default_settings.cmd_name = find_config_tree_int(cmd,
+ "log/command_names",
+ DEFAULT_CMD_NAME);
+ init_cmd_name(cmd->default_settings.cmd_name);
+
+ /* Test mode */
+ cmd->default_settings.test =
+ find_config_tree_int(cmd, "global/test", 0);
+ init_test(cmd->default_settings.test);
+
+ /* Settings for logging to file */
+ if (find_config_tree_int(cmd, "log/overwrite", DEFAULT_OVERWRITE))
+ append = 0;
+
+ log_file = find_config_tree_str(cmd, "log/file", 0);
+
+ if (log_file) {
+ release_log_memory();
+ fin_log();
+ init_log_file(log_file, append);
+ }
+
+ log_file = find_config_tree_str(cmd, "log/activate_file", 0);
+ if (log_file)
+ init_log_direct(log_file, append);
+
+ init_log_while_suspended(find_config_tree_int(cmd,
+ "log/activation", 0));
+
+ t = time(NULL);
+ ctime_r(&t, &timebuf[0]);
+ timebuf[24] = '\0';
+ log_verbose("Logging initialised at %s", timebuf);
+
+ /* Tell device-mapper about our logging */
+#ifdef DEVMAPPER_SUPPORT
+ dm_log_with_errno_init(print_log);
+#endif
+ reset_log_duplicated();
+ reset_lvm_errno(1);
+}
+
+static int _process_config(struct cmd_context *cmd)
+{
+ mode_t old_umask;
+ const char *read_ahead;
+ struct stat st;
+ const struct config_node *cn;
+ const struct config_value *cv;
+
+ /* umask */
+ cmd->default_settings.umask = find_config_tree_int(cmd,
+ "global/umask",
+ DEFAULT_UMASK);
+
+ if ((old_umask = umask((mode_t) cmd->default_settings.umask)) !=
+ (mode_t) cmd->default_settings.umask)
+ log_verbose("Set umask from %04o to %04o",
+ old_umask, cmd->default_settings.umask);
+
+ /* dev dir */
+ if (dm_snprintf(cmd->dev_dir, sizeof(cmd->dev_dir), "%s/",
+ find_config_tree_str(cmd, "devices/dir",
+ DEFAULT_DEV_DIR)) < 0) {
+ log_error("Device directory given in config file too long");
+ return 0;
+ }
+#ifdef DEVMAPPER_SUPPORT
+ dm_set_dev_dir(cmd->dev_dir);
+#endif
+
+ /* proc dir */
+ if (dm_snprintf(cmd->proc_dir, sizeof(cmd->proc_dir), "%s",
+ find_config_tree_str(cmd, "global/proc",
+ DEFAULT_PROC_DIR)) < 0) {
+ log_error("Device directory given in config file too long");
+ return 0;
+ }
+
+ if (*cmd->proc_dir && !dir_exists(cmd->proc_dir)) {
+ log_warn("WARNING: proc dir %s not found - some checks will be bypassed",
+ cmd->proc_dir);
+ cmd->proc_dir[0] = '\0';
+ }
+
+ /* FIXME Use global value of sysfs_dir everywhere instead cmd->sysfs_dir. */
+ _get_sysfs_dir(cmd);
+ set_sysfs_dir_path(cmd->sysfs_dir);
+
+ /* activation? */
+ cmd->default_settings.activation = find_config_tree_int(cmd,
+ "global/activation",
+ DEFAULT_ACTIVATION);
+ set_activation(cmd->default_settings.activation);
+
+ cmd->default_settings.suffix = find_config_tree_int(cmd,
+ "global/suffix",
+ DEFAULT_SUFFIX);
+
+ if (!(cmd->default_settings.unit_factor =
+ units_to_bytes(find_config_tree_str(cmd,
+ "global/units",
+ DEFAULT_UNITS),
+ &cmd->default_settings.unit_type))) {
+ log_error("Invalid units specification");
+ return 0;
+ }
+
+ read_ahead = find_config_tree_str(cmd, "activation/readahead", DEFAULT_READ_AHEAD);
+ if (!strcasecmp(read_ahead, "auto"))
+ cmd->default_settings.read_ahead = DM_READ_AHEAD_AUTO;
+ else if (!strcasecmp(read_ahead, "none"))
+ cmd->default_settings.read_ahead = DM_READ_AHEAD_NONE;
+ else {
+ log_error("Invalid readahead specification");
+ return 0;
+ }
+
+ cmd->default_settings.udev_rules = find_config_tree_int(cmd,
+ "activation/udev_rules",
+ DEFAULT_UDEV_RULES);
+
+ cmd->default_settings.udev_sync = find_config_tree_int(cmd,
+ "activation/udev_sync",
+ DEFAULT_UDEV_SYNC);
+
+ cmd->stripe_filler = find_config_tree_str(cmd,
+ "activation/missing_stripe_filler",
+ DEFAULT_STRIPE_FILLER);
+
+ /* FIXME Missing error code checks from the stats, not log_warn?, notify if setting overridden, delay message/check till it is actually used (eg consider if lvm shell - file could appear later after this check)? */
+ if (!strcmp(cmd->stripe_filler, "/dev/ioerror") &&
+ stat(cmd->stripe_filler, &st))
+ cmd->stripe_filler = "error";
+
+ if (strcmp(cmd->stripe_filler, "error")) {
+ if (stat(cmd->stripe_filler, &st)) {
+ log_warn("WARNING: activation/missing_stripe_filler = \"%s\" "
+ "is invalid,", cmd->stripe_filler);
+ log_warn(" stat failed: %s", strerror(errno));
+ log_warn("Falling back to \"error\" missing_stripe_filler.");
+ cmd->stripe_filler = "error";
+ } else if (!S_ISBLK(st.st_mode)) {
+ log_warn("WARNING: activation/missing_stripe_filler = \"%s\" "
+ "is not a block device.", cmd->stripe_filler);
+ log_warn("Falling back to \"error\" missing_stripe_filler.");
+ cmd->stripe_filler = "error";
+ }
+ }
+
+ cmd->si_unit_consistency = find_config_tree_int(cmd,
+ "global/si_unit_consistency",
+ DEFAULT_SI_UNIT_CONSISTENCY);
+
+ if ((cn = find_config_tree_node(cmd, "activation/mlock_filter")))
+ for (cv = cn->v; cv; cv = cv->next)
+ if ((cv->type != CFG_STRING) || !cv->v.str[0])
+ log_error("Ignoring invalid activation/mlock_filter entry in config file");
+
+ cmd->metadata_read_only = find_config_tree_int(cmd, "global/metadata_read_only",
+ DEFAULT_METADATA_READ_ONLY);
+
+ return 1;
+}
+
+static int _set_tag(struct cmd_context *cmd, const char *tag)
+{
+ log_very_verbose("Setting host tag: %s", dm_pool_strdup(cmd->libmem, tag));
+
+ if (!str_list_add(cmd->libmem, &cmd->tags, tag)) {
+ log_error("_set_tag: str_list_add %s failed", tag);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _check_host_filters(struct cmd_context *cmd, const struct config_node *hn,
+ int *passes)
+{
+ const struct config_node *cn;
+ const struct config_value *cv;
+
+ *passes = 1;
+
+ for (cn = hn; cn; cn = cn->sib) {
+ if (!cn->v)
+ continue;
+ if (!strcmp(cn->key, "host_list")) {
+ *passes = 0;
+ if (cn->v->type == CFG_EMPTY_ARRAY)
+ continue;
+ for (cv = cn->v; cv; cv = cv->next) {
+ if (cv->type != CFG_STRING) {
+ log_error("Invalid hostname string "
+ "for tag %s", cn->key);
+ return 0;
+ }
+ if (!strcmp(cv->v.str, cmd->hostname)) {
+ *passes = 1;
+ return 1;
+ }
+ }
+ }
+ if (!strcmp(cn->key, "host_filter")) {
+ log_error("host_filter not supported yet");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int _init_tags(struct cmd_context *cmd, struct config_tree *cft)
+{
+ const struct config_node *tn, *cn;
+ const char *tag;
+ int passes;
+
+ if (!(tn = find_config_node(cft->root, "tags")) || !tn->child)
+ return 1;
+
+ /* NB hosttags 0 when already 1 intentionally does not delete the tag */
+ if (!cmd->hosttags && find_config_int(cft->root, "tags/hosttags",
+ DEFAULT_HOSTTAGS)) {
+ /* FIXME Strip out invalid chars: only A-Za-z0-9_+.- */
+ if (!_set_tag(cmd, cmd->hostname))
+ return_0;
+ cmd->hosttags = 1;
+ }
+
+ for (cn = tn->child; cn; cn = cn->sib) {
+ if (cn->v)
+ continue;
+ tag = cn->key;
+ if (*tag == '@')
+ tag++;
+ if (!validate_name(tag)) {
+ log_error("Invalid tag in config file: %s", cn->key);
+ return 0;
+ }
+ if (cn->child) {
+ passes = 0;
+ if (!_check_host_filters(cmd, cn->child, &passes))
+ return_0;
+ if (!passes)
+ continue;
+ }
+ if (!_set_tag(cmd, tag))
+ return_0;
+ }
+
+ return 1;
+}
+
+static int _load_config_file(struct cmd_context *cmd, const char *tag)
+{
+ char config_file[PATH_MAX] = "";
+ const char *filler = "";
+ struct stat info;
+ struct config_tree_list *cfl;
+
+ if (*tag)
+ filler = "_";
+
+ if (dm_snprintf(config_file, sizeof(config_file), "%s/lvm%s%s.conf",
+ cmd->system_dir, filler, tag) < 0) {
+ log_error("LVM_SYSTEM_DIR or tag was too long");
+ return 0;
+ }
+
+ if (!(cfl = dm_pool_alloc(cmd->libmem, sizeof(*cfl)))) {
+ log_error("config_tree_list allocation failed");
+ return 0;
+ }
+
+ if (!(cfl->cft = create_config_tree(config_file, 0))) {
+ log_error("config_tree allocation failed");
+ return 0;
+ }
+
+ /* Is there a config file? */
+ if (stat(config_file, &info) == -1) {
+ if (errno == ENOENT) {
+ dm_list_add(&cmd->config_files, &cfl->list);
+ goto out;
+ }
+ log_sys_error("stat", config_file);
+ destroy_config_tree(cfl->cft);
+ return 0;
+ }
+
+ log_very_verbose("Loading config file: %s", config_file);
+ if (!read_config_file(cfl->cft)) {
+ log_error("Failed to load config file %s", config_file);
+ destroy_config_tree(cfl->cft);
+ return 0;
+ }
+
+ dm_list_add(&cmd->config_files, &cfl->list);
+
+ out:
+ if (*tag)
+ _init_tags(cmd, cfl->cft);
+ else
+ /* Use temporary copy of lvm.conf while loading other files */
+ cmd->cft = cfl->cft;
+
+ return 1;
+}
+
+/* Find and read first config file */
+static int _init_lvm_conf(struct cmd_context *cmd)
+{
+ /* No config file if LVM_SYSTEM_DIR is empty */
+ if (!*cmd->system_dir) {
+ if (!(cmd->cft = create_config_tree(NULL, 0))) {
+ log_error("Failed to create config tree");
+ return 0;
+ }
+ return 1;
+ }
+
+ if (!_load_config_file(cmd, ""))
+ return_0;
+
+ return 1;
+}
+
+/* Read any additional config files */
+static int _init_tag_configs(struct cmd_context *cmd)
+{
+ struct str_list *sl;
+
+ /* Tag list may grow while inside this loop */
+ dm_list_iterate_items(sl, &cmd->tags) {
+ if (!_load_config_file(cmd, sl->str))
+ return_0;
+ }
+
+ return 1;
+}
+
+static int _merge_config_files(struct cmd_context *cmd)
+{
+ struct config_tree_list *cfl;
+
+ /* Replace temporary duplicate copy of lvm.conf */
+ if (cmd->cft->root) {
+ if (!(cmd->cft = create_config_tree(NULL, 0))) {
+ log_error("Failed to create config tree");
+ return 0;
+ }
+ }
+
+ dm_list_iterate_items(cfl, &cmd->config_files) {
+ /* Merge all config trees into cmd->cft using merge/tag rules */
+ if (!merge_config_tree(cmd, cmd->cft, cfl->cft))
+ return_0;
+ }
+
+ return 1;
+}
+
+static void _destroy_tags(struct cmd_context *cmd)
+{
+ struct dm_list *slh, *slht;
+
+ dm_list_iterate_safe(slh, slht, &cmd->tags) {
+ dm_list_del(slh);
+ }
+}
+
+int config_files_changed(struct cmd_context *cmd)
+{
+ struct config_tree_list *cfl;
+
+ dm_list_iterate_items(cfl, &cmd->config_files) {
+ if (config_file_changed(cfl->cft))
+ return 1;
+ }
+
+ return 0;
+}
+
+static void _destroy_tag_configs(struct cmd_context *cmd)
+{
+ struct config_tree_list *cfl;
+
+ dm_list_iterate_items(cfl, &cmd->config_files) {
+ if (cfl->cft == cmd->cft)
+ cmd->cft = NULL;
+ destroy_config_tree(cfl->cft);
+ }
+
+ if (cmd->cft) {
+ destroy_config_tree(cmd->cft);
+ cmd->cft = NULL;
+ }
+
+ dm_list_init(&cmd->config_files);
+}
+
+static int _init_dev_cache(struct cmd_context *cmd)
+{
+ const struct config_node *cn;
+ const struct config_value *cv;
+
+ init_dev_disable_after_error_count(
+ find_config_tree_int(cmd, "devices/disable_after_error_count",
+ DEFAULT_DISABLE_AFTER_ERROR_COUNT));
+
+ if (!dev_cache_init(cmd))
+ return_0;
+
+ if (!(cn = find_config_tree_node(cmd, "devices/scan"))) {
+ if (!dev_cache_add_dir("/dev")) {
+ log_error("Failed to add /dev to internal "
+ "device cache");
+ return 0;
+ }
+ log_verbose("device/scan not in config file: "
+ "Defaulting to /dev");
+ return 1;
+ }
+
+ for (cv = cn->v; cv; cv = cv->next) {
+ if (cv->type != CFG_STRING) {
+ log_error("Invalid string in config file: "
+ "devices/scan");
+ return 0;
+ }
+
+ if (!dev_cache_add_dir(cv->v.str)) {
+ log_error("Failed to add %s to internal device cache",
+ cv->v.str);
+ return 0;
+ }
+ }
+
+ if (!(cn = find_config_tree_node(cmd, "devices/loopfiles")))
+ return 1;
+
+ for (cv = cn->v; cv; cv = cv->next) {
+ if (cv->type != CFG_STRING) {
+ log_error("Invalid string in config file: "
+ "devices/loopfiles");
+ return 0;
+ }
+
+ if (!dev_cache_add_loopfile(cv->v.str)) {
+ log_error("Failed to add loopfile %s to internal "
+ "device cache", cv->v.str);
+ return 0;
+ }
+ }
+
+
+ return 1;
+}
+
+#define MAX_FILTERS 4
+
+static struct dev_filter *_init_filter_components(struct cmd_context *cmd)
+{
+ unsigned nr_filt = 0;
+ const struct config_node *cn;
+ struct dev_filter *filters[MAX_FILTERS];
+
+ memset(filters, 0, sizeof(filters));
+
+ /*
+ * Filters listed in order: top one gets applied first.
+ * Failure to initialise some filters is not fatal.
+ * Update MAX_FILTERS definition above when adding new filters.
+ */
+
+ /*
+ * sysfs filter. Only available on 2.6 kernels. Non-critical.
+ * Listed first because it's very efficient at eliminating
+ * unavailable devices.
+ */
+ if (find_config_tree_bool(cmd, "devices/sysfs_scan",
+ DEFAULT_SYSFS_SCAN)) {
+ if ((filters[nr_filt] = sysfs_filter_create(cmd->sysfs_dir)))
+ nr_filt++;
+ }
+
+ /* regex filter. Optional. */
+ if (!(cn = find_config_tree_node(cmd, "devices/filter")))
+ log_very_verbose("devices/filter not found in config file: "
+ "no regex filter installed");
+
+ else if (!(filters[nr_filt++] = regex_filter_create(cn->v))) {
+ log_error("Failed to create regex device filter");
+ goto err;
+ }
+
+ /* device type filter. Required. */
+ cn = find_config_tree_node(cmd, "devices/types");
+ if (!(filters[nr_filt++] = lvm_type_filter_create(cmd->proc_dir, cn))) {
+ log_error("Failed to create lvm type filter");
+ goto err;
+ }
+
+ /* md component filter. Optional, non-critical. */
+ if (find_config_tree_bool(cmd, "devices/md_component_detection",
+ DEFAULT_MD_COMPONENT_DETECTION)) {
+ init_md_filtering(1);
+ if ((filters[nr_filt] = md_filter_create()))
+ nr_filt++;
+ }
+
+ /* Only build a composite filter if we really need it. */
+ return (nr_filt == 1) ?
+ filters[0] : composite_filter_create(nr_filt, filters);
+err:
+ nr_filt--; /* skip NULL */
+ while (nr_filt-- > 0)
+ filters[nr_filt]->destroy(filters[nr_filt]);
+ return NULL;
+}
+
+static int _init_filters(struct cmd_context *cmd, unsigned load_persistent_cache)
+{
+ const char *dev_cache = NULL, *cache_dir, *cache_file_prefix;
+ struct dev_filter *f3, *f4;
+ struct stat st;
+ char cache_file[PATH_MAX];
+
+ cmd->dump_filter = 0;
+
+ if (!(f3 = _init_filter_components(cmd)))
+ return 0;
+
+ init_ignore_suspended_devices(find_config_tree_int(cmd,
+ "devices/ignore_suspended_devices", DEFAULT_IGNORE_SUSPENDED_DEVICES));
+
+ /*
+ * If 'cache_dir' or 'cache_file_prefix' is set, ignore 'cache'.
+ */
+ cache_dir = find_config_tree_str(cmd, "devices/cache_dir", NULL);
+ cache_file_prefix = find_config_tree_str(cmd, "devices/cache_file_prefix", NULL);
+
+ if (cache_dir || cache_file_prefix) {
+ if (dm_snprintf(cache_file, sizeof(cache_file),
+ "%s%s%s/%s.cache",
+ cache_dir ? "" : cmd->system_dir,
+ cache_dir ? "" : "/",
+ cache_dir ? : DEFAULT_CACHE_SUBDIR,
+ cache_file_prefix ? : DEFAULT_CACHE_FILE_PREFIX) < 0) {
+ log_error("Persistent cache filename too long.");
+ return 0;
+ }
+ } else if (!(dev_cache = find_config_tree_str(cmd, "devices/cache", NULL)) &&
+ (dm_snprintf(cache_file, sizeof(cache_file),
+ "%s/%s/%s.cache",
+ cmd->system_dir, DEFAULT_CACHE_SUBDIR,
+ DEFAULT_CACHE_FILE_PREFIX) < 0)) {
+ log_error("Persistent cache filename too long.");
+ return 0;
+ }
+
+ if (!dev_cache)
+ dev_cache = cache_file;
+
+ if (!(f4 = persistent_filter_create(f3, dev_cache))) {
+ log_error("Failed to create persistent device filter");
+ return 0;
+ }
+
+ /* Should we ever dump persistent filter state? */
+ if (find_config_tree_int(cmd, "devices/write_cache_state", 1))
+ cmd->dump_filter = 1;
+
+ if (!*cmd->system_dir)
+ cmd->dump_filter = 0;
+
+ /*
+ * Only load persistent filter device cache on startup if it is newer
+ * than the config file and this is not a long-lived process.
+ */
+ if (load_persistent_cache && !cmd->is_long_lived &&
+ !stat(dev_cache, &st) &&
+ (st.st_ctime > config_file_timestamp(cmd->cft)) &&
+ !persistent_filter_load(f4, NULL))
+ log_verbose("Failed to load existing device cache from %s",
+ dev_cache);
+
+ cmd->filter = f4;
+
+ return 1;
+}
+
+struct format_type *get_format_by_name(struct cmd_context *cmd, const char *format)
+{
+ struct format_type *fmt;
+
+ dm_list_iterate_items(fmt, &cmd->formats)
+ if (!strcasecmp(fmt->name, format) ||
+ !strcasecmp(fmt->name + 3, format) ||
+ (fmt->alias && !strcasecmp(fmt->alias, format)))
+ return fmt;
+
+ return NULL;
+}
+
+static int _init_formats(struct cmd_context *cmd)
+{
+ const char *format;
+
+ struct format_type *fmt;
+
+#ifdef HAVE_LIBDL
+ const struct config_node *cn;
+#endif
+
+ label_init();
+
+#ifdef LVM1_INTERNAL
+ if (!(fmt = init_lvm1_format(cmd)))
+ return 0;
+ fmt->library = NULL;
+ dm_list_add(&cmd->formats, &fmt->list);
+#endif
+
+#ifdef POOL_INTERNAL
+ if (!(fmt = init_pool_format(cmd)))
+ return 0;
+ fmt->library = NULL;
+ dm_list_add(&cmd->formats, &fmt->list);
+#endif
+
+#ifdef HAVE_LIBDL
+ /* Load any formats in shared libs if not static */
+ if (!is_static() &&
+ (cn = find_config_tree_node(cmd, "global/format_libraries"))) {
+
+ const struct config_value *cv;
+ struct format_type *(*init_format_fn) (struct cmd_context *);
+ void *lib;
+
+ for (cv = cn->v; cv; cv = cv->next) {
+ if (cv->type != CFG_STRING) {
+ log_error("Invalid string in config file: "
+ "global/format_libraries");
+ return 0;
+ }
+ if (!(lib = load_shared_library(cmd, cv->v.str,
+ "format", 0)))
+ return_0;
+
+ if (!(init_format_fn = dlsym(lib, "init_format"))) {
+ log_error("Shared library %s does not contain "
+ "format functions", cv->v.str);
+ dlclose(lib);
+ return 0;
+ }
+
+ if (!(fmt = init_format_fn(cmd))) {
+ dlclose(lib);
+ return_0;
+ }
+
+ fmt->library = lib;
+ dm_list_add(&cmd->formats, &fmt->list);
+ }
+ }
+#endif
+
+ if (!(fmt = create_text_format(cmd)))
+ return 0;
+ fmt->library = NULL;
+ dm_list_add(&cmd->formats, &fmt->list);
+
+ cmd->fmt_backup = fmt;
+
+ format = find_config_tree_str(cmd, "global/format",
+ DEFAULT_FORMAT);
+
+ dm_list_iterate_items(fmt, &cmd->formats) {
+ if (!strcasecmp(fmt->name, format) ||
+ (fmt->alias && !strcasecmp(fmt->alias, format))) {
+ cmd->default_settings.fmt_name = fmt->name;
+ cmd->fmt = fmt;
+ return 1;
+ }
+ }
+
+ log_error("_init_formats: Default format (%s) not found", format);
+ return 0;
+}
+
+int init_lvmcache_orphans(struct cmd_context *cmd)
+{
+ struct format_type *fmt;
+
+ dm_list_iterate_items(fmt, &cmd->formats)
+ if (!lvmcache_add_orphan_vginfo(fmt->orphan_vg_name, fmt))
+ return_0;
+
+ return 1;
+}
+
+struct segtype_library {
+ struct cmd_context *cmd;
+ void *lib;
+ const char *libname;
+};
+
+int lvm_register_segtype(struct segtype_library *seglib,
+ struct segment_type *segtype)
+{
+ struct segment_type *segtype2;
+
+ segtype->library = seglib->lib;
+ segtype->cmd = seglib->cmd;
+
+ dm_list_iterate_items(segtype2, &seglib->cmd->segtypes) {
+ if (strcmp(segtype2->name, segtype->name))
+ continue;
+ log_error("Duplicate segment type %s: "
+ "unloading shared library %s",
+ segtype->name, seglib->libname);
+ segtype->ops->destroy(segtype);
+ return 0;
+ }
+
+ dm_list_add(&seglib->cmd->segtypes, &segtype->list);
+
+ return 1;
+}
+
+static int _init_single_segtype(struct cmd_context *cmd,
+ struct segtype_library *seglib)
+{
+ struct segment_type *(*init_segtype_fn) (struct cmd_context *);
+ struct segment_type *segtype;
+
+ if (!(init_segtype_fn = dlsym(seglib->lib, "init_segtype"))) {
+ log_error("Shared library %s does not contain segment type "
+ "functions", seglib->libname);
+ return 0;
+ }
+
+ if (!(segtype = init_segtype_fn(seglib->cmd)))
+ return_0;
+
+ return lvm_register_segtype(seglib, segtype);
+}
+
+static int _init_segtypes(struct cmd_context *cmd)
+{
+ struct segment_type *segtype;
+ struct segtype_library seglib = { .cmd = cmd };
+
+#ifdef HAVE_LIBDL
+ const struct config_node *cn;
+#endif
+
+ if (!(segtype = init_striped_segtype(cmd)))
+ return 0;
+ segtype->library = NULL;
+ dm_list_add(&cmd->segtypes, &segtype->list);
+
+ if (!(segtype = init_zero_segtype(cmd)))
+ return 0;
+ segtype->library = NULL;
+ dm_list_add(&cmd->segtypes, &segtype->list);
+
+ if (!(segtype = init_error_segtype(cmd)))
+ return 0;
+ segtype->library = NULL;
+ dm_list_add(&cmd->segtypes, &segtype->list);
+
+ if (!(segtype = init_free_segtype(cmd)))
+ return 0;
+ segtype->library = NULL;
+ dm_list_add(&cmd->segtypes, &segtype->list);
+
+#ifdef SNAPSHOT_INTERNAL
+ if (!(segtype = init_snapshot_segtype(cmd)))
+ return 0;
+ segtype->library = NULL;
+ dm_list_add(&cmd->segtypes, &segtype->list);
+#endif
+
+#ifdef MIRRORED_INTERNAL
+ if (!(segtype = init_mirrored_segtype(cmd)))
+ return 0;
+ segtype->library = NULL;
+ dm_list_add(&cmd->segtypes, &segtype->list);
+#endif
+
+#ifdef REPLICATOR_INTERNAL
+ if (!init_replicator_segtype(&seglib))
+ return 0;
+#endif
+
+#ifdef HAVE_LIBDL
+ /* Load any formats in shared libs unless static */
+ if (!is_static() &&
+ (cn = find_config_tree_node(cmd, "global/segment_libraries"))) {
+
+ const struct config_value *cv;
+ int (*init_multiple_segtypes_fn) (struct cmd_context *,
+ struct segtype_library *);
+
+ for (cv = cn->v; cv; cv = cv->next) {
+ if (cv->type != CFG_STRING) {
+ log_error("Invalid string in config file: "
+ "global/segment_libraries");
+ return 0;
+ }
+ seglib.libname = cv->v.str;
+ if (!(seglib.lib = load_shared_library(cmd,
+ seglib.libname,
+ "segment type", 0)))
+ return_0;
+
+ if ((init_multiple_segtypes_fn =
+ dlsym(seglib.lib, "init_multiple_segtypes"))) {
+ if (dlsym(seglib.lib, "init_segtype"))
+ log_warn("WARNING: Shared lib %s has "
+ "conflicting init fns. Using"
+ " init_multiple_segtypes().",
+ seglib.libname);
+ } else
+ init_multiple_segtypes_fn =
+ _init_single_segtype;
+
+ if (!init_multiple_segtypes_fn(cmd, &seglib)) {
+ struct dm_list *sgtl, *tmp;
+ log_error("init_multiple_segtypes() failed: "
+ "Unloading shared library %s",
+ seglib.libname);
+ dm_list_iterate_safe(sgtl, tmp, &cmd->segtypes) {
+ segtype = dm_list_item(sgtl, struct segment_type);
+ if (segtype->library == seglib.lib) {
+ dm_list_del(&segtype->list);
+ segtype->ops->destroy(segtype);
+ }
+ }
+ dlclose(seglib.lib);
+ return_0;
+ }
+ }
+ }
+#endif
+
+ return 1;
+}
+
+static int _init_hostname(struct cmd_context *cmd)
+{
+ struct utsname uts;
+
+ if (uname(&uts)) {
+ log_sys_error("uname", "_init_hostname");
+ return 0;
+ }
+
+ if (!(cmd->hostname = dm_pool_strdup(cmd->libmem, uts.nodename))) {
+ log_error("_init_hostname: dm_pool_strdup failed");
+ return 0;
+ }
+
+ if (!(cmd->kernel_vsn = dm_pool_strdup(cmd->libmem, uts.release))) {
+ log_error("_init_hostname: dm_pool_strdup kernel_vsn failed");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _init_backup(struct cmd_context *cmd)
+{
+ uint32_t days, min;
+ char default_dir[PATH_MAX];
+ const char *dir;
+
+ if (!cmd->system_dir[0]) {
+ log_warn("WARNING: Metadata changes will NOT be backed up");
+ backup_init(cmd, "", 0);
+ archive_init(cmd, "", 0, 0, 0);
+ return 1;
+ }
+
+ /* set up archiving */
+ cmd->default_settings.archive =
+ find_config_tree_bool(cmd, "backup/archive",
+ DEFAULT_ARCHIVE_ENABLED);
+
+ days = (uint32_t) find_config_tree_int(cmd, "backup/retain_days",
+ DEFAULT_ARCHIVE_DAYS);
+
+ min = (uint32_t) find_config_tree_int(cmd, "backup/retain_min",
+ DEFAULT_ARCHIVE_NUMBER);
+
+ if (dm_snprintf
+ (default_dir, sizeof(default_dir), "%s/%s", cmd->system_dir,
+ DEFAULT_ARCHIVE_SUBDIR) == -1) {
+ log_error("Couldn't create default archive path '%s/%s'.",
+ cmd->system_dir, DEFAULT_ARCHIVE_SUBDIR);
+ return 0;
+ }
+
+ dir = find_config_tree_str(cmd, "backup/archive_dir",
+ default_dir);
+
+ if (!archive_init(cmd, dir, days, min,
+ cmd->default_settings.archive)) {
+ log_debug("archive_init failed.");
+ return 0;
+ }
+
+ /* set up the backup */
+ cmd->default_settings.backup =
+ find_config_tree_bool(cmd, "backup/backup",
+ DEFAULT_BACKUP_ENABLED);
+
+ if (dm_snprintf
+ (default_dir, sizeof(default_dir), "%s/%s", cmd->system_dir,
+ DEFAULT_BACKUP_SUBDIR) == -1) {
+ log_error("Couldn't create default backup path '%s/%s'.",
+ cmd->system_dir, DEFAULT_BACKUP_SUBDIR);
+ return 0;
+ }
+
+ dir = find_config_tree_str(cmd, "backup/backup_dir", default_dir);
+
+ if (!backup_init(cmd, dir, cmd->default_settings.backup)) {
+ log_debug("backup_init failed.");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void _init_rand(struct cmd_context *cmd)
+{
+ if (read_urandom(&cmd->rand_seed, sizeof(cmd->rand_seed))) {
+ reset_lvm_errno(1);
+ return;
+ }
+
+ cmd->rand_seed = (unsigned) time(NULL) + (unsigned) getpid();
+ reset_lvm_errno(1);
+}
+
+static void _init_globals(struct cmd_context *cmd)
+{
+ init_full_scan_done(0);
+ init_mirror_in_sync(0);
+
+}
+
+/* Entry point */
+struct cmd_context *create_toolcontext(unsigned is_long_lived,
+ const char *system_dir)
+{
+ struct cmd_context *cmd;
+
+#ifdef M_MMAP_MAX
+ mallopt(M_MMAP_MAX, 0);
+#endif
+
+ if (!setlocale(LC_ALL, ""))
+ log_very_verbose("setlocale failed");
+
+#ifdef INTL_PACKAGE
+ bindtextdomain(INTL_PACKAGE, LOCALEDIR);
+#endif
+
+ init_syslog(DEFAULT_LOG_FACILITY);
+
+ if (!(cmd = dm_zalloc(sizeof(*cmd)))) {
+ log_error("Failed to allocate command context");
+ return NULL;
+ }
+ cmd->is_long_lived = is_long_lived;
+ cmd->handles_missing_pvs = 0;
+ cmd->handles_unknown_segments = 0;
+ cmd->independent_metadata_areas = 0;
+ cmd->hosttags = 0;
+ dm_list_init(&cmd->arg_value_groups);
+ dm_list_init(&cmd->formats);
+ dm_list_init(&cmd->segtypes);
+ dm_list_init(&cmd->tags);
+ dm_list_init(&cmd->config_files);
+
+ /* FIXME Make this configurable? */
+ reset_lvm_errno(1);
+
+ /*
+ * Environment variable LVM_SYSTEM_DIR overrides this below.
+ */
+ if (system_dir)
+ strncpy(cmd->system_dir, system_dir, sizeof(cmd->system_dir) - 1);
+ else
+ strcpy(cmd->system_dir, DEFAULT_SYS_DIR);
+
+ if (!_get_env_vars(cmd))
+ goto_out;
+
+ /* Create system directory if it doesn't already exist */
+ if (*cmd->system_dir && !dm_create_dir(cmd->system_dir)) {
+ log_error("Failed to create LVM2 system dir for metadata backups, config "
+ "files and internal cache.");
+ log_error("Set environment variable LVM_SYSTEM_DIR to alternative location "
+ "or empty string.");
+ goto out;
+ }
+
+ if (!(cmd->libmem = dm_pool_create("library", 4 * 1024))) {
+ log_error("Library memory pool creation failed");
+ goto out;
+ }
+
+ if (!_init_lvm_conf(cmd))
+ goto_out;
+
+ _init_logging(cmd);
+
+ if (!_init_hostname(cmd))
+ goto_out;
+
+ if (!_init_tags(cmd, cmd->cft))
+ goto_out;
+
+ if (!_init_tag_configs(cmd))
+ goto_out;
+
+ if (!_merge_config_files(cmd))
+ goto_out;
+
+ if (!_process_config(cmd))
+ goto_out;
+
+ if (!_init_dev_cache(cmd))
+ goto_out;
+
+ if (!_init_filters(cmd, 1))
+ goto_out;
+
+ if (!(cmd->mem = dm_pool_create("command", 4 * 1024))) {
+ log_error("Command memory pool creation failed");
+ goto out;
+ }
+
+ memlock_init(cmd);
+
+ if (!_init_formats(cmd))
+ goto_out;
+
+ if (!init_lvmcache_orphans(cmd))
+ goto_out;
+
+ if (!_init_segtypes(cmd))
+ goto_out;
+
+ if (!_init_backup(cmd))
+ goto_out;
+
+ _init_rand(cmd);
+
+ _init_globals(cmd);
+
+ cmd->default_settings.cache_vgmetadata = 1;
+ cmd->current_settings = cmd->default_settings;
+
+ cmd->config_valid = 1;
+out:
+ return cmd;
+}
+
+static void _destroy_formats(struct cmd_context *cmd, struct dm_list *formats)
+{
+ struct dm_list *fmtl, *tmp;
+ struct format_type *fmt;
+ void *lib;
+
+ dm_list_iterate_safe(fmtl, tmp, formats) {
+ fmt = dm_list_item(fmtl, struct format_type);
+ dm_list_del(&fmt->list);
+ lib = fmt->library;
+ fmt->ops->destroy(fmt);
+#ifdef HAVE_LIBDL
+ if (lib)
+ dlclose(lib);
+#endif
+ }
+
+ cmd->independent_metadata_areas = 0;
+}
+
+static void _destroy_segtypes(struct dm_list *segtypes)
+{
+ struct dm_list *sgtl, *tmp;
+ struct segment_type *segtype;
+ void *lib;
+
+ dm_list_iterate_safe(sgtl, tmp, segtypes) {
+ segtype = dm_list_item(sgtl, struct segment_type);
+ dm_list_del(&segtype->list);
+ lib = segtype->library;
+ segtype->ops->destroy(segtype);
+#ifdef HAVE_LIBDL
+ /*
+ * If no segtypes remain from this library, close it.
+ */
+ if (lib) {
+ struct segment_type *segtype2;
+ dm_list_iterate_items(segtype2, segtypes)
+ if (segtype2->library == lib)
+ goto skip_dlclose;
+ dlclose(lib);
+skip_dlclose:
+ ;
+ }
+#endif
+ }
+}
+
+int refresh_filters(struct cmd_context *cmd)
+{
+ int r, saved_ignore_suspended_devices = ignore_suspended_devices();
+
+ if (cmd->filter) {
+ cmd->filter->destroy(cmd->filter);
+ cmd->filter = NULL;
+ }
+
+ r = _init_filters(cmd, 0);
+
+ /*
+ * During repair code must not reset suspended flag.
+ */
+ init_ignore_suspended_devices(saved_ignore_suspended_devices);
+
+ return r;
+}
+
+int refresh_toolcontext(struct cmd_context *cmd)
+{
+ log_verbose("Reloading config files");
+
+ /*
+ * Don't update the persistent filter cache as we will
+ * perform a full rescan.
+ */
+
+ activation_release();
+ lvmcache_destroy(cmd, 0);
+ label_exit();
+ _destroy_segtypes(&cmd->segtypes);
+ _destroy_formats(cmd, &cmd->formats);
+ if (cmd->filter) {
+ cmd->filter->destroy(cmd->filter);
+ cmd->filter = NULL;
+ }
+ dev_cache_exit();
+ _destroy_tags(cmd);
+ _destroy_tag_configs(cmd);
+
+ cmd->config_valid = 0;
+
+ cmd->hosttags = 0;
+
+ if (!_init_lvm_conf(cmd))
+ return 0;
+
+ _init_logging(cmd);
+
+ if (!_init_tags(cmd, cmd->cft))
+ return 0;
+
+ if (!_init_tag_configs(cmd))
+ return 0;
+
+ if (!_merge_config_files(cmd))
+ return 0;
+
+ if (!_process_config(cmd))
+ return 0;
+
+ if (!_init_dev_cache(cmd))
+ return 0;
+
+ if (!_init_filters(cmd, 0))
+ return 0;
+
+ if (!_init_formats(cmd))
+ return 0;
+
+ if (!init_lvmcache_orphans(cmd))
+ return 0;
+
+ if (!_init_segtypes(cmd))
+ return 0;
+
+ if (!_init_backup(cmd))
+ return 0;
+
+ cmd->config_valid = 1;
+
+ reset_lvm_errno(1);
+ return 1;
+}
+
+void destroy_toolcontext(struct cmd_context *cmd)
+{
+ if (cmd->dump_filter)
+ persistent_filter_dump(cmd->filter, 1);
+
+ archive_exit(cmd);
+ backup_exit(cmd);
+ lvmcache_destroy(cmd, 0);
+ label_exit();
+ _destroy_segtypes(&cmd->segtypes);
+ _destroy_formats(cmd, &cmd->formats);
+ if (cmd->filter)
+ cmd->filter->destroy(cmd->filter);
+ if (cmd->mem)
+ dm_pool_destroy(cmd->mem);
+ dev_cache_exit();
+ _destroy_tags(cmd);
+ _destroy_tag_configs(cmd);
+ if (cmd->libmem)
+ dm_pool_destroy(cmd->libmem);
+ dm_free(cmd);
+
+ release_log_memory();
+ activation_exit();
+ reset_log_duplicated();
+ fin_log();
+ fin_syslog();
+ reset_lvm_errno(0);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_TOOLCONTEXT_H
+#define _LVM_TOOLCONTEXT_H
+
+#include "dev-cache.h"
+
+#include <stdio.h>
+#include <limits.h>
+
+/*
+ * Config options that can be changed while commands are processed
+ */
+struct config_info {
+ int debug;
+ int verbose;
+ int test;
+ int syslog;
+ int activation;
+ int suffix;
+ int archive; /* should we archive ? */
+ int backup; /* should we backup ? */
+ int read_ahead; /* DM_READ_AHEAD_NONE or _AUTO */
+ int udev_rules;
+ int udev_sync;
+ int cache_vgmetadata;
+ const char *msg_prefix;
+ const char *fmt_name;
+ uint64_t unit_factor;
+ int cmd_name; /* Show command name? */
+ mode_t umask;
+ char unit_type;
+ char _padding[1];
+};
+
+struct config_tree;
+struct archive_params;
+struct backup_params;
+struct arg_values;
+
+/* FIXME Split into tool & library contexts */
+/* command-instance-related variables needed by library */
+struct cmd_context {
+ struct dm_pool *libmem; /* For permanent config data */
+ struct dm_pool *mem; /* Transient: Cleared between each command */
+
+ const struct format_type *fmt; /* Current format to use by default */
+ struct format_type *fmt_backup; /* Format to use for backups */
+
+ struct dm_list formats; /* Available formats */
+ struct dm_list segtypes; /* Available segment types */
+ const char *hostname;
+ const char *kernel_vsn;
+
+ unsigned rand_seed;
+ const char *cmd_line;
+ struct command *command;
+ char **argv;
+ struct arg_values *arg_values;
+ struct dm_list arg_value_groups;
+ unsigned is_long_lived:1; /* Optimises persistent_filter handling */
+ unsigned handles_missing_pvs:1;
+ unsigned handles_unknown_segments:1;
+ unsigned partial_activation:1;
+ unsigned si_unit_consistency:1;
+ unsigned metadata_read_only:1;
+
+ unsigned independent_metadata_areas:1; /* Active formats have MDAs outside PVs */
+
+ struct dev_filter *filter;
+ int dump_filter; /* Dump filter when exiting? */
+
+ struct dm_list config_files;
+ int config_valid;
+ struct config_tree *cft;
+ struct config_tree *cft_override;
+ struct config_info default_settings;
+ struct config_info current_settings;
+
+ struct archive_params *archive_params;
+ struct backup_params *backup_params;
+ const char *stripe_filler;
+
+ /* List of defined tags */
+ struct dm_list tags;
+ int hosttags;
+
+ char system_dir[PATH_MAX];
+ char dev_dir[PATH_MAX];
+ char proc_dir[PATH_MAX];
+ char sysfs_dir[PATH_MAX]; /* FIXME Use global value instead. */
+};
+
+/*
+ * system_dir may be NULL to use the default value.
+ * The environment variable LVM_SYSTEM_DIR always takes precedence.
+ */
+struct cmd_context *create_toolcontext(unsigned is_long_lived,
+ const char *system_dir);
+void destroy_toolcontext(struct cmd_context *cmd);
+int refresh_toolcontext(struct cmd_context *cmd);
+int refresh_filters(struct cmd_context *cmd);
+int config_files_changed(struct cmd_context *cmd);
+int init_lvmcache_orphans(struct cmd_context *cmd);
+
+struct format_type *get_format_by_name(struct cmd_context *cmd, const char *format);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "config.h"
+#include "crc.h"
+#include "device.h"
+#include "str_list.h"
+#include "toolcontext.h"
+#include "lvm-string.h"
+#include "lvm-file.h"
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#define SECTION_B_CHAR '{'
+#define SECTION_E_CHAR '}'
+
+enum {
+ TOK_INT,
+ TOK_FLOAT,
+ TOK_STRING, /* Single quotes */
+ TOK_STRING_ESCAPED, /* Double quotes */
+ TOK_EQ,
+ TOK_SECTION_B,
+ TOK_SECTION_E,
+ TOK_ARRAY_B,
+ TOK_ARRAY_E,
+ TOK_IDENTIFIER,
+ TOK_COMMA,
+ TOK_EOF
+};
+
+struct parser {
+ const char *fb, *fe; /* file limits */
+
+ int t; /* token limits and type */
+ const char *tb, *te;
+
+ int fd; /* descriptor for file being parsed */
+ int line; /* line number we are on */
+
+ struct dm_pool *mem;
+};
+
+struct cs {
+ struct config_tree cft;
+ struct dm_pool *mem;
+ time_t timestamp;
+ char *filename;
+ int exists;
+ int keep_open;
+ struct device *dev;
+};
+
+struct output_line {
+ FILE *fp;
+ struct dm_pool *mem;
+ putline_fn putline;
+ void *putline_baton;
+};
+
+static void _get_token(struct parser *p, int tok_prev);
+static void _eat_space(struct parser *p);
+static struct config_node *_file(struct parser *p);
+static struct config_node *_section(struct parser *p);
+static struct config_value *_value(struct parser *p);
+static struct config_value *_type(struct parser *p);
+static int _match_aux(struct parser *p, int t);
+static struct config_value *_create_value(struct dm_pool *mem);
+static struct config_node *_create_node(struct dm_pool *mem);
+static char *_dup_tok(struct parser *p);
+
+static const int sep = '/';
+
+#define MAX_INDENT 32
+
+#define match(t) do {\
+ if (!_match_aux(p, (t))) {\
+ log_error("Parse error at byte %" PRIptrdiff_t " (line %d): unexpected token", \
+ p->tb - p->fb + 1, p->line); \
+ return 0;\
+ } \
+} while(0);
+
+static int _tok_match(const char *str, const char *b, const char *e)
+{
+ while (*str && (b != e)) {
+ if (*str++ != *b++)
+ return 0;
+ }
+
+ return !(*str || (b != e));
+}
+
+/*
+ * public interface
+ */
+struct config_tree *create_config_tree(const char *filename, int keep_open)
+{
+ struct cs *c;
+ struct dm_pool *mem = dm_pool_create("config", 10 * 1024);
+
+ if (!mem) {
+ log_error("Failed to allocate config pool.");
+ return 0;
+ }
+
+ if (!(c = dm_pool_zalloc(mem, sizeof(*c)))) {
+ log_error("Failed to allocate config tree.");
+ dm_pool_destroy(mem);
+ return 0;
+ }
+
+ c->mem = mem;
+ c->cft.root = (struct config_node *) NULL;
+ c->timestamp = 0;
+ c->exists = 0;
+ c->keep_open = keep_open;
+ c->dev = 0;
+ if (filename)
+ c->filename = dm_pool_strdup(c->mem, filename);
+ return &c->cft;
+}
+
+void destroy_config_tree(struct config_tree *cft)
+{
+ struct cs *c = (struct cs *) cft;
+
+ if (c->dev)
+ dev_close(c->dev);
+
+ dm_pool_destroy(c->mem);
+}
+
+static int _parse_config_file(struct parser *p, struct config_tree *cft)
+{
+ p->tb = p->te = p->fb;
+ p->line = 1;
+ _get_token(p, TOK_SECTION_E);
+ if (!(cft->root = _file(p)))
+ return_0;
+
+ return 1;
+}
+
+struct config_tree *create_config_tree_from_string(struct cmd_context *cmd __attribute__((unused)),
+ const char *config_settings)
+{
+ struct cs *c;
+ struct config_tree *cft;
+ struct parser *p;
+
+ if (!(cft = create_config_tree(NULL, 0)))
+ return_NULL;
+
+ c = (struct cs *) cft;
+ if (!(p = dm_pool_alloc(c->mem, sizeof(*p)))) {
+ log_error("Failed to allocate config tree parser.");
+ destroy_config_tree(cft);
+ return NULL;
+ }
+
+ p->mem = c->mem;
+ p->fb = config_settings;
+ p->fe = config_settings + strlen(config_settings);
+
+ if (!_parse_config_file(p, cft)) {
+ destroy_config_tree(cft);
+ return_NULL;
+ }
+
+ return cft;
+}
+
+int override_config_tree_from_string(struct cmd_context *cmd,
+ const char *config_settings)
+{
+ if (!(cmd->cft_override = create_config_tree_from_string(cmd,config_settings))) {
+ log_error("Failed to set overridden configuration entries.");
+ return 1;
+ }
+
+ return 0;
+}
+
+int read_config_fd(struct config_tree *cft, struct device *dev,
+ off_t offset, size_t size, off_t offset2, size_t size2,
+ checksum_fn_t checksum_fn, uint32_t checksum)
+{
+ struct cs *c = (struct cs *) cft;
+ struct parser *p;
+ int r = 0;
+ int use_mmap = 1;
+ off_t mmap_offset = 0;
+ char *buf = NULL;
+
+ if (!(p = dm_pool_alloc(c->mem, sizeof(*p))))
+ return_0;
+ p->mem = c->mem;
+
+ /* Only use mmap with regular files */
+ if (!(dev->flags & DEV_REGULAR) || size2)
+ use_mmap = 0;
+
+ if (use_mmap) {
+ mmap_offset = offset % lvm_getpagesize();
+ /* memory map the file */
+ p->fb = mmap((caddr_t) 0, size + mmap_offset, PROT_READ,
+ MAP_PRIVATE, dev_fd(dev), offset - mmap_offset);
+ if (p->fb == (caddr_t) (-1)) {
+ log_sys_error("mmap", dev_name(dev));
+ goto out;
+ }
+ p->fb = p->fb + mmap_offset;
+ } else {
+ if (!(buf = dm_malloc(size + size2)))
+ return_0;
+ if (!dev_read_circular(dev, (uint64_t) offset, size,
+ (uint64_t) offset2, size2, buf)) {
+ goto out;
+ }
+ p->fb = buf;
+ }
+
+ if (checksum_fn && checksum !=
+ (checksum_fn(checksum_fn(INITIAL_CRC, (const uint8_t *)p->fb, size),
+ (const uint8_t *)(p->fb + size), size2))) {
+ log_error("%s: Checksum error", dev_name(dev));
+ goto out;
+ }
+
+ p->fe = p->fb + size + size2;
+
+ if (!_parse_config_file(p, cft))
+ goto_out;
+
+ r = 1;
+
+ out:
+ if (!use_mmap)
+ dm_free(buf);
+ else {
+ /* unmap the file */
+ if (munmap((char *) (p->fb - mmap_offset), size + mmap_offset)) {
+ log_sys_error("munmap", dev_name(dev));
+ r = 0;
+ }
+ }
+
+ return r;
+}
+
+int read_config_file(struct config_tree *cft)
+{
+ struct cs *c = (struct cs *) cft;
+ struct stat info;
+ int r = 1;
+
+ if (stat(c->filename, &info)) {
+ log_sys_error("stat", c->filename);
+ c->exists = 0;
+ return 0;
+ }
+
+ if (!S_ISREG(info.st_mode)) {
+ log_error("%s is not a regular file", c->filename);
+ c->exists = 0;
+ return 0;
+ }
+
+ c->exists = 1;
+
+ if (info.st_size == 0) {
+ log_verbose("%s is empty", c->filename);
+ return 1;
+ }
+
+ if (!c->dev) {
+ if (!(c->dev = dev_create_file(c->filename, NULL, NULL, 1)))
+ return_0;
+
+ if (!dev_open_flags(c->dev, O_RDONLY, 0, 0)) {
+ c->dev = 0;
+ return_0;
+ }
+ }
+
+ r = read_config_fd(cft, c->dev, 0, (size_t) info.st_size, 0, 0,
+ (checksum_fn_t) NULL, 0);
+
+ if (!c->keep_open) {
+ dev_close(c->dev);
+ c->dev = 0;
+ }
+
+ c->timestamp = info.st_ctime;
+
+ return r;
+}
+
+time_t config_file_timestamp(struct config_tree *cft)
+{
+ struct cs *c = (struct cs *) cft;
+
+ return c->timestamp;
+}
+
+/*
+ * Return 1 if config files ought to be reloaded
+ */
+int config_file_changed(struct config_tree *cft)
+{
+ struct cs *c = (struct cs *) cft;
+ struct stat info;
+
+ if (!c->filename)
+ return 0;
+
+ if (stat(c->filename, &info) == -1) {
+ /* Ignore a deleted config file: still use original data */
+ if (errno == ENOENT) {
+ if (!c->exists)
+ return 0;
+ log_very_verbose("Config file %s has disappeared!",
+ c->filename);
+ goto reload;
+ }
+ log_sys_error("stat", c->filename);
+ log_error("Failed to reload configuration files");
+ return 0;
+ }
+
+ if (!S_ISREG(info.st_mode)) {
+ log_error("Configuration file %s is not a regular file",
+ c->filename);
+ goto reload;
+ }
+
+ /* Unchanged? */
+ if (c->timestamp == info.st_ctime)
+ return 0;
+
+ reload:
+ log_verbose("Detected config file change to %s", c->filename);
+ return 1;
+}
+
+static int _line_start(struct output_line *outline)
+{
+ if (!dm_pool_begin_object(outline->mem, 128)) {
+ log_error("dm_pool_begin_object failed for config line");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _line_append(struct output_line *outline, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+static int _line_append(struct output_line *outline, const char *fmt, ...)
+{
+ char buf[4096];
+ va_list ap;
+ int n;
+
+ va_start(ap, fmt);
+ n = vsnprintf(&buf[0], sizeof buf - 1, fmt, ap);
+ va_end(ap);
+
+ if (n < 0 || n > (int) sizeof buf - 1) {
+ log_error("vsnprintf failed for config line");
+ return 0;
+ }
+
+ if (!dm_pool_grow_object(outline->mem, &buf[0], strlen(buf))) {
+ log_error("dm_pool_grow_object failed for config line");
+ return 0;
+ }
+
+ return 1;
+}
+
+#define line_append(args...) do {if (!_line_append(outline, args)) {return_0;}} while (0)
+
+static int _line_end(struct output_line *outline)
+{
+ const char *line;
+
+ if (!dm_pool_grow_object(outline->mem, "\0", 1)) {
+ log_error("dm_pool_grow_object failed for config line");
+ return 0;
+ }
+
+ line = dm_pool_end_object(outline->mem);
+ if (outline->putline)
+ outline->putline(line, outline->putline_baton);
+ else {
+ if (!outline->fp)
+ log_print("%s", line);
+ else
+ fprintf(outline->fp, "%s\n", line);
+ }
+
+ return 1;
+}
+
+static int _write_value(struct output_line *outline, const struct config_value *v)
+{
+ char *buf;
+
+ switch (v->type) {
+ case CFG_STRING:
+ if (!(buf = alloca(escaped_len(v->v.str)))) {
+ log_error("temporary stack allocation for a config "
+ "string failed");
+ return 0;
+ }
+ line_append("\"%s\"", escape_double_quotes(buf, v->v.str));
+ break;
+
+ case CFG_FLOAT:
+ line_append("%f", v->v.r);
+ break;
+
+ case CFG_INT:
+ line_append("%" PRId64, v->v.i);
+ break;
+
+ case CFG_EMPTY_ARRAY:
+ line_append("[]");
+ break;
+
+ default:
+ log_error("_write_value: Unknown value type: %d", v->type);
+
+ }
+
+ return 1;
+}
+
+static int _write_config(const struct config_node *n, int only_one,
+ struct output_line *outline, int level)
+{
+ char space[MAX_INDENT + 1];
+ int l = (level < MAX_INDENT) ? level : MAX_INDENT;
+ int i;
+
+ if (!n)
+ return 1;
+
+ for (i = 0; i < l; i++)
+ space[i] = '\t';
+ space[i] = '\0';
+
+ do {
+ if (!_line_start(outline))
+ return_0;
+ line_append("%s%s", space, n->key);
+ if (!n->v) {
+ /* it's a sub section */
+ line_append(" {");
+ if (!_line_end(outline))
+ return_0;
+ _write_config(n->child, 0, outline, level + 1);
+ if (!_line_start(outline))
+ return_0;
+ line_append("%s}", space);
+ } else {
+ /* it's a value */
+ const struct config_value *v = n->v;
+ line_append("=");
+ if (v->next) {
+ line_append("[");
+ while (v) {
+ if (!_write_value(outline, v))
+ return_0;
+ v = v->next;
+ if (v)
+ line_append(", ");
+ }
+ line_append("]");
+ } else
+ if (!_write_value(outline, v))
+ return_0;
+ }
+ if (!_line_end(outline))
+ return_0;
+ n = n->sib;
+ } while (n && !only_one);
+ /* FIXME: add error checking */
+ return 1;
+}
+
+int write_config_node(const struct config_node *cn, putline_fn putline, void *baton)
+{
+ struct output_line outline;
+ outline.fp = NULL;
+ if (!(outline.mem = dm_pool_create("config_line", 1024)))
+ return_0;
+ outline.putline = putline;
+ outline.putline_baton = baton;
+ if (!_write_config(cn, 0, &outline, 0)) {
+ dm_pool_destroy(outline.mem);
+ return_0;
+ }
+ dm_pool_destroy(outline.mem);
+ return 1;
+}
+
+int write_config_file(struct config_tree *cft, const char *file,
+ int argc, char **argv)
+{
+ const struct config_node *cn;
+ int r = 1;
+ struct output_line outline;
+ outline.fp = NULL;
+ outline.putline = NULL;
+
+ if (!file)
+ file = "stdout";
+ else if (!(outline.fp = fopen(file, "w"))) {
+ log_sys_error("open", file);
+ return 0;
+ }
+
+ if (!(outline.mem = dm_pool_create("config_line", 1024))) {
+ r = 0;
+ goto_out;
+ }
+
+ log_verbose("Dumping configuration to %s", file);
+ if (!argc) {
+ if (!_write_config(cft->root, 0, &outline, 0)) {
+ log_error("Failure while writing to %s", file);
+ r = 0;
+ }
+ } else while (argc--) {
+ if ((cn = find_config_node(cft->root, *argv))) {
+ if (!_write_config(cn, 1, &outline, 0)) {
+ log_error("Failure while writing to %s", file);
+ r = 0;
+ }
+ } else {
+ log_error("Configuration node %s not found", *argv);
+ r = 0;
+ }
+ argv++;
+ }
+
+ dm_pool_destroy(outline.mem);
+
+out:
+ if (outline.fp && lvm_fclose(outline.fp, file)) {
+ stack;
+ r = 0;
+ }
+
+ return r;
+}
+
+/*
+ * parser
+ */
+static struct config_node *_file(struct parser *p)
+{
+ struct config_node *root = NULL, *n, *l = NULL;
+ while (p->t != TOK_EOF) {
+ if (!(n = _section(p)))
+ return_0;
+
+ if (!root)
+ root = n;
+ else
+ l->sib = n;
+ n->parent = root;
+ l = n;
+ }
+ return root;
+}
+
+static struct config_node *_section(struct parser *p)
+{
+ /* IDENTIFIER SECTION_B_CHAR VALUE* SECTION_E_CHAR */
+ struct config_node *root, *n, *l = NULL;
+ if (!(root = _create_node(p->mem)))
+ return_0;
+
+ if (!(root->key = _dup_tok(p)))
+ return_0;
+
+ match(TOK_IDENTIFIER);
+
+ if (p->t == TOK_SECTION_B) {
+ match(TOK_SECTION_B);
+ while (p->t != TOK_SECTION_E) {
+ if (!(n = _section(p)))
+ return_0;
+
+ if (!root->child)
+ root->child = n;
+ else
+ l->sib = n;
+ n->parent = root;
+ l = n;
+ }
+ match(TOK_SECTION_E);
+ } else {
+ match(TOK_EQ);
+ if (!(root->v = _value(p)))
+ return_0;
+ }
+
+ return root;
+}
+
+static struct config_value *_value(struct parser *p)
+{
+ /* '[' TYPE* ']' | TYPE */
+ struct config_value *h = NULL, *l, *ll = NULL;
+ if (p->t == TOK_ARRAY_B) {
+ match(TOK_ARRAY_B);
+ while (p->t != TOK_ARRAY_E) {
+ if (!(l = _type(p)))
+ return_0;
+
+ if (!h)
+ h = l;
+ else
+ ll->next = l;
+ ll = l;
+
+ if (p->t == TOK_COMMA)
+ match(TOK_COMMA);
+ }
+ match(TOK_ARRAY_E);
+ /*
+ * Special case for an empty array.
+ */
+ if (!h) {
+ if (!(h = _create_value(p->mem)))
+ return NULL;
+
+ h->type = CFG_EMPTY_ARRAY;
+ }
+
+ } else
+ h = _type(p);
+
+ return h;
+}
+
+static struct config_value *_type(struct parser *p)
+{
+ /* [+-]{0,1}[0-9]+ | [0-9]*\.[0-9]* | ".*" */
+ struct config_value *v = _create_value(p->mem);
+ char *str;
+
+ if (!v)
+ return NULL;
+
+ switch (p->t) {
+ case TOK_INT:
+ v->type = CFG_INT;
+ v->v.i = strtoll(p->tb, NULL, 0); /* FIXME: check error */
+ match(TOK_INT);
+ break;
+
+ case TOK_FLOAT:
+ v->type = CFG_FLOAT;
+ v->v.r = strtod(p->tb, NULL); /* FIXME: check error */
+ match(TOK_FLOAT);
+ break;
+
+ case TOK_STRING:
+ v->type = CFG_STRING;
+
+ p->tb++, p->te--; /* strip "'s */
+ if (!(v->v.str = _dup_tok(p)))
+ return_0;
+ p->te++;
+ match(TOK_STRING);
+ break;
+
+ case TOK_STRING_ESCAPED:
+ v->type = CFG_STRING;
+
+ p->tb++, p->te--; /* strip "'s */
+ if (!(str = _dup_tok(p)))
+ return_0;
+ unescape_double_quotes(str);
+ v->v.str = str;
+ p->te++;
+ match(TOK_STRING_ESCAPED);
+ break;
+
+ default:
+ log_error("Parse error at byte %" PRIptrdiff_t " (line %d): expected a value",
+ p->tb - p->fb + 1, p->line);
+ return 0;
+ }
+ return v;
+}
+
+static int _match_aux(struct parser *p, int t)
+{
+ if (p->t != t)
+ return 0;
+
+ _get_token(p, t);
+ return 1;
+}
+
+/*
+ * tokeniser
+ */
+static void _get_token(struct parser *p, int tok_prev)
+{
+ int values_allowed = 0;
+
+ const char *te;
+
+ p->tb = p->te;
+ _eat_space(p);
+ if (p->tb == p->fe || !*p->tb) {
+ p->t = TOK_EOF;
+ return;
+ }
+
+ /* Should next token be interpreted as value instead of identifier? */
+ if (tok_prev == TOK_EQ || tok_prev == TOK_ARRAY_B ||
+ tok_prev == TOK_COMMA)
+ values_allowed = 1;
+
+ p->t = TOK_INT; /* fudge so the fall through for
+ floats works */
+
+ te = p->te;
+ switch (*te) {
+ case SECTION_B_CHAR:
+ p->t = TOK_SECTION_B;
+ te++;
+ break;
+
+ case SECTION_E_CHAR:
+ p->t = TOK_SECTION_E;
+ te++;
+ break;
+
+ case '[':
+ p->t = TOK_ARRAY_B;
+ te++;
+ break;
+
+ case ']':
+ p->t = TOK_ARRAY_E;
+ te++;
+ break;
+
+ case ',':
+ p->t = TOK_COMMA;
+ te++;
+ break;
+
+ case '=':
+ p->t = TOK_EQ;
+ te++;
+ break;
+
+ case '"':
+ p->t = TOK_STRING_ESCAPED;
+ te++;
+ while ((te != p->fe) && (*te) && (*te != '"')) {
+ if ((*te == '\\') && (te + 1 != p->fe) &&
+ *(te + 1))
+ te++;
+ te++;
+ }
+
+ if ((te != p->fe) && (*te))
+ te++;
+ break;
+
+ case '\'':
+ p->t = TOK_STRING;
+ te++;
+ while ((te != p->fe) && (*te) && (*te != '\''))
+ te++;
+
+ if ((te != p->fe) && (*te))
+ te++;
+ break;
+
+ case '.':
+ p->t = TOK_FLOAT;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '+':
+ case '-':
+ if (values_allowed) {
+ te++;
+ while ((te != p->fe) && (*te)) {
+ if (*te == '.') {
+ if (p->t == TOK_FLOAT)
+ break;
+ p->t = TOK_FLOAT;
+ } else if (!isdigit((int) *te))
+ break;
+ te++;
+ }
+ break;
+ }
+
+ default:
+ p->t = TOK_IDENTIFIER;
+ while ((te != p->fe) && (*te) && !isspace(*te) &&
+ (*te != '#') && (*te != '=') &&
+ (*te != SECTION_B_CHAR) &&
+ (*te != SECTION_E_CHAR))
+ te++;
+ break;
+ }
+
+ p->te = te;
+}
+
+static void _eat_space(struct parser *p)
+{
+ while ((p->tb != p->fe) && (*p->tb)) {
+ if (*p->te == '#')
+ while ((p->te != p->fe) && (*p->te) && (*p->te != '\n'))
+ p->te++;
+
+ else if (isspace(*p->te)) {
+ while ((p->te != p->fe) && (*p->te) && isspace(*p->te)) {
+ if (*p->te == '\n')
+ p->line++;
+ p->te++;
+ }
+ }
+
+ else
+ return;
+
+ p->tb = p->te;
+ }
+}
+
+/*
+ * memory management
+ */
+static struct config_value *_create_value(struct dm_pool *mem)
+{
+ return dm_pool_zalloc(mem, sizeof(struct config_value));
+}
+
+static struct config_node *_create_node(struct dm_pool *mem)
+{
+ return dm_pool_zalloc(mem, sizeof(struct config_node));
+}
+
+static char *_dup_tok(struct parser *p)
+{
+ size_t len = p->te - p->tb;
+ char *str = dm_pool_alloc(p->mem, len + 1);
+ if (!str)
+ return_0;
+ strncpy(str, p->tb, len);
+ str[len] = '\0';
+ return str;
+}
+
+/*
+ * utility functions
+ */
+static const struct config_node *_find_config_node(const struct config_node *cn,
+ const char *path)
+{
+ const char *e;
+ const struct config_node *cn_found = NULL;
+
+ while (cn) {
+ /* trim any leading slashes */
+ while (*path && (*path == sep))
+ path++;
+
+ /* find the end of this segment */
+ for (e = path; *e && (*e != sep); e++) ;
+
+ /* hunt for the node */
+ cn_found = NULL;
+ while (cn) {
+ if (_tok_match(cn->key, path, e)) {
+ /* Inefficient */
+ if (!cn_found)
+ cn_found = cn;
+ else
+ log_warn("WARNING: Ignoring duplicate"
+ " config node: %s ("
+ "seeking %s)", cn->key, path);
+ }
+
+ cn = cn->sib;
+ }
+
+ if (cn_found && *e)
+ cn = cn_found->child;
+ else
+ break; /* don't move into the last node */
+
+ path = e;
+ }
+
+ return cn_found;
+}
+
+static const struct config_node *_find_first_config_node(const struct config_node *cn1,
+ const struct config_node *cn2,
+ const char *path)
+{
+ const struct config_node *cn;
+
+ if (cn1 && (cn = _find_config_node(cn1, path)))
+ return cn;
+
+ if (cn2 && (cn = _find_config_node(cn2, path)))
+ return cn;
+
+ return NULL;
+}
+
+const struct config_node *find_config_node(const struct config_node *cn,
+ const char *path)
+{
+ return _find_config_node(cn, path);
+}
+
+static const char *_find_config_str(const struct config_node *cn1,
+ const struct config_node *cn2,
+ const char *path, const char *fail)
+{
+ const struct config_node *n = _find_first_config_node(cn1, cn2, path);
+
+ /* Empty strings are ignored */
+ if ((n && n->v && n->v->type == CFG_STRING) && (*n->v->v.str)) {
+ log_very_verbose("Setting %s to %s", path, n->v->v.str);
+ return n->v->v.str;
+ }
+
+ if (fail)
+ log_very_verbose("%s not found in config: defaulting to %s",
+ path, fail);
+ return fail;
+}
+
+const char *find_config_str(const struct config_node *cn,
+ const char *path, const char *fail)
+{
+ return _find_config_str(cn, NULL, path, fail);
+}
+
+static int64_t _find_config_int64(const struct config_node *cn1,
+ const struct config_node *cn2,
+ const char *path, int64_t fail)
+{
+ const struct config_node *n = _find_first_config_node(cn1, cn2, path);
+
+ if (n && n->v && n->v->type == CFG_INT) {
+ log_very_verbose("Setting %s to %" PRId64, path, n->v->v.i);
+ return n->v->v.i;
+ }
+
+ log_very_verbose("%s not found in config: defaulting to %" PRId64,
+ path, fail);
+ return fail;
+}
+
+int find_config_int(const struct config_node *cn, const char *path, int fail)
+{
+ /* FIXME Add log_error message on overflow */
+ return (int) _find_config_int64(cn, NULL, path, (int64_t) fail);
+}
+
+static float _find_config_float(const struct config_node *cn1,
+ const struct config_node *cn2,
+ const char *path, float fail)
+{
+ const struct config_node *n = _find_first_config_node(cn1, cn2, path);
+
+ if (n && n->v && n->v->type == CFG_FLOAT) {
+ log_very_verbose("Setting %s to %f", path, n->v->v.r);
+ return n->v->v.r;
+ }
+
+ log_very_verbose("%s not found in config: defaulting to %f",
+ path, fail);
+
+ return fail;
+
+}
+
+float find_config_float(const struct config_node *cn, const char *path,
+ float fail)
+{
+ return _find_config_float(cn, NULL, path, fail);
+}
+
+const struct config_node *find_config_tree_node(struct cmd_context *cmd,
+ const char *path)
+{
+ return _find_first_config_node(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path);
+}
+
+const char *find_config_tree_str(struct cmd_context *cmd,
+ const char *path, const char *fail)
+{
+ return _find_config_str(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
+}
+
+int find_config_tree_int(struct cmd_context *cmd, const char *path,
+ int fail)
+{
+ /* FIXME Add log_error message on overflow */
+ return (int) _find_config_int64(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, (int64_t) fail);
+}
+
+float find_config_tree_float(struct cmd_context *cmd, const char *path,
+ float fail)
+{
+ return _find_config_float(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
+}
+
+static int _str_in_array(const char *str, const char * const values[])
+{
+ int i;
+
+ for (i = 0; values[i]; i++)
+ if (!strcasecmp(str, values[i]))
+ return 1;
+
+ return 0;
+}
+
+static int _str_to_bool(const char *str, int fail)
+{
+ const char * const _true_values[] = { "y", "yes", "on", "true", NULL };
+ const char * const _false_values[] = { "n", "no", "off", "false", NULL };
+
+ if (_str_in_array(str, _true_values))
+ return 1;
+
+ if (_str_in_array(str, _false_values))
+ return 0;
+
+ return fail;
+}
+
+static int _find_config_bool(const struct config_node *cn1,
+ const struct config_node *cn2,
+ const char *path, int fail)
+{
+ const struct config_node *n = _find_first_config_node(cn1, cn2, path);
+ const struct config_value *v;
+
+ if (!n)
+ return fail;
+
+ v = n->v;
+
+ switch (v->type) {
+ case CFG_INT:
+ return v->v.i ? 1 : 0;
+
+ case CFG_STRING:
+ return _str_to_bool(v->v.str, fail);
+ }
+
+ return fail;
+}
+
+int find_config_bool(const struct config_node *cn, const char *path, int fail)
+{
+ return _find_config_bool(cn, NULL, path, fail);
+}
+
+int find_config_tree_bool(struct cmd_context *cmd, const char *path, int fail)
+{
+ return _find_config_bool(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
+}
+
+int get_config_uint32(const struct config_node *cn, const char *path,
+ uint32_t *result)
+{
+ const struct config_node *n;
+
+ n = find_config_node(cn, path);
+
+ if (!n || !n->v || n->v->type != CFG_INT)
+ return 0;
+
+ *result = n->v->v.i;
+ return 1;
+}
+
+int get_config_uint64(const struct config_node *cn, const char *path,
+ uint64_t *result)
+{
+ const struct config_node *n;
+
+ n = find_config_node(cn, path);
+
+ if (!n || !n->v || n->v->type != CFG_INT)
+ return 0;
+
+ *result = (uint64_t) n->v->v.i;
+ return 1;
+}
+
+int get_config_str(const struct config_node *cn, const char *path,
+ const char **result)
+{
+ const struct config_node *n;
+
+ n = find_config_node(cn, path);
+
+ if (!n || !n->v || n->v->type != CFG_STRING)
+ return 0;
+
+ *result = n->v->v.str;
+ return 1;
+}
+
+/* Insert cn2 after cn1 */
+static void _insert_config_node(struct config_node **cn1,
+ struct config_node *cn2)
+{
+ if (!*cn1) {
+ *cn1 = cn2;
+ cn2->sib = NULL;
+ } else {
+ cn2->sib = (*cn1)->sib;
+ (*cn1)->sib = cn2;
+ }
+}
+
+/*
+ * Merge section cn2 into section cn1 (which has the same name)
+ * overwriting any existing cn1 nodes with matching names.
+ */
+static void _merge_section(struct config_node *cn1, struct config_node *cn2)
+{
+ struct config_node *cn, *nextn, *oldn;
+ struct config_value *cv;
+
+ for (cn = cn2->child; cn; cn = nextn) {
+ nextn = cn->sib;
+
+ /* Skip "tags" */
+ if (!strcmp(cn->key, "tags"))
+ continue;
+
+ /* Subsection? */
+ if (!cn->v)
+ /* Ignore - we don't have any of these yet */
+ continue;
+ /* Not already present? */
+ if (!(oldn = (struct config_node*)find_config_node(cn1->child, cn->key))) {
+ _insert_config_node(&cn1->child, cn);
+ continue;
+ }
+ /* Merge certain value lists */
+ if ((!strcmp(cn1->key, "activation") &&
+ !strcmp(cn->key, "volume_list")) ||
+ (!strcmp(cn1->key, "devices") &&
+ (!strcmp(cn->key, "filter") || !strcmp(cn->key, "types")))) {
+ cv = cn->v;
+ while (cv->next)
+ cv = cv->next;
+ cv->next = oldn->v;
+ }
+
+ /* Replace values */
+ oldn->v = cn->v;
+ }
+}
+
+static int _match_host_tags(struct dm_list *tags, const struct config_node *tn)
+{
+ const struct config_value *tv;
+ const char *str;
+
+ for (tv = tn->v; tv; tv = tv->next) {
+ if (tv->type != CFG_STRING)
+ continue;
+ str = tv->v.str;
+ if (*str == '@')
+ str++;
+ if (!*str)
+ continue;
+ if (str_list_match_item(tags, str))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Destructively merge a new config tree into an existing one */
+int merge_config_tree(struct cmd_context *cmd, struct config_tree *cft,
+ struct config_tree *newdata)
+{
+ const struct config_node *root = cft->root;
+ struct config_node *cn, *nextn, *oldn, *cn2;
+ const struct config_node *tn;
+
+ for (cn = newdata->root; cn; cn = nextn) {
+ nextn = cn->sib;
+ /* Ignore tags section */
+ if (!strcmp(cn->key, "tags"))
+ continue;
+ /* If there's a tags node, skip if host tags don't match */
+ if ((tn = find_config_node(cn->child, "tags"))) {
+ if (!_match_host_tags(&cmd->tags, tn))
+ continue;
+ }
+ if (!(oldn = (struct config_node *)find_config_node(root, cn->key))) {
+ _insert_config_node(&cft->root, cn);
+ /* Remove any "tags" nodes */
+ for (cn2 = cn->child; cn2; cn2 = cn2->sib) {
+ if (!strcmp(cn2->key, "tags")) {
+ cn->child = cn2->sib;
+ continue;
+ }
+ if (cn2->sib && !strcmp(cn2->sib->key, "tags")) {
+ cn2->sib = cn2->sib->sib;
+ continue;
+ }
+ }
+ continue;
+ }
+ _merge_section(oldn, cn);
+ }
+
+ return 1;
+}
+
+/*
+ * Convert a token type to the char it represents.
+ */
+static char _token_type_to_char(int type)
+{
+ switch (type) {
+ case TOK_SECTION_B:
+ return SECTION_B_CHAR;
+ case TOK_SECTION_E:
+ return SECTION_E_CHAR;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Returns:
+ * # of 'type' tokens in 'str'.
+ */
+static unsigned _count_tokens(const char *str, unsigned len, int type)
+{
+ char c;
+
+ c = _token_type_to_char(type);
+
+ return count_chars(str, len, c);
+}
+
+const char *config_parent_name(const struct config_node *n)
+{
+ return (n->parent ? n->parent->key : "(root)");
+}
+/*
+ * Heuristic function to make a quick guess as to whether a text
+ * region probably contains a valid config "section". (Useful for
+ * scanning areas of the disk for old metadata.)
+ * Config sections contain various tokens, may contain other sections
+ * and strings, and are delimited by begin (type 'TOK_SECTION_B') and
+ * end (type 'TOK_SECTION_E') tokens. As a quick heuristic, we just
+ * count the number of begin and end tokens, and see if they are
+ * non-zero and the counts match.
+ * Full validation of the section should be done with another function
+ * (for example, read_config_fd).
+ *
+ * Returns:
+ * 0 - probably is not a valid config section
+ * 1 - probably _is_ a valid config section
+ */
+unsigned maybe_config_section(const char *str, unsigned len)
+{
+ int begin_count;
+ int end_count;
+
+ begin_count = _count_tokens(str, len, TOK_SECTION_B);
+ end_count = _count_tokens(str, len, TOK_SECTION_E);
+
+ if (begin_count && end_count && (begin_count == end_count))
+ return 1;
+ else
+ return 0;
+}
+
+static struct config_value *_clone_config_value(struct dm_pool *mem, const struct config_value *v)
+{
+ struct config_value *new_cv;
+
+ if (!v)
+ return NULL;
+
+ if (!(new_cv = _create_value(mem))) {
+ log_error("Failed to clone config value.");
+ return NULL;
+ }
+
+ new_cv->type = v->type;
+ if (v->type == CFG_STRING) {
+ if (!(new_cv->v.str = dm_pool_strdup(mem, v->v.str))) {
+ log_error("Failed to clone config string value.");
+ return NULL;
+ }
+ } else
+ new_cv->v = v->v;
+
+ if (v->next && !(new_cv->next = _clone_config_value(mem, v->next)))
+ return_NULL;
+
+ return new_cv;
+}
+
+struct config_node *clone_config_node(struct dm_pool *mem, const struct config_node *cn,
+ int siblings)
+{
+ struct config_node *new_cn;
+
+ if (!cn)
+ return NULL;
+
+ if (!(new_cn = _create_node(mem))) {
+ log_error("Failed to clone config node.");
+ return NULL;
+ }
+
+ if ((cn->key && !(new_cn->key = dm_pool_strdup(mem, cn->key)))) {
+ log_error("Failed to clone config node key.");
+ return NULL;
+ }
+
+ if ((cn->v && !(new_cn->v = _clone_config_value(mem, cn->v))) ||
+ (cn->child && !(new_cn->child = clone_config_node(mem, cn->child, 1))) ||
+ (siblings && cn->sib && !(new_cn->sib = clone_config_node(mem, cn->sib, siblings))))
+ return_NULL; /* 'new_cn' released with mem pool */
+
+ return new_cn;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_CONFIG_H
+#define _LVM_CONFIG_H
+
+#include "lvm-types.h"
+
+struct device;
+struct cmd_context;
+
+enum {
+ CFG_STRING,
+ CFG_FLOAT,
+ CFG_INT,
+ CFG_EMPTY_ARRAY
+};
+
+struct config_value {
+ int type;
+ union {
+ int64_t i;
+ float r;
+ const char *str;
+ } v;
+ struct config_value *next; /* for arrays */
+};
+
+struct config_node {
+ const char *key;
+ struct config_node *parent, *sib, *child;
+ struct config_value *v;
+};
+
+struct config_tree {
+ struct config_node *root;
+};
+
+struct config_tree_list {
+ struct dm_list list;
+ struct config_tree *cft;
+};
+
+struct config_tree *create_config_tree(const char *filename, int keep_open);
+struct config_tree *create_config_tree_from_string(struct cmd_context *cmd,
+ const char *config_settings);
+int override_config_tree_from_string(struct cmd_context *cmd,
+ const char *config_settings);
+void destroy_config_tree(struct config_tree *cft);
+
+typedef uint32_t (*checksum_fn_t) (uint32_t initial, const uint8_t *buf, uint32_t size);
+
+int read_config_fd(struct config_tree *cft, struct device *dev,
+ off_t offset, size_t size, off_t offset2, size_t size2,
+ checksum_fn_t checksum_fn, uint32_t checksum);
+
+int read_config_file(struct config_tree *cft);
+int write_config_file(struct config_tree *cft, const char *file,
+ int argc, char **argv);
+
+typedef int (*putline_fn)(const char *line, void *baton);
+int write_config_node(const struct config_node *cn, putline_fn putline, void *baton);
+
+time_t config_file_timestamp(struct config_tree *cft);
+int config_file_changed(struct config_tree *cft);
+int merge_config_tree(struct cmd_context *cmd, struct config_tree *cft,
+ struct config_tree *newdata);
+
+const struct config_node *find_config_node(const struct config_node *cn,
+ const char *path);
+const char *find_config_str(const struct config_node *cn, const char *path,
+ const char *fail);
+int find_config_int(const struct config_node *cn, const char *path, int fail);
+float find_config_float(const struct config_node *cn, const char *path,
+ float fail);
+
+/*
+ * These versions check an override tree, if present, first.
+ */
+const struct config_node *find_config_tree_node(struct cmd_context *cmd,
+ const char *path);
+const char *find_config_tree_str(struct cmd_context *cmd,
+ const char *path, const char *fail);
+int find_config_tree_int(struct cmd_context *cmd, const char *path,
+ int fail);
+float find_config_tree_float(struct cmd_context *cmd, const char *path,
+ float fail);
+
+/*
+ * Understands (0, ~0), (y, n), (yes, no), (on,
+ * off), (true, false).
+ */
+int find_config_bool(const struct config_node *cn, const char *path, int fail);
+int find_config_tree_bool(struct cmd_context *cmd, const char *path, int fail);
+
+int get_config_uint32(const struct config_node *cn, const char *path,
+ uint32_t *result);
+
+int get_config_uint64(const struct config_node *cn, const char *path,
+ uint64_t *result);
+
+int get_config_str(const struct config_node *cn, const char *path,
+ const char **result);
+
+unsigned maybe_config_section(const char *str, unsigned len);
+
+const char *config_parent_name(const struct config_node *n);
+
+struct config_node *clone_config_node(struct dm_pool *mem, const struct config_node *cn,
+ int siblings);
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_DEFAULTS_H
+#define _LVM_DEFAULTS_H
+
+#define DEFAULT_PE_ALIGN 2048
+#define DEFAULT_PE_ALIGN_OLD 128
+
+#define DEFAULT_ARCHIVE_ENABLED 1
+#define DEFAULT_BACKUP_ENABLED 1
+
+#define DEFAULT_CACHE_FILE_PREFIX ""
+
+#define DEFAULT_ARCHIVE_DAYS 30
+#define DEFAULT_ARCHIVE_NUMBER 10
+
+#define DEFAULT_DEV_DIR "/dev"
+#define DEFAULT_PROC_DIR "/proc"
+#define DEFAULT_SYSFS_SCAN 1
+#define DEFAULT_MD_COMPONENT_DETECTION 1
+#define DEFAULT_MD_CHUNK_ALIGNMENT 1
+#define DEFAULT_IGNORE_SUSPENDED_DEVICES 1
+#define DEFAULT_DISABLE_AFTER_ERROR_COUNT 0
+#define DEFAULT_REQUIRE_RESTOREFILE_WITH_UUID 1
+#define DEFAULT_DATA_ALIGNMENT_OFFSET_DETECTION 1
+#define DEFAULT_DATA_ALIGNMENT_DETECTION 1
+
+#define DEFAULT_LOCKING_LIB "liblvm2clusterlock.so"
+#define DEFAULT_FALLBACK_TO_LOCAL_LOCKING 1
+#define DEFAULT_FALLBACK_TO_CLUSTERED_LOCKING 1
+#define DEFAULT_WAIT_FOR_LOCKS 1
+#define DEFAULT_PRIORITISE_WRITE_LOCKS 1
+#define DEFAULT_USE_MLOCKALL 0
+#define DEFAULT_METADATA_READ_ONLY 0
+
+#define DEFAULT_MIRRORLOG "disk"
+#define DEFAULT_MIRROR_LOG_FAULT_POLICY "allocate"
+#define DEFAULT_MIRROR_IMAGE_FAULT_POLICY "remove"
+#define DEFAULT_MIRROR_MAX_IMAGES 8 /* limited by kernel DM_KCOPYD_MAX_REGIONS */
+#define DEFAULT_DMEVENTD_MIRROR_LIB "libdevmapper-event-lvm2mirror.so"
+#define DEFAULT_DMEVENTD_SNAPSHOT_LIB "libdevmapper-event-lvm2snapshot.so"
+#define DEFAULT_DMEVENTD_MONITOR 1
+#define DEFAULT_BACKGROUND_POLLING 1
+
+#define DEFAULT_UMASK 0077
+
+#ifdef LVM1_FALLBACK
+# define DEFAULT_FALLBACK_TO_LVM1 1
+#else
+# define DEFAULT_FALLBACK_TO_LVM1 0
+#endif
+
+#define DEFAULT_FORMAT "lvm2"
+
+#define DEFAULT_STRIPESIZE 64 /* KB */
+#define DEFAULT_PVMETADATAIGNORE 0
+#define DEFAULT_PVMETADATAIGNORE_STR "n"
+#define DEFAULT_PVMETADATASIZE 255
+#define DEFAULT_PVMETADATACOPIES 1
+#define DEFAULT_VGMETADATACOPIES 0
+#define DEFAULT_LABELSECTOR UINT64_C(1)
+#define DEFAULT_READ_AHEAD "auto"
+#define DEFAULT_UDEV_RULES 1
+#define DEFAULT_UDEV_SYNC 0
+#define DEFAULT_EXTENT_SIZE 4096 /* In KB */
+#define DEFAULT_MAX_PV 0
+#define DEFAULT_MAX_LV 0
+#define DEFAULT_ALLOC_POLICY ALLOC_NORMAL
+#define DEFAULT_CLUSTERED 0
+
+#define DEFAULT_MSG_PREFIX " "
+#define DEFAULT_CMD_NAME 0
+#define DEFAULT_OVERWRITE 0
+
+#ifndef DEFAULT_LOG_FACILITY
+# define DEFAULT_LOG_FACILITY LOG_USER
+#endif
+
+#define DEFAULT_SYSLOG 1
+#define DEFAULT_VERBOSE 0
+#define DEFAULT_LOGLEVEL 0
+#define DEFAULT_INDENT 1
+#define DEFAULT_ABORT_ON_INTERNAL_ERRORS 0
+#define DEFAULT_UNITS "h"
+#define DEFAULT_SUFFIX 1
+#define DEFAULT_HOSTTAGS 0
+
+#ifndef DEFAULT_SI_UNIT_CONSISTENCY
+# define DEFAULT_SI_UNIT_CONSISTENCY 1
+#endif
+
+#ifdef DEVMAPPER_SUPPORT
+# define DEFAULT_ACTIVATION 1
+# define DEFAULT_RESERVED_MEMORY 8192
+# define DEFAULT_RESERVED_STACK 256
+# define DEFAULT_PROCESS_PRIORITY -18
+#else
+# define DEFAULT_ACTIVATION 0
+#endif
+
+#define DEFAULT_STRIPE_FILLER "error"
+#define DEFAULT_MIRROR_REGION_SIZE 512 /* KB */
+#define DEFAULT_INTERVAL 15
+
+#ifdef READLINE_SUPPORT
+# define DEFAULT_MAX_HISTORY 100
+#endif
+
+#define DEFAULT_MAX_ERROR_COUNT NO_DEV_ERROR_COUNT_LIMIT
+
+#define DEFAULT_REP_ALIGNED 1
+#define DEFAULT_REP_BUFFERED 1
+#define DEFAULT_REP_COLUMNS_AS_ROWS 0
+#define DEFAULT_REP_HEADINGS 1
+#define DEFAULT_REP_PREFIXES 0
+#define DEFAULT_REP_QUOTED 1
+#define DEFAULT_REP_SEPARATOR " "
+
+#define DEFAULT_LVS_COLS "lv_name,vg_name,lv_attr,lv_size,origin,snap_percent,move_pv,mirror_log,copy_percent,convert_lv"
+#define DEFAULT_VGS_COLS "vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free"
+#define DEFAULT_PVS_COLS "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free"
+#define DEFAULT_SEGS_COLS "lv_name,vg_name,lv_attr,stripes,segtype,seg_size"
+#define DEFAULT_PVSEGS_COLS "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size"
+
+#define DEFAULT_LVS_COLS_VERB "lv_name,vg_name,seg_count,lv_attr,lv_size,lv_major,lv_minor,lv_kernel_major,lv_kernel_minor,origin,snap_percent,move_pv,copy_percent,mirror_log,convert_lv,lv_uuid"
+#define DEFAULT_VGS_COLS_VERB "vg_name,vg_attr,vg_extent_size,pv_count,lv_count,snap_count,vg_size,vg_free,vg_uuid"
+#define DEFAULT_PVS_COLS_VERB "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,dev_size,pv_uuid"
+#define DEFAULT_SEGS_COLS_VERB "lv_name,vg_name,lv_attr,seg_start,seg_size,stripes,segtype,stripesize,chunksize"
+#define DEFAULT_PVSEGS_COLS_VERB "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
+
+#define DEFAULT_LVS_SORT "vg_name,lv_name"
+#define DEFAULT_VGS_SORT "vg_name"
+#define DEFAULT_PVS_SORT "pv_name"
+#define DEFAULT_SEGS_SORT "vg_name,lv_name,seg_start"
+#define DEFAULT_PVSEGS_SORT "pv_name,pvseg_start"
+
+#define DEFAULT_MIRROR_DEVICE_FAULT_POLICY "remove"
+#define DEFAULT_MIRROR_LOG_FAULT_POLICY "allocate"
+#define DEFAULT_SNAPSHOT_AUTOEXTEND_THRESHOLD 100
+#define DEFAULT_SNAPSHOT_AUTOEXTEND_PERCENT 20
+
+#endif /* _LVM_DEFAULTS_H */
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "btree.h"
+
+struct node {
+ uint32_t key;
+ struct node *l, *r, *p;
+
+ void *data;
+};
+
+struct btree {
+ struct dm_pool *mem;
+ struct node *root;
+};
+
+struct btree *btree_create(struct dm_pool *mem)
+{
+ struct btree *t = dm_pool_alloc(mem, sizeof(*t));
+
+ if (t) {
+ t->mem = mem;
+ t->root = NULL;
+ }
+
+ return t;
+}
+
+/*
+ * Shuffle the bits in a key, to try and remove
+ * any ordering.
+ */
+static uint32_t _shuffle(uint32_t k)
+{
+#if 1
+ return ((k & 0xff) << 24 |
+ (k & 0xff00) << 8 |
+ (k & 0xff0000) >> 8 | (k & 0xff000000) >> 24);
+#else
+ return k;
+#endif
+}
+
+static struct node **_lookup(struct node *const *c, uint32_t key,
+ struct node **p)
+{
+ *p = NULL;
+ while (*c) {
+ *p = *c;
+ if ((*c)->key == key)
+ break;
+
+ if (key < (*c)->key)
+ c = &(*c)->l;
+
+ else
+ c = &(*c)->r;
+ }
+
+ return (struct node **)c;
+}
+
+void *btree_lookup(const struct btree *t, uint32_t k)
+{
+ uint32_t key = _shuffle(k);
+ struct node *p, **c = _lookup(&t->root, key, &p);
+ return (*c) ? (*c)->data : NULL;
+}
+
+int btree_insert(struct btree *t, uint32_t k, void *data)
+{
+ uint32_t key = _shuffle(k);
+ struct node *p, **c = _lookup(&t->root, key, &p), *n;
+
+ if (!*c) {
+ if (!(n = dm_pool_alloc(t->mem, sizeof(*n))))
+ return_0;
+
+ n->key = key;
+ n->data = data;
+ n->l = n->r = NULL;
+ n->p = p;
+
+ *c = n;
+ }
+
+ return 1;
+}
+
+void *btree_get_data(const struct btree_iter *it)
+{
+ return ((const struct node *) it)->data;
+}
+
+static struct node *_left(struct node *n)
+{
+ while (n->l)
+ n = n->l;
+ return n;
+}
+
+struct btree_iter *btree_first(const struct btree *t)
+{
+ if (!t->root)
+ return NULL;
+
+ return (struct btree_iter *) _left(t->root);
+}
+
+struct btree_iter *btree_next(const struct btree_iter *it)
+{
+ struct node *n = (struct node *) it;
+ uint32_t k = n->key;
+
+ if (n->r)
+ return (struct btree_iter *) _left(n->r);
+
+ do
+ n = n->p;
+ while (n && k > n->key);
+
+ return (struct btree_iter *) n;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_BTREE_H
+#define _LVM_BTREE_H
+
+struct btree;
+
+struct btree *btree_create(struct dm_pool *mem);
+
+void *btree_lookup(const struct btree *t, uint32_t k);
+int btree_insert(struct btree *t, uint32_t k, void *data);
+
+struct btree_iter;
+void *btree_get_data(const struct btree_iter *it);
+
+struct btree_iter *btree_first(const struct btree *t);
+struct btree_iter *btree_next(const struct btree_iter *it);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_TYPES_H
+#define _LVM_TYPES_H
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+/* Define some portable printing types */
+#define PRIsize_t "zu"
+#define PRIptrdiff_t "td"
+#define PRIpid_t PRId32
+
+struct str_list {
+ struct dm_list list;
+ const char *str;
+};
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "str_list.h"
+
+struct dm_list *str_list_create(struct dm_pool *mem)
+{
+ struct dm_list *sl;
+
+ if (!(sl = dm_pool_alloc(mem, sizeof(struct dm_list)))) {
+ log_errno(ENOMEM, "str_list allocation failed");
+ return NULL;
+ }
+
+ dm_list_init(sl);
+
+ return sl;
+}
+
+int str_list_add(struct dm_pool *mem, struct dm_list *sll, const char *str)
+{
+ struct str_list *sln;
+
+ if (!str)
+ return_0;
+
+ /* Already in list? */
+ if (str_list_match_item(sll, str))
+ return 1;
+
+ if (!(sln = dm_pool_alloc(mem, sizeof(*sln))))
+ return_0;
+
+ sln->str = str;
+ dm_list_add(sll, &sln->list);
+
+ return 1;
+}
+
+int str_list_del(struct dm_list *sll, const char *str)
+{
+ struct dm_list *slh, *slht;
+
+ dm_list_iterate_safe(slh, slht, sll) {
+ if (!strcmp(str, dm_list_item(slh, struct str_list)->str))
+ dm_list_del(slh);
+ }
+
+ return 1;
+}
+
+int str_list_dup(struct dm_pool *mem, struct dm_list *sllnew,
+ const struct dm_list *sllold)
+{
+ struct str_list *sl;
+
+ dm_list_init(sllnew);
+
+ dm_list_iterate_items(sl, sllold) {
+ if (!str_list_add(mem, sllnew, dm_pool_strdup(mem, sl->str)))
+ return_0;
+ }
+
+ return 1;
+}
+
+/*
+ * Is item on list?
+ */
+int str_list_match_item(const struct dm_list *sll, const char *str)
+{
+ struct str_list *sl;
+
+ dm_list_iterate_items(sl, sll)
+ if (!strcmp(str, sl->str))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Is at least one item on both lists?
+ * If tag_matched is non-NULL, it is set to the tag that matched.
+ */
+int str_list_match_list(const struct dm_list *sll, const struct dm_list *sll2, const char **tag_matched)
+{
+ struct str_list *sl;
+
+ dm_list_iterate_items(sl, sll)
+ if (str_list_match_item(sll2, sl->str)) {
+ if (tag_matched)
+ *tag_matched = sl->str;
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Do both lists contain the same set of items?
+ */
+int str_list_lists_equal(const struct dm_list *sll, const struct dm_list *sll2)
+{
+ struct str_list *sl;
+
+ if (dm_list_size(sll) != dm_list_size(sll2))
+ return 0;
+
+ dm_list_iterate_items(sl, sll)
+ if (!str_list_match_item(sll2, sl->str))
+ return 0;
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_STR_LIST_H
+#define _LVM_STR_LIST_H
+
+struct dm_list *str_list_create(struct dm_pool *mem);
+int str_list_add(struct dm_pool *mem, struct dm_list *sll, const char *str);
+int str_list_del(struct dm_list *sll, const char *str);
+int str_list_match_item(const struct dm_list *sll, const char *str);
+int str_list_match_list(const struct dm_list *sll, const struct dm_list *sll2, const char **tag_matched);
+int str_list_lists_equal(const struct dm_list *sll, const struct dm_list *sll2);
+int str_list_dup(struct dm_pool *mem, struct dm_list *sllnew,
+ const struct dm_list *sllold);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "dev-cache.h"
+#include "lvm-types.h"
+#include "btree.h"
+#include "filter.h"
+#include "filter-persistent.h"
+#include "toolcontext.h"
+
+#include <unistd.h>
+#include <sys/param.h>
+#include <dirent.h>
+
+struct dev_iter {
+ struct btree_iter *current;
+ struct dev_filter *filter;
+};
+
+struct dir_list {
+ struct dm_list list;
+ char dir[0];
+};
+
+static struct {
+ struct dm_pool *mem;
+ struct dm_hash_table *names;
+ struct btree *devices;
+ struct dm_regex *preferred_names_matcher;
+ const char *dev_dir;
+
+ int has_scanned;
+ struct dm_list dirs;
+ struct dm_list files;
+
+} _cache;
+
+#define _alloc(x) dm_pool_zalloc(_cache.mem, (x))
+#define _free(x) dm_pool_free(_cache.mem, (x))
+#define _strdup(x) dm_pool_strdup(_cache.mem, (x))
+
+static int _insert(const char *path, int rec);
+
+struct device *dev_create_file(const char *filename, struct device *dev,
+ struct str_list *alias, int use_malloc)
+{
+ int allocate = !dev;
+
+ if (allocate) {
+ if (use_malloc) {
+ if (!(dev = dm_malloc(sizeof(*dev)))) {
+ log_error("struct device allocation failed");
+ return NULL;
+ }
+ if (!(alias = dm_malloc(sizeof(*alias)))) {
+ log_error("struct str_list allocation failed");
+ dm_free(dev);
+ return NULL;
+ }
+ if (!(alias->str = dm_strdup(filename))) {
+ log_error("filename strdup failed");
+ dm_free(dev);
+ dm_free(alias);
+ return NULL;
+ }
+ dev->flags = DEV_ALLOCED;
+ } else {
+ if (!(dev = _alloc(sizeof(*dev)))) {
+ log_error("struct device allocation failed");
+ return NULL;
+ }
+ if (!(alias = _alloc(sizeof(*alias)))) {
+ log_error("struct str_list allocation failed");
+ _free(dev);
+ return NULL;
+ }
+ if (!(alias->str = _strdup(filename))) {
+ log_error("filename strdup failed");
+ return NULL;
+ }
+ }
+ } else if (!(alias->str = dm_strdup(filename))) {
+ log_error("filename strdup failed");
+ return NULL;
+ }
+
+ dev->flags |= DEV_REGULAR;
+ dm_list_init(&dev->aliases);
+ dm_list_add(&dev->aliases, &alias->list);
+ dev->end = UINT64_C(0);
+ dev->dev = 0;
+ dev->fd = -1;
+ dev->open_count = 0;
+ dev->error_count = 0;
+ dev->max_error_count = NO_DEV_ERROR_COUNT_LIMIT;
+ dev->block_size = -1;
+ dev->read_ahead = -1;
+ memset(dev->pvid, 0, sizeof(dev->pvid));
+ dm_list_init(&dev->open_list);
+
+ return dev;
+}
+
+static struct device *_dev_create(dev_t d)
+{
+ struct device *dev;
+
+ if (!(dev = _alloc(sizeof(*dev)))) {
+ log_error("struct device allocation failed");
+ return NULL;
+ }
+ dev->flags = 0;
+ dm_list_init(&dev->aliases);
+ dev->dev = d;
+ dev->fd = -1;
+ dev->open_count = 0;
+ dev->max_error_count = dev_disable_after_error_count();
+ dev->block_size = -1;
+ dev->read_ahead = -1;
+ dev->end = UINT64_C(0);
+ memset(dev->pvid, 0, sizeof(dev->pvid));
+ dm_list_init(&dev->open_list);
+
+ return dev;
+}
+
+void dev_set_preferred_name(struct str_list *sl, struct device *dev)
+{
+ /*
+ * Don't interfere with ordering specified in config file.
+ */
+ if (_cache.preferred_names_matcher)
+ return;
+
+ log_debug("%s: New preferred name", sl->str);
+ dm_list_del(&sl->list);
+ dm_list_add_h(&dev->aliases, &sl->list);
+}
+
+/*
+ * Check whether path0 or path1 contains the subpath. The path that
+ * *does not* contain the subpath wins (return 0 or 1). If both paths
+ * contain the subpath, return -1. If none of them contains the subpath,
+ * return -2.
+ */
+static int _builtin_preference(const char *path0, const char *path1,
+ size_t skip_prefix_count, const char *subpath)
+{
+ size_t subpath_len;
+ int r0, r1;
+
+ subpath_len = strlen(subpath);
+
+ r0 = !strncmp(path0 + skip_prefix_count, subpath, subpath_len);
+ r1 = !strncmp(path1 + skip_prefix_count, subpath, subpath_len);
+
+ if (!r0 && r1)
+ /* path0 does not have the subpath - it wins */
+ return 0;
+ else if (r0 && !r1)
+ /* path1 does not have the subpath - it wins */
+ return 1;
+ else if (r0 && r1)
+ /* both of them have the subpath */
+ return -1;
+
+ /* no path has the subpath */
+ return -2;
+}
+
+static int _apply_builtin_path_preference_rules(const char *path0, const char *path1)
+{
+ size_t devdir_len;
+ int r;
+
+ devdir_len = strlen(_cache.dev_dir);
+
+ if (!strncmp(path0, _cache.dev_dir, devdir_len) &&
+ !strncmp(path1, _cache.dev_dir, devdir_len)) {
+ /*
+ * We're trying to achieve the ordering:
+ * /dev/block/ < /dev/dm-* < /dev/disk/ < /dev/mapper/ < anything else
+ */
+
+ /* Prefer any other path over /dev/block/ path. */
+ if ((r = _builtin_preference(path0, path1, devdir_len, "block/")) >= -1)
+ return r;
+
+ /* Prefer any other path over /dev/dm-* path. */
+ if ((r = _builtin_preference(path0, path1, devdir_len, "dm-")) >= -1)
+ return r;
+
+ /* Prefer any other path over /dev/disk/ path. */
+ if ((r = _builtin_preference(path0, path1, devdir_len, "disk/")) >= -1)
+ return r;
+
+ /* Prefer any other path over /dev/mapper/ path. */
+ if ((r = _builtin_preference(path0, path1, 0, dm_dir())) >= -1)
+ return r;
+ }
+
+ return -1;
+}
+
+/* Return 1 if we prefer path1 else return 0 */
+static int _compare_paths(const char *path0, const char *path1)
+{
+ int slash0 = 0, slash1 = 0;
+ int m0, m1;
+ const char *p;
+ char p0[PATH_MAX], p1[PATH_MAX];
+ char *s0, *s1;
+ struct stat stat0, stat1;
+ int r;
+
+ /*
+ * FIXME Better to compare patterns one-at-a-time against all names.
+ */
+ if (_cache.preferred_names_matcher) {
+ m0 = dm_regex_match(_cache.preferred_names_matcher, path0);
+ m1 = dm_regex_match(_cache.preferred_names_matcher, path1);
+
+ if (m0 != m1) {
+ if (m0 < 0)
+ return 1;
+ if (m1 < 0)
+ return 0;
+ if (m0 < m1)
+ return 1;
+ if (m1 < m0)
+ return 0;
+ }
+ }
+
+ /* Apply built-in preference rules first. */
+ if ((r = _apply_builtin_path_preference_rules(path0, path1)) >= 0)
+ return r;
+
+ /* Return the path with fewer slashes */
+ for (p = path0; p++; p = (const char *) strchr(p, '/'))
+ slash0++;
+
+ for (p = path1; p++; p = (const char *) strchr(p, '/'))
+ slash1++;
+
+ if (slash0 < slash1)
+ return 0;
+ if (slash1 < slash0)
+ return 1;
+
+ strncpy(p0, path0, PATH_MAX);
+ strncpy(p1, path1, PATH_MAX);
+ s0 = &p0[0] + 1;
+ s1 = &p1[0] + 1;
+
+ /* We prefer symlinks - they exist for a reason!
+ * So we prefer a shorter path before the first symlink in the name.
+ * FIXME Configuration option to invert this? */
+ while (s0) {
+ s0 = strchr(s0, '/');
+ s1 = strchr(s1, '/');
+ if (s0) {
+ *s0 = '\0';
+ *s1 = '\0';
+ }
+ if (lstat(p0, &stat0)) {
+ log_sys_very_verbose("lstat", p0);
+ return 1;
+ }
+ if (lstat(p1, &stat1)) {
+ log_sys_very_verbose("lstat", p1);
+ return 0;
+ }
+ if (S_ISLNK(stat0.st_mode) && !S_ISLNK(stat1.st_mode))
+ return 0;
+ if (!S_ISLNK(stat0.st_mode) && S_ISLNK(stat1.st_mode))
+ return 1;
+ if (s0) {
+ *s0++ = '/';
+ *s1++ = '/';
+ }
+ }
+
+ /* ASCII comparison */
+ if (strcmp(path0, path1) < 0)
+ return 0;
+ else
+ return 1;
+}
+
+static int _add_alias(struct device *dev, const char *path)
+{
+ struct str_list *sl = _alloc(sizeof(*sl));
+ struct str_list *strl;
+ const char *oldpath;
+ int prefer_old = 1;
+
+ if (!sl)
+ return_0;
+
+ /* Is name already there? */
+ dm_list_iterate_items(strl, &dev->aliases) {
+ if (!strcmp(strl->str, path)) {
+ log_debug("%s: Already in device cache", path);
+ return 1;
+ }
+ }
+
+ if (!(sl->str = dm_pool_strdup(_cache.mem, path)))
+ return_0;
+
+ if (!dm_list_empty(&dev->aliases)) {
+ oldpath = dm_list_item(dev->aliases.n, struct str_list)->str;
+ prefer_old = _compare_paths(path, oldpath);
+ log_debug("%s: Aliased to %s in device cache%s",
+ path, oldpath, prefer_old ? "" : " (preferred name)");
+
+ } else
+ log_debug("%s: Added to device cache", path);
+
+ if (prefer_old)
+ dm_list_add(&dev->aliases, &sl->list);
+ else
+ dm_list_add_h(&dev->aliases, &sl->list);
+
+ return 1;
+}
+
+/*
+ * Either creates a new dev, or adds an alias to
+ * an existing dev.
+ */
+static int _insert_dev(const char *path, dev_t d)
+{
+ struct device *dev;
+ static dev_t loopfile_count = 0;
+ int loopfile = 0;
+
+ /* Generate pretend device numbers for loopfiles */
+ if (!d) {
+ if (dm_hash_lookup(_cache.names, path))
+ return 1;
+ d = ++loopfile_count;
+ loopfile = 1;
+ }
+
+ /* is this device already registered ? */
+ if (!(dev = (struct device *) btree_lookup(_cache.devices,
+ (uint32_t) d))) {
+ /* create new device */
+ if (loopfile) {
+ if (!(dev = dev_create_file(path, NULL, NULL, 0)))
+ return_0;
+ } else if (!(dev = _dev_create(d)))
+ return_0;
+
+ if (!(btree_insert(_cache.devices, (uint32_t) d, dev))) {
+ log_error("Couldn't insert device into binary tree.");
+ _free(dev);
+ return 0;
+ }
+ }
+
+ if (!loopfile && !_add_alias(dev, path)) {
+ log_error("Couldn't add alias to dev cache.");
+ return 0;
+ }
+
+ if (!dm_hash_insert(_cache.names, path, dev)) {
+ log_error("Couldn't add name to hash in dev cache.");
+ return 0;
+ }
+
+ return 1;
+}
+
+static char *_join(const char *dir, const char *name)
+{
+ size_t len = strlen(dir) + strlen(name) + 2;
+ char *r = dm_malloc(len);
+ if (r)
+ snprintf(r, len, "%s/%s", dir, name);
+
+ return r;
+}
+
+/*
+ * Get rid of extra slashes in the path string.
+ */
+static void _collapse_slashes(char *str)
+{
+ char *ptr;
+ int was_slash = 0;
+
+ for (ptr = str; *ptr; ptr++) {
+ if (*ptr == '/') {
+ if (was_slash)
+ continue;
+
+ was_slash = 1;
+ } else
+ was_slash = 0;
+ *str++ = *ptr;
+ }
+
+ *str = *ptr;
+}
+
+static int _insert_dir(const char *dir)
+{
+ int n, dirent_count, r = 1;
+ struct dirent **dirent;
+ char *path;
+
+ dirent_count = scandir(dir, &dirent, NULL, alphasort);
+ if (dirent_count > 0) {
+ for (n = 0; n < dirent_count; n++) {
+ if (dirent[n]->d_name[0] == '.') {
+ free(dirent[n]);
+ continue;
+ }
+
+ if (!(path = _join(dir, dirent[n]->d_name)))
+ return_0;
+
+ _collapse_slashes(path);
+ r &= _insert(path, 1);
+ dm_free(path);
+
+ free(dirent[n]);
+ }
+ free(dirent);
+ }
+
+ return r;
+}
+
+static int _insert_file(const char *path)
+{
+ struct stat info;
+
+ if (stat(path, &info) < 0) {
+ log_sys_very_verbose("stat", path);
+ return 0;
+ }
+
+ if (!S_ISREG(info.st_mode)) {
+ log_debug("%s: Not a regular file", path);
+ return 0;
+ }
+
+ if (!_insert_dev(path, 0))
+ return_0;
+
+ return 1;
+}
+
+static int _insert(const char *path, int rec)
+{
+ struct stat info;
+ int r = 0;
+
+ if (stat(path, &info) < 0) {
+ log_sys_very_verbose("stat", path);
+ return 0;
+ }
+
+ if (S_ISDIR(info.st_mode)) { /* add a directory */
+ /* check it's not a symbolic link */
+ if (lstat(path, &info) < 0) {
+ log_sys_very_verbose("lstat", path);
+ return 0;
+ }
+
+ if (S_ISLNK(info.st_mode)) {
+ log_debug("%s: Symbolic link to directory", path);
+ return 0;
+ }
+
+ if (rec)
+ r = _insert_dir(path);
+
+ } else { /* add a device */
+ if (!S_ISBLK(info.st_mode)) {
+ log_debug("%s: Not a block device", path);
+ return 0;
+ }
+
+ if (!_insert_dev(path, info.st_rdev))
+ return_0;
+
+ r = 1;
+ }
+
+ return r;
+}
+
+static void _full_scan(int dev_scan)
+{
+ struct dir_list *dl;
+
+ if (_cache.has_scanned && !dev_scan)
+ return;
+
+ dm_list_iterate_items(dl, &_cache.dirs)
+ _insert_dir(dl->dir);
+
+ dm_list_iterate_items(dl, &_cache.files)
+ _insert_file(dl->dir);
+
+ _cache.has_scanned = 1;
+ init_full_scan_done(1);
+}
+
+int dev_cache_has_scanned(void)
+{
+ return _cache.has_scanned;
+}
+
+void dev_cache_scan(int do_scan)
+{
+ if (!do_scan)
+ _cache.has_scanned = 1;
+ else
+ _full_scan(1);
+}
+
+static int _init_preferred_names(struct cmd_context *cmd)
+{
+ const struct config_node *cn;
+ const struct config_value *v;
+ struct dm_pool *scratch = NULL;
+ const char **regex;
+ unsigned count = 0;
+ int i, r = 0;
+
+ _cache.preferred_names_matcher = NULL;
+
+ if (!(cn = find_config_tree_node(cmd, "devices/preferred_names")) ||
+ cn->v->type == CFG_EMPTY_ARRAY) {
+ log_very_verbose("devices/preferred_names not found in config file: "
+ "using built-in preferences");
+ return 1;
+ }
+
+ for (v = cn->v; v; v = v->next) {
+ if (v->type != CFG_STRING) {
+ log_error("preferred_names patterns must be enclosed in quotes");
+ return 0;
+ }
+
+ count++;
+ }
+
+ if (!(scratch = dm_pool_create("preferred device name matcher", 1024)))
+ return_0;
+
+ if (!(regex = dm_pool_alloc(scratch, sizeof(*regex) * count))) {
+ log_error("Failed to allocate preferred device name "
+ "pattern list.");
+ goto out;
+ }
+
+ for (v = cn->v, i = count - 1; v; v = v->next, i--) {
+ if (!(regex[i] = dm_pool_strdup(scratch, v->v.str))) {
+ log_error("Failed to allocate a preferred device name "
+ "pattern.");
+ goto out;
+ }
+ }
+
+ if (!(_cache.preferred_names_matcher =
+ dm_regex_create(_cache.mem, regex, count))) {
+ log_error("Preferred device name pattern matcher creation failed.");
+ goto out;
+ }
+
+ r = 1;
+
+out:
+ dm_pool_destroy(scratch);
+
+ return r;
+}
+
+int dev_cache_init(struct cmd_context *cmd)
+{
+ _cache.names = NULL;
+ _cache.has_scanned = 0;
+
+ if (!(_cache.mem = dm_pool_create("dev_cache", 10 * 1024)))
+ return_0;
+
+ if (!(_cache.names = dm_hash_create(128))) {
+ dm_pool_destroy(_cache.mem);
+ _cache.mem = 0;
+ return_0;
+ }
+
+ if (!(_cache.devices = btree_create(_cache.mem))) {
+ log_error("Couldn't create binary tree for dev-cache.");
+ goto bad;
+ }
+
+ if (!(_cache.dev_dir = _strdup(cmd->dev_dir))) {
+ log_error("strdup dev_dir failed.");
+ goto bad;
+ }
+
+ dm_list_init(&_cache.dirs);
+ dm_list_init(&_cache.files);
+
+ if (!_init_preferred_names(cmd))
+ goto_bad;
+
+ return 1;
+
+ bad:
+ dev_cache_exit();
+ return 0;
+}
+
+static void _check_closed(struct device *dev)
+{
+ if (dev->fd >= 0)
+ log_error("Device '%s' has been left open.", dev_name(dev));
+}
+
+static void _check_for_open_devices(void)
+{
+ dm_hash_iter(_cache.names, (dm_hash_iterate_fn) _check_closed);
+}
+
+void dev_cache_exit(void)
+{
+ if (_cache.names)
+ _check_for_open_devices();
+
+ if (_cache.preferred_names_matcher)
+ _cache.preferred_names_matcher = NULL;
+
+ if (_cache.mem) {
+ dm_pool_destroy(_cache.mem);
+ _cache.mem = NULL;
+ }
+
+ if (_cache.names) {
+ dm_hash_destroy(_cache.names);
+ _cache.names = NULL;
+ }
+
+ _cache.devices = NULL;
+ _cache.has_scanned = 0;
+ dm_list_init(&_cache.dirs);
+ dm_list_init(&_cache.files);
+}
+
+int dev_cache_add_dir(const char *path)
+{
+ struct dir_list *dl;
+ struct stat st;
+
+ if (stat(path, &st)) {
+ log_error("Ignoring %s: %s", path, strerror(errno));
+ /* But don't fail */
+ return 1;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ log_error("Ignoring %s: Not a directory", path);
+ return 1;
+ }
+
+ if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1))) {
+ log_error("dir_list allocation failed");
+ return 0;
+ }
+
+ strcpy(dl->dir, path);
+ dm_list_add(&_cache.dirs, &dl->list);
+ return 1;
+}
+
+int dev_cache_add_loopfile(const char *path)
+{
+ struct dir_list *dl;
+ struct stat st;
+
+ if (stat(path, &st)) {
+ log_error("Ignoring %s: %s", path, strerror(errno));
+ /* But don't fail */
+ return 1;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ log_error("Ignoring %s: Not a regular file", path);
+ return 1;
+ }
+
+ if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1))) {
+ log_error("dir_list allocation failed for file");
+ return 0;
+ }
+
+ strcpy(dl->dir, path);
+ dm_list_add(&_cache.files, &dl->list);
+ return 1;
+}
+
+/* Check cached device name is still valid before returning it */
+/* This should be a rare occurrence */
+/* set quiet if the cache is expected to be out-of-date */
+/* FIXME Make rest of code pass/cache struct device instead of dev_name */
+const char *dev_name_confirmed(struct device *dev, int quiet)
+{
+ struct stat buf;
+ const char *name;
+ int r;
+
+ if ((dev->flags & DEV_REGULAR))
+ return dev_name(dev);
+
+ while ((r = stat(name = dm_list_item(dev->aliases.n,
+ struct str_list)->str, &buf)) ||
+ (buf.st_rdev != dev->dev)) {
+ if (r < 0) {
+ if (quiet)
+ log_sys_debug("stat", name);
+ else
+ log_sys_error("stat", name);
+ }
+ if (quiet)
+ log_debug("Path %s no longer valid for device(%d,%d)",
+ name, (int) MAJOR(dev->dev),
+ (int) MINOR(dev->dev));
+ else
+ log_error("Path %s no longer valid for device(%d,%d)",
+ name, (int) MAJOR(dev->dev),
+ (int) MINOR(dev->dev));
+
+ /* Remove the incorrect hash entry */
+ dm_hash_remove(_cache.names, name);
+
+ /* Leave list alone if there isn't an alternative name */
+ /* so dev_name will always find something to return. */
+ /* Otherwise add the name to the correct device. */
+ if (dm_list_size(&dev->aliases) > 1) {
+ dm_list_del(dev->aliases.n);
+ if (!r)
+ _insert(name, 0);
+ continue;
+ }
+
+ /* Scanning issues this inappropriately sometimes. */
+ log_debug("Aborting - please provide new pathname for what "
+ "used to be %s", name);
+ return NULL;
+ }
+
+ return dev_name(dev);
+}
+
+struct device *dev_cache_get(const char *name, struct dev_filter *f)
+{
+ struct stat buf;
+ struct device *d = (struct device *) dm_hash_lookup(_cache.names, name);
+
+ if (d && (d->flags & DEV_REGULAR))
+ return d;
+
+ /* If the entry's wrong, remove it */
+ if (d && (stat(name, &buf) || (buf.st_rdev != d->dev))) {
+ dm_hash_remove(_cache.names, name);
+ d = NULL;
+ }
+
+ if (!d) {
+ _insert(name, 0);
+ d = (struct device *) dm_hash_lookup(_cache.names, name);
+ if (!d) {
+ _full_scan(0);
+ d = (struct device *) dm_hash_lookup(_cache.names, name);
+ }
+ }
+
+ return (d && (!f || (d->flags & DEV_REGULAR) ||
+ f->passes_filter(f, d))) ? d : NULL;
+}
+
+struct dev_iter *dev_iter_create(struct dev_filter *f, int dev_scan)
+{
+ struct dev_iter *di = dm_malloc(sizeof(*di));
+
+ if (!di) {
+ log_error("dev_iter allocation failed");
+ return NULL;
+ }
+
+ if (dev_scan && !trust_cache()) {
+ /* Flag gets reset between each command */
+ if (!full_scan_done())
+ persistent_filter_wipe(f); /* Calls _full_scan(1) */
+ } else
+ _full_scan(0);
+
+ di->current = btree_first(_cache.devices);
+ di->filter = f;
+ di->filter->use_count++;
+
+ return di;
+}
+
+void dev_iter_destroy(struct dev_iter *iter)
+{
+ iter->filter->use_count--;
+ dm_free(iter);
+}
+
+static struct device *_iter_next(struct dev_iter *iter)
+{
+ struct device *d = btree_get_data(iter->current);
+ iter->current = btree_next(iter->current);
+ return d;
+}
+
+struct device *dev_iter_get(struct dev_iter *iter)
+{
+ while (iter->current) {
+ struct device *d = _iter_next(iter);
+ if (!iter->filter || (d->flags & DEV_REGULAR) ||
+ iter->filter->passes_filter(iter->filter, d))
+ return d;
+ }
+
+ return NULL;
+}
+
+void dev_reset_error_count(struct cmd_context *cmd)
+{
+ struct dev_iter *iter;
+ struct device *dev;
+
+ if (!(iter = dev_iter_create(cmd->filter, 0))) {
+ log_error("Resetting device error count failed");
+ return;
+ }
+
+ for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter))
+ dev->error_count = 0;
+
+ dev_iter_destroy(iter);
+}
+
+int dev_fd(struct device *dev)
+{
+ return dev->fd;
+}
+
+const char *dev_name(const struct device *dev)
+{
+ return (dev) ? dm_list_item(dev->aliases.n, struct str_list)->str :
+ "unknown device";
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_DEV_CACHE_H
+#define _LVM_DEV_CACHE_H
+
+#include "device.h"
+
+/*
+ * predicate for devices.
+ */
+struct dev_filter {
+ int (*passes_filter) (struct dev_filter * f, struct device * dev);
+ void (*destroy) (struct dev_filter * f);
+ unsigned use_count;
+ void *private;
+};
+
+/*
+ * The global device cache.
+ */
+struct cmd_context;
+int dev_cache_init(struct cmd_context *cmd);
+void dev_cache_exit(void);
+
+/* Trigger(1) or avoid(0) a scan */
+void dev_cache_scan(int do_scan);
+int dev_cache_has_scanned(void);
+
+int dev_cache_add_dir(const char *path);
+int dev_cache_add_loopfile(const char *path);
+struct device *dev_cache_get(const char *name, struct dev_filter *f);
+
+void dev_set_preferred_name(struct str_list *sl, struct device *dev);
+
+/*
+ * Object for iterating through the cache.
+ */
+struct dev_iter;
+struct dev_iter *dev_iter_create(struct dev_filter *f, int dev_scan);
+void dev_iter_destroy(struct dev_iter *iter);
+struct device *dev_iter_get(struct dev_iter *iter);
+
+void dev_reset_error_count(struct cmd_context *cmd);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "lvm-types.h"
+#include "device.h"
+#include "metadata.h"
+#include "lvmcache.h"
+#include "memlock.h"
+#include "locking.h"
+
+#include <limits.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef linux
+# define u64 uint64_t /* Missing without __KERNEL__ */
+# undef WNOHANG /* Avoid redefinition */
+# undef WUNTRACED /* Avoid redefinition */
+# include <linux/fs.h> /* For block ioctl definitions */
+# define BLKSIZE_SHIFT SECTOR_SHIFT
+# ifndef BLKGETSIZE64 /* fs.h out-of-date */
+# define BLKGETSIZE64 _IOR(0x12, 114, size_t)
+# endif /* BLKGETSIZE64 */
+#else
+# include <sys/disk.h>
+# define BLKBSZGET DKIOCGETBLOCKSIZE
+# define BLKSSZGET DKIOCGETBLOCKSIZE
+# define BLKGETSIZE64 DKIOCGETBLOCKCOUNT
+# define BLKFLSBUF DKIOCSYNCHRONIZECACHE
+# define BLKSIZE_SHIFT 0
+#endif
+
+#ifdef O_DIRECT_SUPPORT
+# ifndef O_DIRECT
+# error O_DIRECT support configured but O_DIRECT definition not found in headers
+# endif
+#endif
+
+static DM_LIST_INIT(_open_devices);
+
+/*-----------------------------------------------------------------
+ * The standard io loop that keeps submitting an io until it's
+ * all gone.
+ *---------------------------------------------------------------*/
+static int _io(struct device_area *where, void *buffer, int should_write)
+{
+ int fd = dev_fd(where->dev);
+ ssize_t n = 0;
+ size_t total = 0;
+
+ if (fd < 0) {
+ log_error("Attempt to read an unopened device (%s).",
+ dev_name(where->dev));
+ return 0;
+ }
+
+ /*
+ * Skip all writes in test mode.
+ */
+ if (should_write && test_mode())
+ return 1;
+
+ if (where->size > SSIZE_MAX) {
+ log_error("Read size too large: %" PRIu64, where->size);
+ return 0;
+ }
+
+ if (lseek(fd, (off_t) where->start, SEEK_SET) < 0) {
+ log_error("%s: lseek %" PRIu64 " failed: %s",
+ dev_name(where->dev), (uint64_t) where->start,
+ strerror(errno));
+ return 0;
+ }
+
+ while (total < (size_t) where->size) {
+ do
+ n = should_write ?
+ write(fd, buffer, (size_t) where->size - total) :
+ read(fd, buffer, (size_t) where->size - total);
+ while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
+
+ if (n < 0)
+ log_error_once("%s: %s failed after %" PRIu64 " of %" PRIu64
+ " at %" PRIu64 ": %s", dev_name(where->dev),
+ should_write ? "write" : "read",
+ (uint64_t) total,
+ (uint64_t) where->size,
+ (uint64_t) where->start, strerror(errno));
+
+ if (n <= 0)
+ break;
+
+ total += n;
+ buffer += n;
+ }
+
+ return (total == (size_t) where->size);
+}
+
+/*-----------------------------------------------------------------
+ * LVM2 uses O_DIRECT when performing metadata io, which requires
+ * block size aligned accesses. If any io is not aligned we have
+ * to perform the io via a bounce buffer, obviously this is quite
+ * inefficient.
+ *---------------------------------------------------------------*/
+
+/*
+ * Get the sector size from an _open_ device.
+ */
+static int _get_block_size(struct device *dev, unsigned int *size)
+{
+ const char *name = dev_name(dev);
+
+ if ((dev->block_size == -1)) {
+ if (ioctl(dev_fd(dev), BLKBSZGET, &dev->block_size) < 0) {
+ log_sys_error("ioctl BLKBSZGET", name);
+ return 0;
+ }
+ log_debug("%s: block size is %u bytes", name, dev->block_size);
+ }
+
+ *size = (unsigned int) dev->block_size;
+
+ return 1;
+}
+
+/*
+ * Widens a region to be an aligned region.
+ */
+static void _widen_region(unsigned int block_size, struct device_area *region,
+ struct device_area *result)
+{
+ uint64_t mask = block_size - 1, delta;
+ memcpy(result, region, sizeof(*result));
+
+ /* adjust the start */
+ delta = result->start & mask;
+ if (delta) {
+ result->start -= delta;
+ result->size += delta;
+ }
+
+ /* adjust the end */
+ delta = (result->start + result->size) & mask;
+ if (delta)
+ result->size += block_size - delta;
+}
+
+static int _aligned_io(struct device_area *where, void *buffer,
+ int should_write)
+{
+ void *bounce, *bounce_buf;
+ unsigned int block_size = 0;
+ uintptr_t mask;
+ struct device_area widened;
+ int r = 0;
+
+ if (!(where->dev->flags & DEV_REGULAR) &&
+ !_get_block_size(where->dev, &block_size))
+ return_0;
+
+ if (!block_size)
+ block_size = lvm_getpagesize();
+
+ _widen_region(block_size, where, &widened);
+
+ /* Do we need to use a bounce buffer? */
+ mask = block_size - 1;
+ if (!memcmp(where, &widened, sizeof(widened)) &&
+ !((uintptr_t) buffer & mask))
+ return _io(where, buffer, should_write);
+
+ /* Allocate a bounce buffer with an extra block */
+ if (!(bounce_buf = bounce = dm_malloc((size_t) widened.size + block_size))) {
+ log_error("Bounce buffer malloc failed");
+ return 0;
+ }
+
+ /*
+ * Realign start of bounce buffer (using the extra sector)
+ */
+ if (((uintptr_t) bounce) & mask)
+ bounce = (void *) ((((uintptr_t) bounce) + mask) & ~mask);
+
+ /* channel the io through the bounce buffer */
+ if (!_io(&widened, bounce, 0)) {
+ if (!should_write)
+ goto_out;
+ /* FIXME pre-extend the file */
+ memset(bounce, '\n', widened.size);
+ }
+
+ if (should_write) {
+ memcpy(bounce + (where->start - widened.start), buffer,
+ (size_t) where->size);
+
+ /* ... then we write */
+ if (!(r = _io(&widened, bounce, 1)))
+ stack;
+
+ goto out;
+ }
+
+ memcpy(buffer, bounce + (where->start - widened.start),
+ (size_t) where->size);
+
+ r = 1;
+
+out:
+ dm_free(bounce_buf);
+ return r;
+}
+
+static int _dev_get_size_file(const struct device *dev, uint64_t *size)
+{
+ const char *name = dev_name(dev);
+ struct stat info;
+
+ if (stat(name, &info)) {
+ log_sys_error("stat", name);
+ return 0;
+ }
+
+ *size = info.st_size;
+ *size >>= SECTOR_SHIFT; /* Convert to sectors */
+
+ log_very_verbose("%s: size is %" PRIu64 " sectors", name, *size);
+
+ return 1;
+}
+
+static int _dev_get_size_dev(const struct device *dev, uint64_t *size)
+{
+ int fd;
+ const char *name = dev_name(dev);
+
+ if ((fd = open(name, O_RDONLY)) < 0) {
+ log_sys_error("open", name);
+ return 0;
+ }
+
+ if (ioctl(fd, BLKGETSIZE64, size) < 0) {
+ log_sys_error("ioctl BLKGETSIZE64", name);
+ if (close(fd))
+ log_sys_error("close", name);
+ return 0;
+ }
+
+ *size >>= BLKSIZE_SHIFT; /* Convert to sectors */
+ if (close(fd))
+ log_sys_error("close", name);
+
+ log_very_verbose("%s: size is %" PRIu64 " sectors", name, *size);
+
+ return 1;
+}
+
+static int _dev_read_ahead_dev(struct device *dev, uint32_t *read_ahead)
+{
+ long read_ahead_long;
+
+ if (dev->read_ahead != -1) {
+ *read_ahead = (uint32_t) dev->read_ahead;
+ return 1;
+ }
+
+ if (!dev_open(dev))
+ return_0;
+
+ if (ioctl(dev->fd, BLKRAGET, &read_ahead_long) < 0) {
+ log_sys_error("ioctl BLKRAGET", dev_name(dev));
+ if (!dev_close(dev))
+ stack;
+ return 0;
+ }
+
+ if (!dev_close(dev))
+ stack;
+
+ *read_ahead = (uint32_t) read_ahead_long;
+ dev->read_ahead = read_ahead_long;
+
+ log_very_verbose("%s: read_ahead is %u sectors",
+ dev_name(dev), *read_ahead);
+
+ return 1;
+}
+
+/*-----------------------------------------------------------------
+ * Public functions
+ *---------------------------------------------------------------*/
+
+int dev_get_size(const struct device *dev, uint64_t *size)
+{
+ if (!dev)
+ return 0;
+
+ if ((dev->flags & DEV_REGULAR))
+ return _dev_get_size_file(dev, size);
+ else
+ return _dev_get_size_dev(dev, size);
+}
+
+int dev_get_read_ahead(struct device *dev, uint32_t *read_ahead)
+{
+ if (!dev)
+ return 0;
+
+ if (dev->flags & DEV_REGULAR) {
+ *read_ahead = 0;
+ return 1;
+ }
+
+ return _dev_read_ahead_dev(dev, read_ahead);
+}
+
+/* FIXME Unused
+int dev_get_sectsize(struct device *dev, uint32_t *size)
+{
+ int fd;
+ int s;
+ const char *name = dev_name(dev);
+
+ if ((fd = open(name, O_RDONLY)) < 0) {
+ log_sys_error("open", name);
+ return 0;
+ }
+
+ if (ioctl(fd, BLKSSZGET, &s) < 0) {
+ log_sys_error("ioctl BLKSSZGET", name);
+ if (close(fd))
+ log_sys_error("close", name);
+ return 0;
+ }
+
+ if (close(fd))
+ log_sys_error("close", name);
+
+ *size = (uint32_t) s;
+
+ log_very_verbose("%s: sector size is %" PRIu32 " bytes", name, *size);
+
+ return 1;
+}
+*/
+
+void dev_flush(struct device *dev)
+{
+ if (!(dev->flags & DEV_REGULAR) && ioctl(dev->fd, BLKFLSBUF, 0) >= 0)
+ return;
+
+ if (fsync(dev->fd) >= 0)
+ return;
+
+ sync();
+}
+
+int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
+{
+ struct stat buf;
+ const char *name;
+ int need_excl = 0, need_rw = 0;
+
+ if ((flags & O_ACCMODE) == O_RDWR)
+ need_rw = 1;
+
+ if ((flags & O_EXCL))
+ need_excl = 1;
+
+ if (dev->fd >= 0) {
+ if (((dev->flags & DEV_OPENED_RW) || !need_rw) &&
+ ((dev->flags & DEV_OPENED_EXCL) || !need_excl)) {
+ dev->open_count++;
+ return 1;
+ }
+
+ if (dev->open_count && !need_excl) {
+ /* FIXME Ensure we never get here */
+ log_error(INTERNAL_ERROR "%s already opened read-only",
+ dev_name(dev));
+ dev->open_count++;
+ }
+
+ dev_close_immediate(dev);
+ }
+
+ if (memlock())
+ /* FIXME Make this log_error */
+ log_verbose("dev_open(%s) called while suspended",
+ dev_name(dev));
+
+ if (dev->flags & DEV_REGULAR)
+ name = dev_name(dev);
+ else if (!(name = dev_name_confirmed(dev, quiet)))
+ return_0;
+
+ if (!(dev->flags & DEV_REGULAR)) {
+ if (stat(name, &buf) < 0) {
+ log_sys_error("%s: stat failed", name);
+ return 0;
+ }
+ if (buf.st_rdev != dev->dev) {
+ log_error("%s: device changed", name);
+ return 0;
+ }
+ }
+
+#ifdef O_DIRECT_SUPPORT
+ if (direct) {
+ if (!(dev->flags & DEV_O_DIRECT_TESTED))
+ dev->flags |= DEV_O_DIRECT;
+
+ if ((dev->flags & DEV_O_DIRECT))
+ flags |= O_DIRECT;
+ }
+#endif
+
+#ifdef O_NOATIME
+ /* Don't update atime on device inodes */
+ if (!(dev->flags & DEV_REGULAR))
+ flags |= O_NOATIME;
+#endif
+
+ if ((dev->fd = open(name, flags, 0777)) < 0) {
+#ifdef O_DIRECT_SUPPORT
+ if (direct && !(dev->flags & DEV_O_DIRECT_TESTED)) {
+ flags &= ~O_DIRECT;
+ if ((dev->fd = open(name, flags, 0777)) >= 0) {
+ dev->flags &= ~DEV_O_DIRECT;
+ log_debug("%s: Not using O_DIRECT", name);
+ goto opened;
+ }
+ }
+#endif
+ if (quiet)
+ log_sys_debug("open", name);
+ else
+ log_sys_error("open", name);
+ return 0;
+ }
+
+#ifdef O_DIRECT_SUPPORT
+ opened:
+ if (direct)
+ dev->flags |= DEV_O_DIRECT_TESTED;
+#endif
+ dev->open_count++;
+ dev->flags &= ~DEV_ACCESSED_W;
+
+ if (need_rw)
+ dev->flags |= DEV_OPENED_RW;
+ else
+ dev->flags &= ~DEV_OPENED_RW;
+
+ if (need_excl)
+ dev->flags |= DEV_OPENED_EXCL;
+ else
+ dev->flags &= ~DEV_OPENED_EXCL;
+
+ if (!(dev->flags & DEV_REGULAR) &&
+ ((fstat(dev->fd, &buf) < 0) || (buf.st_rdev != dev->dev))) {
+ log_error("%s: fstat failed: Has device name changed?", name);
+ dev_close_immediate(dev);
+ return 0;
+ }
+
+#ifndef O_DIRECT_SUPPORT
+ if (!(dev->flags & DEV_REGULAR))
+ dev_flush(dev);
+#endif
+
+ if ((flags & O_CREAT) && !(flags & O_TRUNC))
+ dev->end = lseek(dev->fd, (off_t) 0, SEEK_END);
+
+ dm_list_add(&_open_devices, &dev->open_list);
+
+ log_debug("Opened %s %s%s%s", dev_name(dev),
+ dev->flags & DEV_OPENED_RW ? "RW" : "RO",
+ dev->flags & DEV_OPENED_EXCL ? " O_EXCL" : "",
+ dev->flags & DEV_O_DIRECT ? " O_DIRECT" : "");
+
+ return 1;
+}
+
+int dev_open_quiet(struct device *dev)
+{
+ int flags;
+
+ flags = vg_write_lock_held() ? O_RDWR : O_RDONLY;
+
+ return dev_open_flags(dev, flags, 1, 1);
+}
+
+int dev_open(struct device *dev)
+{
+ int flags;
+
+ flags = vg_write_lock_held() ? O_RDWR : O_RDONLY;
+
+ return dev_open_flags(dev, flags, 1, 0);
+}
+
+int dev_test_excl(struct device *dev)
+{
+ int flags;
+ int r;
+
+ flags = vg_write_lock_held() ? O_RDWR : O_RDONLY;
+ flags |= O_EXCL;
+
+ r = dev_open_flags(dev, flags, 1, 1);
+ if (r)
+ dev_close_immediate(dev);
+
+ return r;
+}
+
+static void _close(struct device *dev)
+{
+ if (close(dev->fd))
+ log_sys_error("close", dev_name(dev));
+ dev->fd = -1;
+ dev->block_size = -1;
+ dm_list_del(&dev->open_list);
+
+ log_debug("Closed %s", dev_name(dev));
+
+ if (dev->flags & DEV_ALLOCED) {
+ dm_free((void *) dm_list_item(dev->aliases.n, struct str_list)->
+ str);
+ dm_free(dev->aliases.n);
+ dm_free(dev);
+ }
+}
+
+static int _dev_close(struct device *dev, int immediate)
+{
+ struct lvmcache_info *info;
+
+ if (dev->fd < 0) {
+ log_error("Attempt to close device '%s' "
+ "which is not open.", dev_name(dev));
+ return 0;
+ }
+
+#ifndef O_DIRECT_SUPPORT
+ if (dev->flags & DEV_ACCESSED_W)
+ dev_flush(dev);
+#endif
+
+ if (dev->open_count > 0)
+ dev->open_count--;
+
+ if (immediate && dev->open_count)
+ log_debug("%s: Immediate close attempt while still referenced",
+ dev_name(dev));
+
+ /* Close unless device is known to belong to a locked VG */
+ if (immediate ||
+ (dev->open_count < 1 &&
+ (!(info = info_from_pvid(dev->pvid, 0)) ||
+ !info->vginfo ||
+ !vgname_is_locked(info->vginfo->vgname))))
+ _close(dev);
+
+ return 1;
+}
+
+int dev_close(struct device *dev)
+{
+ return _dev_close(dev, 0);
+}
+
+int dev_close_immediate(struct device *dev)
+{
+ return _dev_close(dev, 1);
+}
+
+void dev_close_all(void)
+{
+ struct dm_list *doh, *doht;
+ struct device *dev;
+
+ dm_list_iterate_safe(doh, doht, &_open_devices) {
+ dev = dm_list_struct_base(doh, struct device, open_list);
+ if (dev->open_count < 1)
+ _close(dev);
+ }
+}
+
+static inline int _dev_is_valid(struct device *dev)
+{
+ return (dev->max_error_count == NO_DEV_ERROR_COUNT_LIMIT ||
+ dev->error_count < dev->max_error_count);
+}
+
+static void _dev_inc_error_count(struct device *dev)
+{
+ if (++dev->error_count == dev->max_error_count)
+ log_warn("WARNING: Error counts reached a limit of %d. "
+ "Device %s was disabled",
+ dev->max_error_count, dev_name(dev));
+}
+
+int dev_read(struct device *dev, uint64_t offset, size_t len, void *buffer)
+{
+ struct device_area where;
+ int ret;
+
+ if (!dev->open_count)
+ return_0;
+
+ if (!_dev_is_valid(dev))
+ return 0;
+
+ where.dev = dev;
+ where.start = offset;
+ where.size = len;
+
+ ret = _aligned_io(&where, buffer, 0);
+ if (!ret)
+ _dev_inc_error_count(dev);
+
+ return ret;
+}
+
+/*
+ * Read from 'dev' into 'buf', possibly in 2 distinct regions, denoted
+ * by (offset,len) and (offset2,len2). Thus, the total size of
+ * 'buf' should be len+len2.
+ */
+int dev_read_circular(struct device *dev, uint64_t offset, size_t len,
+ uint64_t offset2, size_t len2, void *buf)
+{
+ if (!dev_read(dev, offset, len, buf)) {
+ log_error("Read from %s failed", dev_name(dev));
+ return 0;
+ }
+
+ /*
+ * The second region is optional, and allows for
+ * a circular buffer on the device.
+ */
+ if (!len2)
+ return 1;
+
+ if (!dev_read(dev, offset2, len2, buf + len)) {
+ log_error("Circular read from %s failed",
+ dev_name(dev));
+ return 0;
+ }
+
+ return 1;
+}
+
+/* FIXME If O_DIRECT can't extend file, dev_extend first; dev_truncate after.
+ * But fails if concurrent processes writing
+ */
+
+/* FIXME pre-extend the file */
+int dev_append(struct device *dev, size_t len, void *buffer)
+{
+ int r;
+
+ if (!dev->open_count)
+ return_0;
+
+ r = dev_write(dev, dev->end, len, buffer);
+ dev->end += (uint64_t) len;
+
+#ifndef O_DIRECT_SUPPORT
+ dev_flush(dev);
+#endif
+ return r;
+}
+
+int dev_write(struct device *dev, uint64_t offset, size_t len, void *buffer)
+{
+ struct device_area where;
+ int ret;
+
+ if (!dev->open_count)
+ return_0;
+
+ if (!_dev_is_valid(dev))
+ return 0;
+
+ where.dev = dev;
+ where.start = offset;
+ where.size = len;
+
+ dev->flags |= DEV_ACCESSED_W;
+
+ ret = _aligned_io(&where, buffer, 1);
+ if (!ret)
+ _dev_inc_error_count(dev);
+
+ return ret;
+}
+
+int dev_set(struct device *dev, uint64_t offset, size_t len, int value)
+{
+ size_t s;
+ char buffer[4096] __attribute__((aligned(8)));
+
+ if (!dev_open(dev))
+ return_0;
+
+ if ((offset % SECTOR_SIZE) || (len % SECTOR_SIZE))
+ log_debug("Wiping %s at %" PRIu64 " length %" PRIsize_t,
+ dev_name(dev), offset, len);
+ else
+ log_debug("Wiping %s at sector %" PRIu64 " length %" PRIsize_t
+ " sectors", dev_name(dev), offset >> SECTOR_SHIFT,
+ len >> SECTOR_SHIFT);
+
+ memset(buffer, value, sizeof(buffer));
+ while (1) {
+ s = len > sizeof(buffer) ? sizeof(buffer) : len;
+ if (!dev_write(dev, offset, s, buffer))
+ break;
+
+ len -= s;
+ if (!len)
+ break;
+
+ offset += s;
+ }
+
+ dev->flags |= DEV_ACCESSED_W;
+
+ if (!dev_close(dev))
+ stack;
+
+ return (len == 0);
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+
+#define LUKS_SIGNATURE "LUKS\xba\xbe"
+#define LUKS_SIGNATURE_SIZE 6
+
+int dev_is_luks(struct device *dev, uint64_t *signature)
+{
+ char buf[LUKS_SIGNATURE_SIZE];
+ int ret = -1;
+
+ if (!dev_open(dev)) {
+ stack;
+ return -1;
+ }
+
+ *signature = 0;
+
+ if (!dev_read(dev, 0, LUKS_SIGNATURE_SIZE, buf))
+ goto_out;
+
+ ret = memcmp(buf, LUKS_SIGNATURE, LUKS_SIGNATURE_SIZE) ? 0 : 1;
+
+out:
+ if (!dev_close(dev))
+ stack;
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2004 Luca Berra
+ * Copyright (C) 2004-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "xlate.h"
+#include "filter.h"
+
+#ifdef linux
+
+/* Lifted from <linux/raid/md_p.h> because of difficulty including it */
+
+#define MD_SB_MAGIC 0xa92b4efc
+#define MD_RESERVED_BYTES (64 * 1024ULL)
+#define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512)
+#define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) \
+ - MD_RESERVED_SECTORS)
+
+static int _dev_has_md_magic(struct device *dev, uint64_t sb_offset)
+{
+ uint32_t md_magic;
+
+ /* Version 1 is little endian; version 0.90.0 is machine endian */
+ if (dev_read(dev, sb_offset, sizeof(uint32_t), &md_magic) &&
+ ((md_magic == xlate32(MD_SB_MAGIC)) ||
+ (md_magic == MD_SB_MAGIC)))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Calculate the position of the superblock.
+ * It is always aligned to a 4K boundary and
+ * depending on minor_version, it can be:
+ * 0: At least 8K, but less than 12K, from end of device
+ * 1: At start of device
+ * 2: 4K from start of device.
+ */
+typedef enum {
+ MD_MINOR_VERSION_MIN,
+ MD_MINOR_V0 = MD_MINOR_VERSION_MIN,
+ MD_MINOR_V1,
+ MD_MINOR_V2,
+ MD_MINOR_VERSION_MAX = MD_MINOR_V2
+} md_minor_version_t;
+
+static uint64_t _v1_sb_offset(uint64_t size, md_minor_version_t minor_version)
+{
+ uint64_t uninitialized_var(sb_offset);
+
+ switch(minor_version) {
+ case MD_MINOR_V0:
+ sb_offset = (size - 8 * 2) & ~(4 * 2 - 1ULL);
+ break;
+ case MD_MINOR_V1:
+ sb_offset = 0;
+ break;
+ case MD_MINOR_V2:
+ sb_offset = 4 * 2;
+ break;
+ }
+ sb_offset <<= SECTOR_SHIFT;
+
+ return sb_offset;
+}
+
+/*
+ * Returns -1 on error
+ */
+int dev_is_md(struct device *dev, uint64_t *sb)
+{
+ int ret = 1;
+ md_minor_version_t minor;
+ uint64_t size, sb_offset;
+
+ if (!dev_get_size(dev, &size)) {
+ stack;
+ return -1;
+ }
+
+ if (size < MD_RESERVED_SECTORS * 2)
+ return 0;
+
+ if (!dev_open(dev)) {
+ stack;
+ return -1;
+ }
+
+ /* Check if it is an md component device. */
+ /* Version 0.90.0 */
+ sb_offset = MD_NEW_SIZE_SECTORS(size) << SECTOR_SHIFT;
+ if (_dev_has_md_magic(dev, sb_offset))
+ goto out;
+
+ minor = MD_MINOR_VERSION_MIN;
+ /* Version 1, try v1.0 -> v1.2 */
+ do {
+ sb_offset = _v1_sb_offset(size, minor);
+ if (_dev_has_md_magic(dev, sb_offset))
+ goto out;
+ } while (++minor <= MD_MINOR_VERSION_MAX);
+
+ ret = 0;
+
+out:
+ if (!dev_close(dev))
+ stack;
+
+ if (ret && sb)
+ *sb = sb_offset;
+
+ return ret;
+}
+
+static int _md_sysfs_attribute_snprintf(char *path, size_t size,
+ const char *sysfs_dir,
+ struct device *blkdev,
+ const char *attribute)
+{
+ struct stat info;
+ dev_t dev = blkdev->dev;
+ int ret = -1;
+
+ if (!sysfs_dir || !*sysfs_dir)
+ return ret;
+
+ if (MAJOR(dev) == blkext_major()) {
+ /* lookup parent MD device from blkext partition */
+ if (!get_primary_dev(sysfs_dir, blkdev, &dev))
+ return ret;
+ }
+
+ if (MAJOR(dev) != md_major())
+ return ret;
+
+ ret = dm_snprintf(path, size, "%s/dev/block/%d:%d/md/%s", sysfs_dir,
+ (int)MAJOR(dev), (int)MINOR(dev), attribute);
+ if (ret < 0) {
+ log_error("dm_snprintf md %s failed", attribute);
+ return ret;
+ }
+
+ if (stat(path, &info) == -1) {
+ if (errno != ENOENT) {
+ log_sys_error("stat", path);
+ return ret;
+ }
+ /* old sysfs structure */
+ ret = dm_snprintf(path, size, "%s/block/md%d/md/%s",
+ sysfs_dir, (int)MINOR(dev), attribute);
+ if (ret < 0) {
+ log_error("dm_snprintf old md %s failed", attribute);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int _md_sysfs_attribute_scanf(const char *sysfs_dir,
+ struct device *dev,
+ const char *attribute_name,
+ const char *attribute_fmt,
+ void *attribute_value)
+{
+ char path[PATH_MAX+1], buffer[64];
+ FILE *fp;
+ int ret = 0;
+
+ if (_md_sysfs_attribute_snprintf(path, PATH_MAX, sysfs_dir,
+ dev, attribute_name) < 0)
+ return ret;
+
+ if (!(fp = fopen(path, "r"))) {
+ log_sys_error("fopen", path);
+ return ret;
+ }
+
+ if (!fgets(buffer, sizeof(buffer), fp)) {
+ log_sys_error("fgets", path);
+ goto out;
+ }
+
+ if ((ret = sscanf(buffer, attribute_fmt, attribute_value)) != 1) {
+ log_error("%s sysfs attr %s not in expected format: %s",
+ dev_name(dev), attribute_name, buffer);
+ goto out;
+ }
+
+out:
+ if (fclose(fp))
+ log_sys_error("fclose", path);
+
+ return ret;
+}
+
+/*
+ * Retrieve chunk size from md device using sysfs.
+ */
+static unsigned long dev_md_chunk_size(const char *sysfs_dir,
+ struct device *dev)
+{
+ const char *attribute = "chunk_size";
+ unsigned long chunk_size_bytes = 0UL;
+
+ if (_md_sysfs_attribute_scanf(sysfs_dir, dev, attribute,
+ "%lu", &chunk_size_bytes) != 1)
+ return 0;
+
+ log_very_verbose("Device %s %s is %lu bytes.",
+ dev_name(dev), attribute, chunk_size_bytes);
+
+ return chunk_size_bytes >> SECTOR_SHIFT;
+}
+
+/*
+ * Retrieve level from md device using sysfs.
+ */
+static int dev_md_level(const char *sysfs_dir, struct device *dev)
+{
+ const char *attribute = "level";
+ int level = -1;
+
+ if (_md_sysfs_attribute_scanf(sysfs_dir, dev, attribute,
+ "raid%d", &level) != 1)
+ return -1;
+
+ log_very_verbose("Device %s %s is raid%d.",
+ dev_name(dev), attribute, level);
+
+ return level;
+}
+
+/*
+ * Retrieve raid_disks from md device using sysfs.
+ */
+static int dev_md_raid_disks(const char *sysfs_dir, struct device *dev)
+{
+ const char *attribute = "raid_disks";
+ int raid_disks = 0;
+
+ if (_md_sysfs_attribute_scanf(sysfs_dir, dev, attribute,
+ "%d", &raid_disks) != 1)
+ return 0;
+
+ log_very_verbose("Device %s %s is %d.",
+ dev_name(dev), attribute, raid_disks);
+
+ return raid_disks;
+}
+
+/*
+ * Calculate stripe width of md device using its sysfs files.
+ */
+unsigned long dev_md_stripe_width(const char *sysfs_dir, struct device *dev)
+{
+ unsigned long chunk_size_sectors = 0UL;
+ unsigned long stripe_width_sectors = 0UL;
+ int level, raid_disks, data_disks;
+
+ chunk_size_sectors = dev_md_chunk_size(sysfs_dir, dev);
+ if (!chunk_size_sectors)
+ return 0;
+
+ level = dev_md_level(sysfs_dir, dev);
+ if (level < 0)
+ return 0;
+
+ raid_disks = dev_md_raid_disks(sysfs_dir, dev);
+ if (!raid_disks)
+ return 0;
+
+ /* The raid level governs the number of data disks. */
+ switch (level) {
+ case 0:
+ /* striped md does not have any parity disks */
+ data_disks = raid_disks;
+ break;
+ case 1:
+ case 10:
+ /* mirrored md effectively has 1 data disk */
+ data_disks = 1;
+ break;
+ case 4:
+ case 5:
+ /* both raid 4 and 5 have a single parity disk */
+ data_disks = raid_disks - 1;
+ break;
+ case 6:
+ /* raid 6 has 2 parity disks */
+ data_disks = raid_disks - 2;
+ break;
+ default:
+ log_error("Device %s has an unknown md raid level: %d",
+ dev_name(dev), level);
+ return 0;
+ }
+
+ stripe_width_sectors = chunk_size_sectors * data_disks;
+
+ log_very_verbose("Device %s stripe-width is %lu bytes.",
+ dev_name(dev),
+ stripe_width_sectors << SECTOR_SHIFT);
+
+ return stripe_width_sectors;
+}
+
+#else
+
+int dev_is_md(struct device *dev __attribute__((unused)),
+ uint64_t *sb __attribute__((unused)))
+{
+ return 0;
+}
+
+unsigned long dev_md_stripe_width(const char *sysfs_dir __attribute__((unused)),
+ struct device *dev __attribute__((unused)))
+{
+ return 0UL;
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "xlate.h"
+#include "filter.h"
+
+#ifdef linux
+
+#define MAX_PAGESIZE (64 * 1024)
+#define SIGNATURE_SIZE 10
+
+static int
+_swap_detect_signature(const char *buf)
+{
+ if (memcmp(buf, "SWAP-SPACE", 10) == 0 ||
+ memcmp(buf, "SWAPSPACE2", 10) == 0)
+ return 1;
+
+ if (memcmp(buf, "S1SUSPEND", 9) == 0 ||
+ memcmp(buf, "S2SUSPEND", 9) == 0 ||
+ memcmp(buf, "ULSUSPEND", 9) == 0 ||
+ memcmp(buf, "\xed\xc3\x02\xe9\x98\x56\xe5\x0c", 8) == 0)
+ return 1;
+
+ return 0;
+}
+
+int dev_is_swap(struct device *dev, uint64_t *signature)
+{
+ char buf[10];
+ uint64_t size;
+ int page, ret = 0;
+
+ if (!dev_get_size(dev, &size)) {
+ stack;
+ return -1;
+ }
+
+ if (!dev_open(dev)) {
+ stack;
+ return -1;
+ }
+
+ *signature = 0;
+
+ for (page = 0x1000; page <= MAX_PAGESIZE; page <<= 1) {
+ /*
+ * skip 32k pagesize since this does not seem to be supported
+ */
+ if (page == 0x8000)
+ continue;
+ if (size < page)
+ break;
+ if (!dev_read(dev, page - SIGNATURE_SIZE,
+ SIGNATURE_SIZE, buf)) {
+ ret = -1;
+ break;
+ }
+ if (_swap_detect_signature(buf)) {
+ *signature = page - SIGNATURE_SIZE;
+ ret = 1;
+ break;
+ }
+ }
+
+ if (!dev_close(dev))
+ stack;
+
+ return ret;
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "lvm-types.h"
+#include "device.h"
+#include "metadata.h"
+#include "filter.h"
+#include "xlate.h"
+
+#include <libgen.h> /* dirname, basename */
+
+/* See linux/genhd.h and fs/partitions/msdos */
+
+#define PART_MAGIC 0xAA55
+#define PART_MAGIC_OFFSET UINT64_C(0x1FE)
+#define PART_OFFSET UINT64_C(0x1BE)
+
+struct partition {
+ uint8_t boot_ind;
+ uint8_t head;
+ uint8_t sector;
+ uint8_t cyl;
+ uint8_t sys_ind; /* partition type */
+ uint8_t end_head;
+ uint8_t end_sector;
+ uint8_t end_cyl;
+ uint32_t start_sect;
+ uint32_t nr_sects;
+} __attribute__((packed));
+
+static int _is_partitionable(struct device *dev)
+{
+ int parts = max_partitions(MAJOR(dev->dev));
+
+ /* All MD devices are partitionable via blkext (as of 2.6.28) */
+ if (MAJOR(dev->dev) == md_major())
+ return 1;
+
+ if ((parts <= 1) || (MINOR(dev->dev) % parts))
+ return 0;
+
+ return 1;
+}
+
+static int _has_partition_table(struct device *dev)
+{
+ int ret = 0;
+ unsigned p;
+ struct {
+ uint8_t skip[PART_OFFSET];
+ struct partition part[4];
+ uint16_t magic;
+ } __attribute__((packed)) buf; /* sizeof() == SECTOR_SIZE */
+
+ if (!dev_read(dev, UINT64_C(0), sizeof(buf), &buf))
+ return_0;
+
+ /* FIXME Check for other types of partition table too */
+
+ /* Check for msdos partition table */
+ if (buf.magic == xlate16(PART_MAGIC)) {
+ for (p = 0; p < 4; ++p) {
+ /* Table is invalid if boot indicator not 0 or 0x80 */
+ if (buf.part[p].boot_ind & 0x7f) {
+ ret = 0;
+ break;
+ }
+ /* Must have at least one non-empty partition */
+ if (buf.part[p].nr_sects)
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+int is_partitioned_dev(struct device *dev)
+{
+ if (!_is_partitionable(dev))
+ return 0;
+
+ return _has_partition_table(dev);
+}
+
+#if 0
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/genhd.h>
+
+int _get_partition_type(struct dev_filter *filter, struct device *d);
+
+#define MINOR_PART(dev) (MINOR((dev)->dev) % max_partitions(MINOR((dev)->dev)))
+
+int is_extended_partition(struct device *d)
+{
+ return (MINOR_PART(d) > 4) ? 1 : 0;
+}
+
+struct device *dev_primary(struct dev_mgr *dm, struct device *d)
+{
+ struct device *ret;
+
+ ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
+ /* FIXME: Needs replacing with a 'refresh' */
+ if (!ret) {
+ init_dev_scan(dm);
+ ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
+ }
+
+ return ret;
+
+}
+
+int partition_type_is_lvm(struct dev_mgr *dm, struct device *d)
+{
+ int pt;
+
+ pt = _get_partition_type(dm, d);
+
+ if (!pt) {
+ if (is_whole_disk(dm, d))
+ /* FIXME: Overloaded pt=0 in error cases */
+ return 1;
+ else {
+ log_error
+ ("%s: missing partition table "
+ "on partitioned device", d->name);
+ return 0;
+ }
+ }
+
+ if (is_whole_disk(dm, d)) {
+ log_error("%s: looks to possess partition table", d->name);
+ return 0;
+ }
+
+ /* check part type */
+ if (pt != LVM_PARTITION && pt != LVM_NEW_PARTITION) {
+ log_error("%s: invalid partition type 0x%x "
+ "(must be 0x%x)", d->name, pt, LVM_NEW_PARTITION);
+ return 0;
+ }
+
+ if (pt == LVM_PARTITION) {
+ log_error
+ ("%s: old LVM partition type found - please change to 0x%x",
+ d->name, LVM_NEW_PARTITION);
+ return 0;
+ }
+
+ return 1;
+}
+
+int _get_partition_type(struct dev_mgr *dm, struct device *d)
+{
+ int pv_handle = -1;
+ struct device *primary;
+ ssize_t read_ret;
+ ssize_t bytes_read = 0;
+ char *buffer;
+ unsigned short *s_buffer;
+ struct partition *part;
+ loff_t offset = 0;
+ loff_t extended_offset = 0;
+ int part_sought;
+ int part_found = 0;
+ int first_partition = 1;
+ int extended_partition = 0;
+ int p;
+
+ if (!(primary = dev_primary(dm, d))) {
+ log_error
+ ("Failed to find main device containing partition %s",
+ d->name);
+ return 0;
+ }
+
+ if (!(buffer = dm_malloc(SECTOR_SIZE))) {
+ log_error("Failed to allocate partition table buffer");
+ return 0;
+ }
+
+ /* Get partition table */
+ if ((pv_handle = open(primary->name, O_RDONLY)) < 0) {
+ log_error("%s: open failed: %s", primary->name,
+ strerror(errno));
+ return 0;
+ }
+
+ s_buffer = (unsigned short *) buffer;
+ part = (struct partition *) (buffer + 0x1be);
+ part_sought = MINOR_PART(dm, d);
+
+ do {
+ bytes_read = 0;
+
+ if (llseek(pv_handle, offset * SECTOR_SIZE, SEEK_SET) == -1) {
+ log_error("%s: llseek failed: %s",
+ primary->name, strerror(errno));
+ return 0;
+ }
+
+ while ((bytes_read < SECTOR_SIZE) &&
+ (read_ret =
+ read(pv_handle, buffer + bytes_read,
+ SECTOR_SIZE - bytes_read)) != -1)
+ bytes_read += read_ret;
+
+ if (read_ret == -1) {
+ log_error("%s: read failed: %s", primary->name,
+ strerror(errno));
+ return 0;
+ }
+
+ if (s_buffer[255] == 0xAA55) {
+ if (is_whole_disk(dm, d))
+ return -1;
+ } else
+ return 0;
+
+ extended_partition = 0;
+
+ /* Loop through primary partitions */
+ for (p = 0; p < 4; p++) {
+ if (part[p].sys_ind == DOS_EXTENDED_PARTITION ||
+ part[p].sys_ind == LINUX_EXTENDED_PARTITION
+ || part[p].sys_ind == WIN98_EXTENDED_PARTITION) {
+ extended_partition = 1;
+ offset = extended_offset + part[p].start_sect;
+ if (extended_offset == 0)
+ extended_offset = part[p].start_sect;
+ if (first_partition == 1)
+ part_found++;
+ } else if (first_partition == 1) {
+ if (p == part_sought) {
+ if (part[p].sys_ind == 0) {
+ /* missing primary? */
+ return 0;
+ }
+ } else
+ part_found++;
+ } else if (!part[p].sys_ind)
+ part_found++;
+
+ if (part_sought == part_found)
+ return part[p].sys_ind;
+
+ }
+ first_partition = 0;
+ }
+ while (extended_partition == 1);
+
+ return 0;
+}
+#endif
+
+#ifdef linux
+
+int get_primary_dev(const char *sysfs_dir,
+ const struct device *dev, dev_t *result)
+{
+ char path[PATH_MAX+1];
+ char temp_path[PATH_MAX+1];
+ char buffer[64];
+ struct stat info;
+ FILE *fp;
+ uint32_t pri_maj, pri_min;
+ int ret = 0;
+
+ /* check if dev is a partition */
+ if (dm_snprintf(path, PATH_MAX, "%s/dev/block/%d:%d/partition",
+ sysfs_dir, (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) {
+ log_error("dm_snprintf partition failed");
+ return ret;
+ }
+
+ if (stat(path, &info) == -1) {
+ if (errno != ENOENT)
+ log_sys_error("stat", path);
+ return ret;
+ }
+
+ /*
+ * extract parent's path from the partition's symlink, e.g.:
+ * - readlink /sys/dev/block/259:0 = ../../block/md0/md0p1
+ * - dirname ../../block/md0/md0p1 = ../../block/md0
+ * - basename ../../block/md0/md0 = md0
+ * Parent's 'dev' sysfs attribute = /sys/block/md0/dev
+ */
+ if (readlink(dirname(path), temp_path, PATH_MAX) < 0) {
+ log_sys_error("readlink", path);
+ return ret;
+ }
+
+ if (dm_snprintf(path, PATH_MAX, "%s/block/%s/dev",
+ sysfs_dir, basename(dirname(temp_path))) < 0) {
+ log_error("dm_snprintf dev failed");
+ return ret;
+ }
+
+ /* finally, parse 'dev' attribute and create corresponding dev_t */
+ if (stat(path, &info) == -1) {
+ if (errno == ENOENT)
+ log_error("sysfs file %s does not exist", path);
+ else
+ log_sys_error("stat", path);
+ return ret;
+ }
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ log_sys_error("fopen", path);
+ return ret;
+ }
+
+ if (!fgets(buffer, sizeof(buffer), fp)) {
+ log_sys_error("fgets", path);
+ goto out;
+ }
+
+ if (sscanf(buffer, "%d:%d", &pri_maj, &pri_min) != 2) {
+ log_error("sysfs file %s not in expected MAJ:MIN format: %s",
+ path, buffer);
+ goto out;
+ }
+ *result = MKDEV(pri_maj, pri_min);
+ ret = 1;
+
+out:
+ if (fclose(fp))
+ log_sys_error("fclose", path);
+
+ return ret;
+}
+
+static unsigned long _dev_topology_attribute(const char *attribute,
+ const char *sysfs_dir,
+ struct device *dev)
+{
+ const char *sysfs_fmt_str = "%s/dev/block/%d:%d/%s";
+ char path[PATH_MAX+1], buffer[64];
+ FILE *fp;
+ struct stat info;
+ dev_t uninitialized_var(primary);
+ unsigned long result = 0UL;
+
+ if (!attribute || !*attribute)
+ return_0;
+
+ if (!sysfs_dir || !*sysfs_dir)
+ return_0;
+
+ if (dm_snprintf(path, PATH_MAX, sysfs_fmt_str, sysfs_dir,
+ (int)MAJOR(dev->dev), (int)MINOR(dev->dev),
+ attribute) < 0) {
+ log_error("dm_snprintf %s failed", attribute);
+ return 0;
+ }
+
+ /*
+ * check if the desired sysfs attribute exists
+ * - if not: either the kernel doesn't have topology support
+ * or the device could be a partition
+ */
+ if (stat(path, &info) == -1) {
+ if (errno != ENOENT) {
+ log_sys_error("stat", path);
+ return 0;
+ }
+ if (!get_primary_dev(sysfs_dir, dev, &primary))
+ return 0;
+
+ /* get attribute from partition's primary device */
+ if (dm_snprintf(path, PATH_MAX, sysfs_fmt_str, sysfs_dir,
+ (int)MAJOR(primary), (int)MINOR(primary),
+ attribute) < 0) {
+ log_error("primary dm_snprintf %s failed", attribute);
+ return 0;
+ }
+ if (stat(path, &info) == -1) {
+ if (errno != ENOENT)
+ log_sys_error("stat", path);
+ return 0;
+ }
+ }
+
+ if (!(fp = fopen(path, "r"))) {
+ log_sys_error("fopen", path);
+ return 0;
+ }
+
+ if (!fgets(buffer, sizeof(buffer), fp)) {
+ log_sys_error("fgets", path);
+ goto out;
+ }
+
+ if (sscanf(buffer, "%lu", &result) != 1) {
+ log_error("sysfs file %s not in expected format: %s", path,
+ buffer);
+ goto out;
+ }
+
+ log_very_verbose("Device %s %s is %lu bytes.",
+ dev_name(dev), attribute, result);
+
+out:
+ if (fclose(fp))
+ log_sys_error("fclose", path);
+
+ return result >> SECTOR_SHIFT;
+}
+
+unsigned long dev_alignment_offset(const char *sysfs_dir,
+ struct device *dev)
+{
+ return _dev_topology_attribute("alignment_offset",
+ sysfs_dir, dev);
+}
+
+unsigned long dev_minimum_io_size(const char *sysfs_dir,
+ struct device *dev)
+{
+ return _dev_topology_attribute("queue/minimum_io_size",
+ sysfs_dir, dev);
+}
+
+unsigned long dev_optimal_io_size(const char *sysfs_dir,
+ struct device *dev)
+{
+ return _dev_topology_attribute("queue/optimal_io_size",
+ sysfs_dir, dev);
+}
+
+#else
+
+int get_primary_dev(const char *sysfs_dir,
+ struct device *dev, dev_t *result)
+{
+ return 0;
+}
+
+unsigned long dev_alignment_offset(const char *sysfs_dir,
+ struct device *dev)
+{
+ return 0UL;
+}
+
+unsigned long dev_minimum_io_size(const char *sysfs_dir,
+ struct device *dev)
+{
+ return 0UL;
+}
+
+unsigned long dev_optimal_io_size(const char *sysfs_dir,
+ struct device *dev)
+{
+ return 0UL;
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_DEVICE_H
+#define _LVM_DEVICE_H
+
+#include "uuid.h"
+
+#include <fcntl.h>
+
+#define DEV_ACCESSED_W 0x00000001 /* Device written to? */
+#define DEV_REGULAR 0x00000002 /* Regular file? */
+#define DEV_ALLOCED 0x00000004 /* dm_malloc used */
+#define DEV_OPENED_RW 0x00000008 /* Opened RW */
+#define DEV_OPENED_EXCL 0x00000010 /* Opened EXCL */
+#define DEV_O_DIRECT 0x00000020 /* Use O_DIRECT */
+#define DEV_O_DIRECT_TESTED 0x00000040 /* DEV_O_DIRECT is reliable */
+
+/*
+ * All devices in LVM will be represented by one of these.
+ * pointer comparisons are valid.
+ */
+struct device {
+ struct dm_list aliases; /* struct str_list from lvm-types.h */
+ dev_t dev;
+
+ /* private */
+ int fd;
+ int open_count;
+ int error_count;
+ int max_error_count;
+ int block_size;
+ int read_ahead;
+ uint32_t flags;
+ uint64_t end;
+ struct dm_list open_list;
+
+ char pvid[ID_LEN + 1];
+ char _padding[7];
+};
+
+struct device_list {
+ struct dm_list list;
+ struct device *dev;
+};
+
+struct device_area {
+ struct device *dev;
+ uint64_t start; /* Bytes */
+ uint64_t size; /* Bytes */
+};
+
+/*
+ * All io should use these routines.
+ */
+int dev_get_size(const struct device *dev, uint64_t *size);
+int dev_get_sectsize(struct device *dev, uint32_t *size);
+int dev_get_read_ahead(struct device *dev, uint32_t *read_ahead);
+
+/* Use quiet version if device number could change e.g. when opening LV */
+int dev_open(struct device *dev);
+int dev_open_quiet(struct device *dev);
+int dev_open_flags(struct device *dev, int flags, int direct, int quiet);
+int dev_close(struct device *dev);
+int dev_close_immediate(struct device *dev);
+void dev_close_all(void);
+int dev_test_excl(struct device *dev);
+
+int dev_fd(struct device *dev);
+const char *dev_name(const struct device *dev);
+
+int dev_read(struct device *dev, uint64_t offset, size_t len, void *buffer);
+int dev_read_circular(struct device *dev, uint64_t offset, size_t len,
+ uint64_t offset2, size_t len2, void *buf);
+int dev_write(struct device *dev, uint64_t offset, size_t len, void *buffer);
+int dev_append(struct device *dev, size_t len, void *buffer);
+int dev_set(struct device *dev, uint64_t offset, size_t len, int value);
+void dev_flush(struct device *dev);
+
+struct device *dev_create_file(const char *filename, struct device *dev,
+ struct str_list *alias, int use_malloc);
+
+/* Return a valid device name from the alias list; NULL otherwise */
+const char *dev_name_confirmed(struct device *dev, int quiet);
+
+/* Does device contain md superblock? If so, where? */
+int dev_is_md(struct device *dev, uint64_t *sb);
+int dev_is_swap(struct device *dev, uint64_t *signature);
+int dev_is_luks(struct device *dev, uint64_t *signature);
+unsigned long dev_md_stripe_width(const char *sysfs_dir, struct device *dev);
+
+int is_partitioned_dev(struct device *dev);
+
+int get_primary_dev(const char *sysfs_dir,
+ const struct device *dev, dev_t *result);
+
+unsigned long dev_alignment_offset(const char *sysfs_dir,
+ struct device *dev);
+
+unsigned long dev_minimum_io_size(const char *sysfs_dir,
+ struct device *dev);
+
+unsigned long dev_optimal_io_size(const char *sysfs_dir,
+ struct device *dev);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "display.h"
+#include "activate.h"
+#include "toolcontext.h"
+#include "segtype.h"
+
+#define SIZE_BUF 128
+
+typedef enum { SIZE_LONG = 0, SIZE_SHORT = 1, SIZE_UNIT = 2 } size_len_t;
+
+static const struct {
+ alloc_policy_t alloc;
+ const char str[14]; /* must be changed when size extends 13 chars */
+ const char repchar;
+} _policies[] = {
+ {
+ ALLOC_CONTIGUOUS, "contiguous", 'c'}, {
+ ALLOC_CLING, "cling", 'l'}, {
+ ALLOC_CLING_BY_TAGS, "cling_by_tags", 't'}, { /* Only used in log mesgs */
+ ALLOC_NORMAL, "normal", 'n'}, {
+ ALLOC_ANYWHERE, "anywhere", 'a'}, {
+ ALLOC_INHERIT, "inherit", 'i'}
+};
+
+static const int _num_policies = sizeof(_policies) / sizeof(*_policies);
+
+uint64_t units_to_bytes(const char *units, char *unit_type)
+{
+ char *ptr = NULL;
+ uint64_t v;
+
+ if (isdigit(*units)) {
+ v = (uint64_t) strtod(units, &ptr);
+ if (ptr == units)
+ return 0;
+ units = ptr;
+ } else
+ v = 1;
+
+ if (v == 1)
+ *unit_type = *units;
+ else
+ *unit_type = 'U';
+
+ switch (*units) {
+ case 'h':
+ case 'H':
+ v = UINT64_C(1);
+ *unit_type = *units;
+ break;
+ case 'b':
+ case 'B':
+ v *= UINT64_C(1);
+ break;
+#define KILO UINT64_C(1024)
+ case 's':
+ case 'S':
+ v *= (KILO/2);
+ break;
+ case 'k':
+ v *= KILO;
+ break;
+ case 'm':
+ v *= KILO * KILO;
+ break;
+ case 'g':
+ v *= KILO * KILO * KILO;
+ break;
+ case 't':
+ v *= KILO * KILO * KILO * KILO;
+ break;
+ case 'p':
+ v *= KILO * KILO * KILO * KILO * KILO;
+ break;
+ case 'e':
+ v *= KILO * KILO * KILO * KILO * KILO * KILO;
+ break;
+#undef KILO
+#define KILO UINT64_C(1000)
+ case 'K':
+ v *= KILO;
+ break;
+ case 'M':
+ v *= KILO * KILO;
+ break;
+ case 'G':
+ v *= KILO * KILO * KILO;
+ break;
+ case 'T':
+ v *= KILO * KILO * KILO * KILO;
+ break;
+ case 'P':
+ v *= KILO * KILO * KILO * KILO * KILO;
+ break;
+ case 'E':
+ v *= KILO * KILO * KILO * KILO * KILO * KILO;
+ break;
+#undef KILO
+ default:
+ return 0;
+ }
+
+ if (*(units + 1))
+ return 0;
+
+ return v;
+}
+
+const char alloc_policy_char(alloc_policy_t alloc)
+{
+ int i;
+
+ for (i = 0; i < _num_policies; i++)
+ if (_policies[i].alloc == alloc)
+ return _policies[i].repchar;
+
+ return '-';
+}
+
+const char *get_alloc_string(alloc_policy_t alloc)
+{
+ int i;
+
+ for (i = 0; i < _num_policies; i++)
+ if (_policies[i].alloc == alloc)
+ return _policies[i].str;
+
+ return NULL;
+}
+
+alloc_policy_t get_alloc_from_string(const char *str)
+{
+ int i;
+
+ /* cling_by_tags is part of cling */
+ if (!strcmp("cling_by_tags", str))
+ return ALLOC_CLING;
+
+ for (i = 0; i < _num_policies; i++)
+ if (!strcmp(_policies[i].str, str))
+ return _policies[i].alloc;
+
+ /* Special case for old metadata */
+ if (!strcmp("next free", str))
+ return ALLOC_NORMAL;
+
+ log_error("Unrecognised allocation policy %s", str);
+ return ALLOC_INVALID;
+}
+
+#define BASE_UNKNOWN 0
+#define BASE_SHARED 1
+#define BASE_1024 7
+#define BASE_1000 13
+#define BASE_SPECIAL 19
+#define NUM_UNIT_PREFIXES 6
+#define NUM_SPECIAL 3
+
+/* Size supplied in sectors */
+static const char *_display_size(const struct cmd_context *cmd,
+ uint64_t size, size_len_t sl)
+{
+ unsigned base = BASE_UNKNOWN;
+ unsigned s;
+ int suffix = 1, precision;
+ uint64_t byte = UINT64_C(0);
+ uint64_t units = UINT64_C(1024);
+ char *size_buf = NULL;
+ const char * const size_str[][3] = {
+ /* BASE_UNKNOWN */
+ {" ", " ", " "}, /* [0] */
+
+ /* BASE_SHARED - Used if cmd->si_unit_consistency = 0 */
+ {" Exabyte", " EB", "E"}, /* [1] */
+ {" Petabyte", " PB", "P"}, /* [2] */
+ {" Terabyte", " TB", "T"}, /* [3] */
+ {" Gigabyte", " GB", "G"}, /* [4] */
+ {" Megabyte", " MB", "M"}, /* [5] */
+ {" Kilobyte", " KB", "K"}, /* [6] */
+
+ /* BASE_1024 - Used if cmd->si_unit_consistency = 1 */
+ {" Exbibyte", " EiB", "e"}, /* [7] */
+ {" Pebibyte", " PiB", "p"}, /* [8] */
+ {" Tebibyte", " TiB", "t"}, /* [9] */
+ {" Gibibyte", " GiB", "g"}, /* [10] */
+ {" Mebibyte", " MiB", "m"}, /* [11] */
+ {" Kibibyte", " KiB", "k"}, /* [12] */
+
+ /* BASE_1000 - Used if cmd->si_unit_consistency = 1 */
+ {" Exabyte", " EB", "E"}, /* [13] */
+ {" Petabyte", " PB", "P"}, /* [14] */
+ {" Terabyte", " TB", "T"}, /* [15] */
+ {" Gigabyte", " GB", "G"}, /* [16] */
+ {" Megabyte", " MB", "M"}, /* [17] */
+ {" Kilobyte", " kB", "K"}, /* [18] */
+
+ /* BASE_SPECIAL */
+ {" Byte ", " B ", "B"}, /* [19] */
+ {" Units ", " Un", "U"}, /* [20] */
+ {" Sectors ", " Se", "S"}, /* [21] */
+ };
+
+ if (!(size_buf = dm_pool_alloc(cmd->mem, SIZE_BUF))) {
+ log_error("no memory for size display buffer");
+ return "";
+ }
+
+ suffix = cmd->current_settings.suffix;
+
+ if (!cmd->si_unit_consistency) {
+ /* Case-independent match */
+ for (s = 0; s < NUM_UNIT_PREFIXES; s++)
+ if (toupper((int) cmd->current_settings.unit_type) ==
+ *size_str[BASE_SHARED + s][2]) {
+ base = BASE_SHARED;
+ break;
+ }
+ } else {
+ /* Case-dependent match for powers of 1000 */
+ for (s = 0; s < NUM_UNIT_PREFIXES; s++)
+ if (cmd->current_settings.unit_type ==
+ *size_str[BASE_1000 + s][2]) {
+ base = BASE_1000;
+ break;
+ }
+
+ /* Case-dependent match for powers of 1024 */
+ if (base == BASE_UNKNOWN)
+ for (s = 0; s < NUM_UNIT_PREFIXES; s++)
+ if (cmd->current_settings.unit_type ==
+ *size_str[BASE_1024 + s][2]) {
+ base = BASE_1024;
+ break;
+ }
+ }
+
+ if (base == BASE_UNKNOWN)
+ /* Check for special units - s, b or u */
+ for (s = 0; s < NUM_SPECIAL; s++)
+ if (toupper((int) cmd->current_settings.unit_type) ==
+ *size_str[BASE_SPECIAL + s][2]) {
+ base = BASE_SPECIAL;
+ break;
+ }
+
+ if (size == UINT64_C(0)) {
+ if (base == BASE_UNKNOWN)
+ s = 0;
+ sprintf(size_buf, "0%s", suffix ? size_str[base + s][sl] : "");
+ return size_buf;
+ }
+
+ size *= UINT64_C(512);
+
+ if (base != BASE_UNKNOWN)
+ byte = cmd->current_settings.unit_factor;
+ else {
+ /* Human-readable style */
+ if (cmd->current_settings.unit_type == 'H') {
+ units = UINT64_C(1000);
+ base = BASE_1000;
+ } else {
+ units = UINT64_C(1024);
+ base = BASE_1024;
+ }
+
+ if (!cmd->si_unit_consistency)
+ base = BASE_SHARED;
+
+ byte = units * units * units * units * units * units;
+
+ for (s = 0; s < NUM_UNIT_PREFIXES && size < byte; s++)
+ byte /= units;
+
+ suffix = 1;
+ }
+
+ /* FIXME Make precision configurable */
+ switch(toupper((int) cmd->current_settings.unit_type)) {
+ case 'B':
+ case 'S':
+ precision = 0;
+ break;
+ default:
+ precision = 2;
+ }
+
+ snprintf(size_buf, SIZE_BUF - 1, "%.*f%s", precision,
+ (double) size / byte, suffix ? size_str[base + s][sl] : "");
+
+ return size_buf;
+}
+
+const char *display_size_long(const struct cmd_context *cmd, uint64_t size)
+{
+ return _display_size(cmd, size, SIZE_LONG);
+}
+
+const char *display_size_units(const struct cmd_context *cmd, uint64_t size)
+{
+ return _display_size(cmd, size, SIZE_UNIT);
+}
+
+const char *display_size(const struct cmd_context *cmd, uint64_t size)
+{
+ return _display_size(cmd, size, SIZE_SHORT);
+}
+
+void pvdisplay_colons(const struct physical_volume *pv)
+{
+ char uuid[64] __attribute__((aligned(8)));
+
+ if (!pv)
+ return;
+
+ if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
+ stack;
+ return;
+ }
+
+ log_print("%s:%s:%" PRIu64 ":-1:%" PRIu64 ":%" PRIu64 ":-1:%" PRIu32 ":%u:%u:%u:%s",
+ pv_dev_name(pv), pv->vg_name, pv->size,
+ /* FIXME pv->pv_number, Derive or remove? */
+ pv->status, /* FIXME Support old or new format here? */
+ pv->status & ALLOCATABLE_PV, /* FIXME remove? */
+ /* FIXME pv->lv_cur, Remove? */
+ pv->pe_size / 2,
+ pv->pe_count,
+ pv->pe_count - pv->pe_alloc_count,
+ pv->pe_alloc_count, *uuid ? uuid : "none");
+}
+
+void pvdisplay_segments(const struct physical_volume *pv)
+{
+ const struct pv_segment *pvseg;
+
+ if (pv->pe_size)
+ log_print("--- Physical Segments ---");
+
+ dm_list_iterate_items(pvseg, &pv->segments) {
+ log_print("Physical extent %u to %u:",
+ pvseg->pe, pvseg->pe + pvseg->len - 1);
+
+ if (pvseg_is_allocated(pvseg)) {
+ log_print(" Logical volume\t%s%s/%s",
+ pvseg->lvseg->lv->vg->cmd->dev_dir,
+ pvseg->lvseg->lv->vg->name,
+ pvseg->lvseg->lv->name);
+ log_print(" Logical extents\t%d to %d",
+ pvseg->lvseg->le, pvseg->lvseg->le +
+ pvseg->lvseg->len - 1);
+ } else
+ log_print(" FREE");
+ }
+
+ log_print(" ");
+}
+
+/* FIXME Include label fields */
+void pvdisplay_full(const struct cmd_context *cmd,
+ const struct physical_volume *pv,
+ void *handle __attribute__((unused)))
+{
+ char uuid[64] __attribute__((aligned(8)));
+ const char *size;
+
+ uint32_t pe_free;
+ uint64_t data_size, pvsize, unusable;
+
+ if (!pv)
+ return;
+
+ if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
+ stack;
+ return;
+ }
+
+ log_print("--- %sPhysical volume ---", pv->pe_size ? "" : "NEW ");
+ log_print("PV Name %s", pv_dev_name(pv));
+ log_print("VG Name %s%s",
+ is_orphan(pv) ? "" : pv->vg_name,
+ pv->status & EXPORTED_VG ? " (exported)" : "");
+
+ data_size = (uint64_t) pv->pe_count * pv->pe_size;
+ if (pv->size > data_size + pv->pe_start) {
+ pvsize = pv->size;
+ unusable = pvsize - data_size;
+ } else {
+ pvsize = data_size + pv->pe_start;
+ unusable = pvsize - pv->size;
+ }
+
+ size = display_size(cmd, pvsize);
+ if (data_size)
+ log_print("PV Size %s / not usable %s", /* [LVM: %s]", */
+ size, display_size(cmd, unusable));
+ else
+ log_print("PV Size %s", size);
+
+ /* PV number not part of LVM2 design
+ log_print("PV# %u", pv->pv_number);
+ */
+
+ pe_free = pv->pe_count - pv->pe_alloc_count;
+ if (pv->pe_count && (pv->status & ALLOCATABLE_PV))
+ log_print("Allocatable yes %s",
+ (!pe_free && pv->pe_count) ? "(but full)" : "");
+ else
+ log_print("Allocatable NO");
+
+ /* LV count is no longer available when displaying PV
+ log_print("Cur LV %u", vg->lv_count);
+ */
+
+ if (cmd->si_unit_consistency)
+ log_print("PE Size %s", display_size(cmd, (uint64_t) pv->pe_size));
+ else
+ log_print("PE Size (KByte) %" PRIu32, pv->pe_size / 2);
+
+ log_print("Total PE %u", pv->pe_count);
+ log_print("Free PE %" PRIu32, pe_free);
+ log_print("Allocated PE %u", pv->pe_alloc_count);
+ log_print("PV UUID %s", *uuid ? uuid : "none");
+ log_print(" ");
+}
+
+int pvdisplay_short(const struct cmd_context *cmd __attribute__((unused)),
+ const struct volume_group *vg __attribute__((unused)),
+ const struct physical_volume *pv,
+ void *handle __attribute__((unused)))
+{
+ char uuid[64] __attribute__((aligned(8)));
+
+ if (!pv)
+ return 0;
+
+ if (!id_write_format(&pv->id, uuid, sizeof(uuid)))
+ return_0;
+
+ log_print("PV Name %s ", pv_dev_name(pv));
+ /* FIXME pv->pv_number); */
+ log_print("PV UUID %s", *uuid ? uuid : "none");
+ log_print("PV Status %sallocatable",
+ (pv->status & ALLOCATABLE_PV) ? "" : "NOT ");
+ log_print("Total PE / Free PE %u / %u",
+ pv->pe_count, pv->pe_count - pv->pe_alloc_count);
+
+ log_print(" ");
+ return 0;
+}
+
+void lvdisplay_colons(const struct logical_volume *lv)
+{
+ int inkernel;
+ struct lvinfo info;
+ inkernel = lv_info(lv->vg->cmd, lv, 0, &info, 1, 0) && info.exists;
+
+ log_print("%s%s/%s:%s:%" PRIu64 ":%d:-1:%d:%" PRIu64 ":%d:-1:%d:%d:%d:%d",
+ lv->vg->cmd->dev_dir,
+ lv->vg->name,
+ lv->name,
+ lv->vg->name,
+ (lv->status & (LVM_READ | LVM_WRITE)) >> 8, inkernel ? 1 : 0,
+ /* FIXME lv->lv_number, */
+ inkernel ? info.open_count : 0, lv->size, lv->le_count,
+ /* FIXME Add num allocated to struct! lv->lv_allocated_le, */
+ (lv->alloc == ALLOC_CONTIGUOUS ? 2 : 0), lv->read_ahead,
+ inkernel ? info.major : -1, inkernel ? info.minor : -1);
+}
+
+int lvdisplay_full(struct cmd_context *cmd,
+ const struct logical_volume *lv,
+ void *handle __attribute__((unused)))
+{
+ struct lvinfo info;
+ int inkernel, snap_active = 0;
+ char uuid[64] __attribute__((aligned(8)));
+ struct lv_segment *snap_seg = NULL, *mirror_seg = NULL;
+ percent_t snap_percent;
+
+ if (!id_write_format(&lv->lvid.id[1], uuid, sizeof(uuid)))
+ return_0;
+
+ inkernel = lv_info(cmd, lv, 0, &info, 1, 1) && info.exists;
+
+ log_print("--- Logical volume ---");
+
+ log_print("LV Name %s%s/%s", lv->vg->cmd->dev_dir,
+ lv->vg->name, lv->name);
+ log_print("VG Name %s", lv->vg->name);
+
+ log_print("LV UUID %s", uuid);
+
+ log_print("LV Write Access %s",
+ (lv->status & LVM_WRITE) ? "read/write" : "read only");
+
+ if (lv_is_origin(lv)) {
+ log_print("LV snapshot status source of");
+
+ dm_list_iterate_items_gen(snap_seg, &lv->snapshot_segs,
+ origin_list) {
+ if (inkernel &&
+ (snap_active = lv_snapshot_percent(snap_seg->cow,
+ &snap_percent)))
+ if (snap_percent == PERCENT_INVALID)
+ snap_active = 0;
+ log_print(" %s%s/%s [%s]",
+ lv->vg->cmd->dev_dir, lv->vg->name,
+ snap_seg->cow->name,
+ snap_active ? "active" : "INACTIVE");
+ }
+ snap_seg = NULL;
+ } else if ((snap_seg = find_cow(lv))) {
+ if (inkernel &&
+ (snap_active = lv_snapshot_percent(snap_seg->cow,
+ &snap_percent)))
+ if (snap_percent == PERCENT_INVALID)
+ snap_active = 0;
+
+ log_print("LV snapshot status %s destination for %s%s/%s",
+ snap_active ? "active" : "INACTIVE",
+ lv->vg->cmd->dev_dir, lv->vg->name,
+ snap_seg->origin->name);
+ }
+
+ if (inkernel && info.suspended)
+ log_print("LV Status suspended");
+ else
+ log_print("LV Status %savailable",
+ inkernel ? "" : "NOT ");
+
+/********* FIXME lv_number
+ log_print("LV # %u", lv->lv_number + 1);
+************/
+
+ if (inkernel)
+ log_print("# open %u", info.open_count);
+
+ log_print("LV Size %s",
+ display_size(cmd,
+ snap_seg ? snap_seg->origin->size : lv->size));
+
+ log_print("Current LE %u",
+ snap_seg ? snap_seg->origin->le_count : lv->le_count);
+
+ if (snap_seg) {
+ log_print("COW-table size %s",
+ display_size(cmd, (uint64_t) lv->size));
+ log_print("COW-table LE %u", lv->le_count);
+
+ if (snap_active)
+ log_print("Allocated to snapshot %.2f%% ",
+ percent_to_float(snap_percent));
+
+ log_print("Snapshot chunk size %s",
+ display_size(cmd, (uint64_t) snap_seg->chunk_size));
+ }
+
+ if (lv->status & MIRRORED) {
+ mirror_seg = first_seg(lv);
+ log_print("Mirrored volumes %" PRIu32, mirror_seg->area_count);
+ if (lv->status & CONVERTING)
+ log_print("LV type Mirror undergoing conversion");
+ }
+
+ log_print("Segments %u", dm_list_size(&lv->segments));
+
+/********* FIXME Stripes & stripesize for each segment
+ log_print("Stripe size %s", display_size(cmd, (uint64_t) lv->stripesize));
+***********/
+
+ log_print("Allocation %s", get_alloc_string(lv->alloc));
+ if (lv->read_ahead == DM_READ_AHEAD_AUTO)
+ log_print("Read ahead sectors auto");
+ else if (lv->read_ahead == DM_READ_AHEAD_NONE)
+ log_print("Read ahead sectors 0");
+ else
+ log_print("Read ahead sectors %u", lv->read_ahead);
+
+ if (inkernel && lv->read_ahead != info.read_ahead)
+ log_print("- currently set to %u", info.read_ahead);
+
+ if (lv->status & FIXED_MINOR) {
+ if (lv->major >= 0)
+ log_print("Persistent major %d", lv->major);
+ log_print("Persistent minor %d", lv->minor);
+ }
+
+ if (inkernel)
+ log_print("Block device %d:%d", info.major,
+ info.minor);
+
+ log_print(" ");
+
+ return 0;
+}
+
+void display_stripe(const struct lv_segment *seg, uint32_t s, const char *pre)
+{
+ switch (seg_type(seg, s)) {
+ case AREA_PV:
+ /* FIXME Re-check the conditions for 'Missing' */
+ log_print("%sPhysical volume\t%s", pre,
+ seg_pv(seg, s) ?
+ pv_dev_name(seg_pv(seg, s)) :
+ "Missing");
+
+ if (seg_pv(seg, s))
+ log_print("%sPhysical extents\t%d to %d", pre,
+ seg_pe(seg, s),
+ seg_pe(seg, s) + seg->area_len - 1);
+ break;
+ case AREA_LV:
+ log_print("%sLogical volume\t%s", pre,
+ seg_lv(seg, s) ?
+ seg_lv(seg, s)->name : "Missing");
+
+ if (seg_lv(seg, s))
+ log_print("%sLogical extents\t%d to %d", pre,
+ seg_le(seg, s),
+ seg_le(seg, s) + seg->area_len - 1);
+ break;
+ case AREA_UNASSIGNED:
+ log_print("%sUnassigned area", pre);
+ }
+}
+
+int lvdisplay_segments(const struct logical_volume *lv)
+{
+ const struct lv_segment *seg;
+
+ log_print("--- Segments ---");
+
+ dm_list_iterate_items(seg, &lv->segments) {
+ log_print("Logical extent %u to %u:",
+ seg->le, seg->le + seg->len - 1);
+
+ log_print(" Type\t\t%s", seg->segtype->ops->name(seg));
+
+ if (seg->segtype->ops->display)
+ seg->segtype->ops->display(seg);
+ }
+
+ log_print(" ");
+ return 1;
+}
+
+void vgdisplay_extents(const struct volume_group *vg __attribute__((unused)))
+{
+}
+
+void vgdisplay_full(const struct volume_group *vg)
+{
+ uint32_t access_str;
+ uint32_t active_pvs;
+ char uuid[64] __attribute__((aligned(8)));
+
+ active_pvs = vg->pv_count - vg_missing_pv_count(vg);
+
+ log_print("--- Volume group ---");
+ log_print("VG Name %s", vg->name);
+ log_print("System ID %s", vg->system_id);
+ log_print("Format %s", vg->fid->fmt->name);
+ if (vg->fid->fmt->features & FMT_MDAS) {
+ log_print("Metadata Areas %d",
+ vg_mda_count(vg));
+ log_print("Metadata Sequence No %d", vg->seqno);
+ }
+ access_str = vg->status & (LVM_READ | LVM_WRITE);
+ log_print("VG Access %s%s%s%s",
+ access_str == (LVM_READ | LVM_WRITE) ? "read/write" : "",
+ access_str == LVM_READ ? "read" : "",
+ access_str == LVM_WRITE ? "write" : "",
+ access_str == 0 ? "error" : "");
+ log_print("VG Status %s%sresizable",
+ vg_is_exported(vg) ? "exported/" : "",
+ vg_is_resizeable(vg) ? "" : "NOT ");
+ /* vg number not part of LVM2 design
+ log_print ("VG # %u\n", vg->vg_number);
+ */
+ if (vg_is_clustered(vg)) {
+ log_print("Clustered yes");
+ log_print("Shared %s",
+ vg->status & SHARED ? "yes" : "no");
+ }
+
+ log_print("MAX LV %u", vg->max_lv);
+ log_print("Cur LV %u", vg_visible_lvs(vg));
+ log_print("Open LV %u", lvs_in_vg_opened(vg));
+/****** FIXME Max LV Size
+ log_print ( "MAX LV Size %s",
+ ( s1 = display_size ( LVM_LV_SIZE_MAX(vg))));
+ free ( s1);
+*********/
+ log_print("Max PV %u", vg->max_pv);
+ log_print("Cur PV %u", vg->pv_count);
+ log_print("Act PV %u", active_pvs);
+
+ log_print("VG Size %s",
+ display_size(vg->cmd,
+ (uint64_t) vg->extent_count * vg->extent_size));
+
+ log_print("PE Size %s",
+ display_size(vg->cmd, (uint64_t) vg->extent_size));
+
+ log_print("Total PE %u", vg->extent_count);
+
+ log_print("Alloc PE / Size %u / %s",
+ vg->extent_count - vg->free_count,
+ display_size(vg->cmd,
+ ((uint64_t) vg->extent_count - vg->free_count) *
+ vg->extent_size));
+
+ log_print("Free PE / Size %u / %s", vg->free_count,
+ display_size(vg->cmd, vg_free(vg)));
+
+ if (!id_write_format(&vg->id, uuid, sizeof(uuid))) {
+ stack;
+ return;
+ }
+
+ log_print("VG UUID %s", uuid);
+ log_print(" ");
+}
+
+void vgdisplay_colons(const struct volume_group *vg)
+{
+ uint32_t active_pvs;
+ const char *access_str;
+ char uuid[64] __attribute__((aligned(8)));
+
+ active_pvs = vg->pv_count - vg_missing_pv_count(vg);
+
+ switch (vg->status & (LVM_READ | LVM_WRITE)) {
+ case LVM_READ | LVM_WRITE:
+ access_str = "r/w";
+ break;
+ case LVM_READ:
+ access_str = "r";
+ break;
+ case LVM_WRITE:
+ access_str = "w";
+ break;
+ default:
+ access_str = "";
+ }
+
+ if (!id_write_format(&vg->id, uuid, sizeof(uuid))) {
+ stack;
+ return;
+ }
+
+ log_print("%s:%s:%" PRIu64 ":-1:%u:%u:%u:-1:%u:%u:%u:%" PRIu64 ":%" PRIu32
+ ":%u:%u:%u:%s",
+ vg->name,
+ access_str,
+ vg->status,
+ /* internal volume group number; obsolete */
+ vg->max_lv,
+ vg_visible_lvs(vg),
+ lvs_in_vg_opened(vg),
+ /* FIXME: maximum logical volume size */
+ vg->max_pv,
+ vg->pv_count,
+ active_pvs,
+ (uint64_t) vg->extent_count * (vg->extent_size / 2),
+ vg->extent_size / 2,
+ vg->extent_count,
+ vg->extent_count - vg->free_count,
+ vg->free_count,
+ uuid[0] ? uuid : "none");
+}
+
+void vgdisplay_short(const struct volume_group *vg)
+{
+ log_print("\"%s\" %-9s [%-9s used / %s free]", vg->name,
+/********* FIXME if "open" print "/used" else print "/idle"??? ******/
+ display_size(vg->cmd,
+ (uint64_t) vg->extent_count * vg->extent_size),
+ display_size(vg->cmd,
+ ((uint64_t) vg->extent_count -
+ vg->free_count) * vg->extent_size),
+ display_size(vg->cmd, vg_free(vg)));
+}
+
+void display_formats(const struct cmd_context *cmd)
+{
+ const struct format_type *fmt;
+
+ dm_list_iterate_items(fmt, &cmd->formats) {
+ log_print("%s", fmt->name);
+ }
+}
+
+void display_segtypes(const struct cmd_context *cmd)
+{
+ const struct segment_type *segtype;
+
+ dm_list_iterate_items(segtype, &cmd->segtypes) {
+ log_print("%s", segtype->name);
+ }
+}
+
+char yes_no_prompt(const char *prompt, ...)
+{
+ int c = 0, ret = 0;
+ va_list ap;
+
+ sigint_allow();
+ do {
+ if (c == '\n' || !c) {
+ va_start(ap, prompt);
+ vprintf(prompt, ap);
+ va_end(ap);
+ fflush(stdout);
+ }
+
+ if ((c = getchar()) == EOF) {
+ ret = 'n';
+ break;
+ }
+
+ c = tolower(c);
+ if ((c == 'y') || (c == 'n'))
+ ret = c;
+ } while (!ret || c != '\n');
+
+ sigint_restore();
+
+ if (c != '\n')
+ printf("\n");
+
+ return ret;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_DISPLAY_H
+#define _LVM_DISPLAY_H
+
+#include "metadata-exported.h"
+#include "locking.h"
+
+#include <stdint.h>
+
+uint64_t units_to_bytes(const char *units, char *unit_type);
+
+/* Specify size in KB */
+const char *display_size(const struct cmd_context *cmd, uint64_t size);
+const char *display_size_long(const struct cmd_context *cmd, uint64_t size);
+const char *display_size_units(const struct cmd_context *cmd, uint64_t size);
+
+char *display_uuid(char *uuidstr);
+void display_stripe(const struct lv_segment *seg, uint32_t s, const char *pre);
+
+void pvdisplay_colons(const struct physical_volume *pv);
+void pvdisplay_segments(const struct physical_volume *pv);
+void pvdisplay_full(const struct cmd_context *cmd,
+ const struct physical_volume *pv,
+ void *handle);
+int pvdisplay_short(const struct cmd_context *cmd,
+ const struct volume_group *vg,
+ const struct physical_volume *pv, void *handle);
+
+void lvdisplay_colons(const struct logical_volume *lv);
+int lvdisplay_segments(const struct logical_volume *lv);
+int lvdisplay_full(struct cmd_context *cmd, const struct logical_volume *lv,
+ void *handle);
+
+void vgdisplay_extents(const struct volume_group *vg);
+void vgdisplay_full(const struct volume_group *vg);
+void vgdisplay_colons(const struct volume_group *vg);
+void vgdisplay_short(const struct volume_group *vg);
+
+void display_formats(const struct cmd_context *cmd);
+void display_segtypes(const struct cmd_context *cmd);
+
+/*
+ * Allocation policy display conversion routines.
+ */
+const char *get_alloc_string(alloc_policy_t alloc);
+const char alloc_policy_char(alloc_policy_t alloc);
+alloc_policy_t get_alloc_from_string(const char *str);
+
+char yes_no_prompt(const char *prompt, ...) __attribute__ ((format(printf, 1, 2)));
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "display.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "str_list.h"
+#include "targets.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "str_list.h"
+#include "metadata.h"
+
+static const char *_errseg_name(const struct lv_segment *seg)
+{
+ return seg->segtype->name;
+}
+
+static int _errseg_merge_segments(struct lv_segment *seg1, struct lv_segment *seg2)
+{
+ seg1->len += seg2->len;
+ seg1->area_len += seg2->area_len;
+
+ return 1;
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static int _errseg_add_target_line(struct dev_manager *dm __attribute__((unused)),
+ struct dm_pool *mem __attribute__((unused)),
+ struct cmd_context *cmd __attribute__((unused)),
+ void **target_state __attribute__((unused)),
+ struct lv_segment *seg __attribute__((unused)),
+ struct dm_tree_node *node, uint64_t len,
+ uint32_t *pvmove_mirror_count __attribute__((unused)))
+{
+ return dm_tree_node_add_error_target(node, len);
+}
+
+static int _errseg_target_present(struct cmd_context *cmd,
+ const struct lv_segment *seg __attribute__((unused)),
+ unsigned *attributes __attribute__((unused)))
+{
+ static int _errseg_checked = 0;
+ static int _errseg_present = 0;
+
+ /* Reported truncated in older kernels */
+ if (!_errseg_checked &&
+ (target_present(cmd, "error", 0) ||
+ target_present(cmd, "erro", 0)))
+ _errseg_present = 1;
+
+ _errseg_checked = 1;
+ return _errseg_present;
+}
+#endif
+
+static int _errseg_modules_needed(struct dm_pool *mem,
+ const struct lv_segment *seg __attribute__((unused)),
+ struct dm_list *modules)
+{
+ if (!str_list_add(mem, modules, "error")) {
+ log_error("error module string list allocation failed");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void _errseg_destroy(struct segment_type *segtype)
+{
+ dm_free(segtype);
+}
+
+static struct segtype_handler _error_ops = {
+ .name = _errseg_name,
+ .merge_segments = _errseg_merge_segments,
+#ifdef DEVMAPPER_SUPPORT
+ .add_target_line = _errseg_add_target_line,
+ .target_present = _errseg_target_present,
+#endif
+ .modules_needed = _errseg_modules_needed,
+ .destroy = _errseg_destroy,
+};
+
+struct segment_type *init_error_segtype(struct cmd_context *cmd)
+{
+ struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+ if (!segtype)
+ return_NULL;
+
+ segtype->cmd = cmd;
+ segtype->ops = &_error_ops;
+ segtype->name = "error";
+ segtype->private = NULL;
+ segtype->flags = SEG_CAN_SPLIT | SEG_VIRTUAL | SEG_CANNOT_BE_ZEROED;
+
+ log_very_verbose("Initialised segtype: %s", segtype->name);
+
+ return segtype;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "filter-composite.h"
+
+#include <stdarg.h>
+
+static int _and_p(struct dev_filter *f, struct device *dev)
+{
+ struct dev_filter **filters = (struct dev_filter **) f->private;
+
+ while (*filters) {
+ if (!(*filters)->passes_filter(*filters, dev))
+ return 0;
+ filters++;
+ }
+
+ log_debug("Using %s", dev_name(dev));
+
+ return 1;
+}
+
+static void _composite_destroy(struct dev_filter *f)
+{
+ struct dev_filter **filters = (struct dev_filter **) f->private;
+
+ if (f->use_count)
+ log_error(INTERNAL_ERROR "Destroying composite filter while in use %u times.", f->use_count);
+
+ while (*filters) {
+ (*filters)->destroy(*filters);
+ filters++;
+ }
+
+ dm_free(f->private);
+ dm_free(f);
+}
+
+struct dev_filter *composite_filter_create(int n, struct dev_filter **filters)
+{
+ struct dev_filter **filters_copy, *cft;
+
+ if (!filters)
+ return_NULL;
+
+ if (!(filters_copy = dm_malloc(sizeof(*filters) * (n + 1)))) {
+ log_error("composite filters allocation failed");
+ return NULL;
+ }
+
+ memcpy(filters_copy, filters, sizeof(*filters) * n);
+ filters_copy[n] = NULL;
+
+ if (!(cft = dm_malloc(sizeof(*cft)))) {
+ log_error("compsoite filters allocation failed");
+ dm_free(filters_copy);
+ return NULL;
+ }
+
+ cft->passes_filter = _and_p;
+ cft->destroy = _composite_destroy;
+ cft->use_count = 0;
+ cft->private = filters_copy;
+
+ return cft;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_FILTER_COMPOSITE_H
+#define _LVM_FILTER_COMPOSITE_H
+
+#include "dev-cache.h"
+
+struct dev_filter *composite_filter_create(int n, struct dev_filter **filters);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2004 Luca Berra
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "filter-md.h"
+#include "metadata.h"
+
+#ifdef linux
+
+static int _ignore_md(struct dev_filter *f __attribute__((unused)),
+ struct device *dev)
+{
+ int ret;
+
+ if (!md_filtering())
+ return 1;
+
+ ret = dev_is_md(dev, NULL);
+
+ if (ret == 1) {
+ log_debug("%s: Skipping md component device", dev_name(dev));
+ return 0;
+ }
+
+ if (ret < 0) {
+ log_debug("%s: Skipping: error in md component detection",
+ dev_name(dev));
+ return 0;
+ }
+
+ return 1;
+}
+
+static void _destroy(struct dev_filter *f)
+{
+ if (f->use_count)
+ log_error(INTERNAL_ERROR "Destroying md filter while in use %u times.", f->use_count);
+
+ dm_free(f);
+}
+
+struct dev_filter *md_filter_create(void)
+{
+ struct dev_filter *f;
+
+ if (!(f = dm_malloc(sizeof(*f)))) {
+ log_error("md filter allocation failed");
+ return NULL;
+ }
+
+ f->passes_filter = _ignore_md;
+ f->destroy = _destroy;
+ f->use_count = 0;
+ f->private = NULL;
+
+ return f;
+}
+
+#else
+
+struct dev_filter *md_filter_create(void)
+{
+ return NULL;
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2004 Luca Berra
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_FILTER_MD_H
+#define _LVM_FILTER_MD_H
+
+#include "dev-cache.h"
+
+struct dev_filter *md_filter_create(void);
+
+#endif
+
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "config.h"
+#include "dev-cache.h"
+#include "filter.h"
+#include "filter-persistent.h"
+#include "lvm-file.h"
+#include "lvm-string.h"
+#include "activate.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+struct pfilter {
+ char *file;
+ struct dm_hash_table *devices;
+ struct dev_filter *real;
+ time_t ctime;
+};
+
+/*
+ * The hash table holds one of these two states
+ * against each entry.
+ */
+#define PF_BAD_DEVICE ((void *) 1)
+#define PF_GOOD_DEVICE ((void *) 2)
+
+static int _init_hash(struct pfilter *pf)
+{
+ if (pf->devices)
+ dm_hash_destroy(pf->devices);
+
+ if (!(pf->devices = dm_hash_create(128)))
+ return_0;
+
+ return 1;
+}
+
+int persistent_filter_wipe(struct dev_filter *f)
+{
+ struct pfilter *pf = (struct pfilter *) f->private;
+
+ log_verbose("Wiping cache of LVM-capable devices");
+ dm_hash_wipe(pf->devices);
+
+ /* Trigger complete device scan */
+ dev_cache_scan(1);
+
+ return 1;
+}
+
+static int _read_array(struct pfilter *pf, struct config_tree *cft,
+ const char *path, void *data)
+{
+ const struct config_node *cn;
+ const struct config_value *cv;
+
+ if (!(cn = find_config_node(cft->root, path))) {
+ log_very_verbose("Couldn't find %s array in '%s'",
+ path, pf->file);
+ return 0;
+ }
+
+ /*
+ * iterate through the array, adding
+ * devices as we go.
+ */
+ for (cv = cn->v; cv; cv = cv->next) {
+ if (cv->type != CFG_STRING) {
+ log_verbose("Devices array contains a value "
+ "which is not a string ... ignoring");
+ continue;
+ }
+
+ if (!dm_hash_insert(pf->devices, cv->v.str, data))
+ log_verbose("Couldn't add '%s' to filter ... ignoring",
+ cv->v.str);
+ /* Populate dev_cache ourselves */
+ dev_cache_get(cv->v.str, NULL);
+ }
+ return 1;
+}
+
+int persistent_filter_load(struct dev_filter *f, struct config_tree **cft_out)
+{
+ struct pfilter *pf = (struct pfilter *) f->private;
+ struct config_tree *cft;
+ struct stat info;
+ int r = 0;
+
+ if (!stat(pf->file, &info))
+ pf->ctime = info.st_ctime;
+ else {
+ log_very_verbose("%s: stat failed: %s", pf->file,
+ strerror(errno));
+ return_0;
+ }
+
+ if (!(cft = create_config_tree(pf->file, 1)))
+ return_0;
+
+ if (!read_config_file(cft))
+ goto_out;
+
+ _read_array(pf, cft, "persistent_filter_cache/valid_devices",
+ PF_GOOD_DEVICE);
+ /* We don't gain anything by holding invalid devices */
+ /* _read_array(pf, cft, "persistent_filter_cache/invalid_devices",
+ PF_BAD_DEVICE); */
+
+ /* Did we find anything? */
+ if (dm_hash_get_num_entries(pf->devices)) {
+ /* We populated dev_cache ourselves */
+ dev_cache_scan(0);
+ r = 1;
+ }
+
+ log_very_verbose("Loaded persistent filter cache from %s", pf->file);
+
+ out:
+ if (r && cft_out)
+ *cft_out = cft;
+ else
+ destroy_config_tree(cft);
+ return r;
+}
+
+static void _write_array(struct pfilter *pf, FILE *fp, const char *path,
+ void *data)
+{
+ void *d;
+ int first = 1;
+ char buf[2 * PATH_MAX];
+ struct dm_hash_node *n;
+
+ for (n = dm_hash_get_first(pf->devices); n;
+ n = dm_hash_get_next(pf->devices, n)) {
+ d = dm_hash_get_data(pf->devices, n);
+
+ if (d != data)
+ continue;
+
+ if (!first)
+ fprintf(fp, ",\n");
+ else {
+ fprintf(fp, "\t%s=[\n", path);
+ first = 0;
+ }
+
+ escape_double_quotes(buf, dm_hash_get_key(pf->devices, n));
+ fprintf(fp, "\t\t\"%s\"", buf);
+ }
+
+ if (!first)
+ fprintf(fp, "\n\t]\n");
+}
+
+int persistent_filter_dump(struct dev_filter *f, int merge_existing)
+{
+ struct pfilter *pf;
+ char *tmp_file;
+ struct stat info, info2;
+ struct config_tree *cft = NULL;
+ FILE *fp;
+ int lockfd;
+ int r = 0;
+
+ if (!f)
+ return_0;
+ pf = (struct pfilter *) f->private;
+
+ if (!dm_hash_get_num_entries(pf->devices)) {
+ log_very_verbose("Internal persistent device cache empty "
+ "- not writing to %s", pf->file);
+ return 0;
+ }
+ if (!dev_cache_has_scanned()) {
+ log_very_verbose("Device cache incomplete - not writing "
+ "to %s", pf->file);
+ return 0;
+ }
+
+ log_very_verbose("Dumping persistent device cache to %s", pf->file);
+
+ while (1) {
+ if ((lockfd = fcntl_lock_file(pf->file, F_WRLCK, 0)) < 0)
+ return_0;
+
+ /*
+ * Ensure we locked the file we expected
+ */
+ if (fstat(lockfd, &info)) {
+ log_sys_error("fstat", pf->file);
+ goto out;
+ }
+ if (stat(pf->file, &info2)) {
+ log_sys_error("stat", pf->file);
+ goto out;
+ }
+
+ if (is_same_inode(info, info2))
+ break;
+
+ fcntl_unlock_file(lockfd);
+ }
+
+ /*
+ * If file contents changed since we loaded it, merge new contents
+ */
+ if (merge_existing && info.st_ctime != pf->ctime)
+ /* Keep cft open to avoid losing lock */
+ persistent_filter_load(f, &cft);
+
+ tmp_file = alloca(strlen(pf->file) + 5);
+ sprintf(tmp_file, "%s.tmp", pf->file);
+
+ if (!(fp = fopen(tmp_file, "w"))) {
+ /* EACCES has been reported over NFS */
+ if (errno != EROFS && errno != EACCES)
+ log_sys_error("fopen", tmp_file);
+ goto out;
+ }
+
+ fprintf(fp, "# This file is automatically maintained by lvm.\n\n");
+ fprintf(fp, "persistent_filter_cache {\n");
+
+ _write_array(pf, fp, "valid_devices", PF_GOOD_DEVICE);
+ /* We don't gain anything by remembering invalid devices */
+ /* _write_array(pf, fp, "invalid_devices", PF_BAD_DEVICE); */
+
+ fprintf(fp, "}\n");
+ if (lvm_fclose(fp, tmp_file))
+ goto_out;
+
+ if (rename(tmp_file, pf->file))
+ log_error("%s: rename to %s failed: %s", tmp_file, pf->file,
+ strerror(errno));
+
+ r = 1;
+
+out:
+ fcntl_unlock_file(lockfd);
+
+ if (cft)
+ destroy_config_tree(cft);
+
+ return r;
+}
+
+static int _lookup_p(struct dev_filter *f, struct device *dev)
+{
+ struct pfilter *pf = (struct pfilter *) f->private;
+ void *l = dm_hash_lookup(pf->devices, dev_name(dev));
+ struct str_list *sl;
+
+ /* Cached BAD? */
+ if (l == PF_BAD_DEVICE) {
+ log_debug("%s: Skipping (cached)", dev_name(dev));
+ return 0;
+ }
+
+ /* Test dm devices every time, so cache them as GOOD. */
+ if (MAJOR(dev->dev) == dm_major()) {
+ if (!l)
+ dm_list_iterate_items(sl, &dev->aliases)
+ dm_hash_insert(pf->devices, sl->str, PF_GOOD_DEVICE);
+ if (!device_is_usable(dev)) {
+ log_debug("%s: Skipping unusable device", dev_name(dev));
+ return 0;
+ }
+ return pf->real->passes_filter(pf->real, dev);
+ }
+
+ /* Uncached */
+ if (!l) {
+ l = pf->real->passes_filter(pf->real, dev) ? PF_GOOD_DEVICE : PF_BAD_DEVICE;
+
+ dm_list_iterate_items(sl, &dev->aliases)
+ dm_hash_insert(pf->devices, sl->str, l);
+ }
+
+ return (l == PF_BAD_DEVICE) ? 0 : 1;
+}
+
+static void _persistent_destroy(struct dev_filter *f)
+{
+ struct pfilter *pf = (struct pfilter *) f->private;
+
+ if (f->use_count)
+ log_error(INTERNAL_ERROR "Destroying persistent filter while in use %u times.", f->use_count);
+
+ dm_hash_destroy(pf->devices);
+ dm_free(pf->file);
+ pf->real->destroy(pf->real);
+ dm_free(pf);
+ dm_free(f);
+}
+
+struct dev_filter *persistent_filter_create(struct dev_filter *real,
+ const char *file)
+{
+ struct pfilter *pf;
+ struct dev_filter *f = NULL;
+ struct stat info;
+
+ if (!(pf = dm_zalloc(sizeof(*pf))))
+ return_NULL;
+
+ if (!(pf->file = dm_malloc(strlen(file) + 1)))
+ goto_bad;
+
+ strcpy(pf->file, file);
+ pf->real = real;
+
+ if (!(_init_hash(pf))) {
+ log_error("Couldn't create hash table for persistent filter.");
+ goto bad;
+ }
+
+ if (!(f = dm_malloc(sizeof(*f))))
+ goto_bad;
+
+ /* Only merge cache file before dumping it if it changed externally. */
+ if (!stat(pf->file, &info))
+ pf->ctime = info.st_ctime;
+
+ f->passes_filter = _lookup_p;
+ f->destroy = _persistent_destroy;
+ f->use_count = 0;
+ f->private = pf;
+
+ return f;
+
+ bad:
+ dm_free(pf->file);
+ if (pf->devices)
+ dm_hash_destroy(pf->devices);
+ dm_free(pf);
+ dm_free(f);
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_FILTER_PERSISTENT_H
+#define _LVM_FILTER_PERSISTENT_H
+
+#include "dev-cache.h"
+
+struct dev_filter *persistent_filter_create(struct dev_filter *f,
+ const char *file);
+
+int persistent_filter_wipe(struct dev_filter *f);
+int persistent_filter_load(struct dev_filter *f, struct config_tree **cft_out);
+int persistent_filter_dump(struct dev_filter *f, int merge_existing);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "filter-regex.h"
+#include "device.h"
+
+struct rfilter {
+ struct dm_pool *mem;
+ dm_bitset_t accept;
+ struct dm_regex *engine;
+};
+
+static int _extract_pattern(struct dm_pool *mem, const char *pat,
+ char **regex, dm_bitset_t accept, int ix)
+{
+ char sep, *r, *ptr;
+
+ /*
+ * is this an accept or reject pattern
+ */
+ switch (*pat) {
+ case 'a':
+ dm_bit_set(accept, ix);
+ break;
+
+ case 'r':
+ dm_bit_clear(accept, ix);
+ break;
+
+ default:
+ log_info("pattern must begin with 'a' or 'r'");
+ return 0;
+ }
+ pat++;
+
+ /*
+ * get the separator
+ */
+ switch (*pat) {
+ case '(':
+ sep = ')';
+ break;
+
+ case '[':
+ sep = ']';
+ break;
+
+ case '{':
+ sep = '}';
+ break;
+
+ default:
+ sep = *pat;
+ }
+ pat++;
+
+ /*
+ * copy the regex
+ */
+ if (!(r = dm_pool_strdup(mem, pat)))
+ return_0;
+
+ /*
+ * trim the trailing character, having checked it's sep.
+ */
+ ptr = r + strlen(r) - 1;
+ if (*ptr != sep) {
+ log_info("invalid separator at end of regex");
+ return 0;
+ }
+ *ptr = '\0';
+
+ regex[ix] = r;
+ return 1;
+}
+
+static int _build_matcher(struct rfilter *rf, const struct config_value *val)
+{
+ struct dm_pool *scratch;
+ const struct config_value *v;
+ char **regex;
+ unsigned count = 0;
+ int i, r = 0;
+
+ if (!(scratch = dm_pool_create("filter dm_regex", 1024)))
+ return_0;
+
+ /*
+ * count how many patterns we have.
+ */
+ for (v = val; v; v = v->next) {
+ if (v->type != CFG_STRING) {
+ log_error("Filter patterns must be enclosed in quotes.");
+ goto out;
+ }
+
+ count++;
+ }
+
+ /*
+ * allocate space for them
+ */
+ if (!(regex = dm_pool_alloc(scratch, sizeof(*regex) * count)))
+ goto_out;
+
+ /*
+ * create the accept/reject bitset
+ */
+ rf->accept = dm_bitset_create(rf->mem, count);
+
+ /*
+ * fill the array back to front because we
+ * want the opposite precedence to what
+ * the matcher gives.
+ */
+ for (v = val, i = count - 1; v; v = v->next, i--)
+ if (!_extract_pattern(scratch, v->v.str, regex, rf->accept, i)) {
+ log_error("Invalid filter pattern \"%s\".", v->v.str);
+ goto out;
+ }
+
+ /*
+ * build the matcher.
+ */
+ if (!(rf->engine = dm_regex_create(rf->mem, (const char **) regex,
+ count)))
+ goto_out;
+ r = 1;
+
+ out:
+ dm_pool_destroy(scratch);
+ return r;
+}
+
+static int _accept_p(struct dev_filter *f, struct device *dev)
+{
+ int m, first = 1, rejected = 0;
+ struct rfilter *rf = (struct rfilter *) f->private;
+ struct str_list *sl;
+
+ dm_list_iterate_items(sl, &dev->aliases) {
+ m = dm_regex_match(rf->engine, sl->str);
+
+ if (m >= 0) {
+ if (dm_bit(rf->accept, m)) {
+ if (!first)
+ dev_set_preferred_name(sl, dev);
+
+ return 1;
+ }
+
+ rejected = 1;
+ }
+
+ first = 0;
+ }
+
+ if (rejected)
+ log_debug("%s: Skipping (regex)", dev_name(dev));
+
+ /*
+ * pass everything that doesn't match
+ * anything.
+ */
+ return !rejected;
+}
+
+static void _regex_destroy(struct dev_filter *f)
+{
+ struct rfilter *rf = (struct rfilter *) f->private;
+
+ if (f->use_count)
+ log_error(INTERNAL_ERROR "Destroying regex filter while in use %u times.", f->use_count);
+
+ dm_pool_destroy(rf->mem);
+}
+
+struct dev_filter *regex_filter_create(const struct config_value *patterns)
+{
+ struct dm_pool *mem = dm_pool_create("filter regex", 10 * 1024);
+ struct rfilter *rf;
+ struct dev_filter *f;
+
+ if (!mem)
+ return_NULL;
+
+ if (!(rf = dm_pool_alloc(mem, sizeof(*rf))))
+ goto_bad;
+
+ rf->mem = mem;
+
+ if (!_build_matcher(rf, patterns))
+ goto_bad;
+
+ if (!(f = dm_pool_zalloc(mem, sizeof(*f))))
+ goto_bad;
+
+ f->passes_filter = _accept_p;
+ f->destroy = _regex_destroy;
+ f->use_count = 0;
+ f->private = rf;
+ return f;
+
+ bad:
+ dm_pool_destroy(mem);
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_FILTER_REGEX_H
+#define _LVM_FILTER_REGEX_H
+
+#include "config.h"
+#include "dev-cache.h"
+
+/*
+ * patterns must be an array of strings of the form:
+ * [ra]<sep><regex><sep>, eg,
+ * r/cdrom/ - reject cdroms
+ * a|loop/[0-4]| - accept loops 0 to 4
+ * r|.*| - reject everything else
+ */
+
+struct dev_filter *regex_filter_create(const struct config_value *patterns);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "filter-sysfs.h"
+#include "lvm-string.h"
+
+#ifdef linux
+
+#include <dirent.h>
+
+static int _locate_sysfs_blocks(const char *sysfs_dir, char *path, size_t len,
+ unsigned *sysfs_depth)
+{
+ struct stat info;
+
+ /*
+ * unified classification directory for all kernel subsystems
+ *
+ * /sys/subsystem/block/devices
+ * |-- sda -> ../../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
+ * |-- sda1 -> ../../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1
+ * `-- sr0 -> ../../../devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block/sr0
+ *
+ */
+ if (dm_snprintf(path, len, "%s/%s", sysfs_dir,
+ "subsystem/block/devices") >= 0) {
+ if (!stat(path, &info)) {
+ *sysfs_depth = 0;
+ return 1;
+ }
+ }
+
+ /*
+ * block subsystem as a class
+ *
+ * /sys/class/block
+ * |-- sda -> ../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda
+ * |-- sda1 -> ../../devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1
+ * `-- sr0 -> ../../devices/pci0000:00/0000:00:1f.2/host1/target1:0:0/1:0:0:0/block/sr0
+ *
+ */
+ if (dm_snprintf(path, len, "%s/%s", sysfs_dir, "class/block") >= 0) {
+ if (!stat(path, &info)) {
+ *sysfs_depth = 0;
+ return 1;
+ }
+ }
+
+ /*
+ * old block subsystem layout with nested directories
+ *
+ * /sys/block/
+ * |-- sda
+ * | |-- capability
+ * | |-- dev
+ * ...
+ * | |-- sda1
+ * | | |-- dev
+ * ...
+ * |
+ * `-- sr0
+ * |-- capability
+ * |-- dev
+ * ...
+ *
+ */
+ if (dm_snprintf(path, len, "%s/%s", sysfs_dir, "block") >= 0) {
+ if (!stat(path, &info)) {
+ *sysfs_depth = 1;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+ * We need to store a set of dev_t.
+ *--------------------------------------------------------------*/
+struct entry {
+ struct entry *next;
+ dev_t dev;
+};
+
+#define SET_BUCKETS 64
+struct dev_set {
+ struct dm_pool *mem;
+ const char *sys_block;
+ unsigned sysfs_depth;
+ int initialised;
+ struct entry *slots[SET_BUCKETS];
+};
+
+static struct dev_set *_dev_set_create(struct dm_pool *mem,
+ const char *sys_block,
+ unsigned sysfs_depth)
+{
+ struct dev_set *ds;
+
+ if (!(ds = dm_pool_zalloc(mem, sizeof(*ds))))
+ return NULL;
+
+ ds->mem = mem;
+ ds->sys_block = dm_pool_strdup(mem, sys_block);
+ ds->sysfs_depth = sysfs_depth;
+ ds->initialised = 0;
+
+ return ds;
+}
+
+static unsigned _hash_dev(dev_t dev)
+{
+ return (major(dev) ^ minor(dev)) & (SET_BUCKETS - 1);
+}
+
+/*
+ * Doesn't check that the set already contains dev.
+ */
+static int _set_insert(struct dev_set *ds, dev_t dev)
+{
+ struct entry *e;
+ unsigned h = _hash_dev(dev);
+
+ if (!(e = dm_pool_alloc(ds->mem, sizeof(*e))))
+ return 0;
+
+ e->next = ds->slots[h];
+ e->dev = dev;
+ ds->slots[h] = e;
+
+ return 1;
+}
+
+static int _set_lookup(struct dev_set *ds, dev_t dev)
+{
+ unsigned h = _hash_dev(dev);
+ struct entry *e;
+
+ for (e = ds->slots[h]; e; e = e->next)
+ if (e->dev == dev)
+ return 1;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+ * filter methods
+ *--------------------------------------------------------------*/
+static int _parse_dev(const char *file, FILE *fp, dev_t *result)
+{
+ unsigned major, minor;
+ char buffer[64];
+
+ if (!fgets(buffer, sizeof(buffer), fp)) {
+ log_error("Empty sysfs device file: %s", file);
+ return 0;
+ }
+
+ if (sscanf(buffer, "%u:%u", &major, &minor) != 2) {
+ log_info("sysfs device file not correct format");
+ return 0;
+ }
+
+ *result = makedev(major, minor);
+ return 1;
+}
+
+static int _read_dev(const char *file, dev_t *result)
+{
+ int r;
+ FILE *fp;
+
+ if (!(fp = fopen(file, "r"))) {
+ log_sys_error("fopen", file);
+ return 0;
+ }
+
+ r = _parse_dev(file, fp, result);
+
+ if (fclose(fp))
+ log_sys_error("fclose", file);
+
+ return r;
+}
+
+/*
+ * Recurse through sysfs directories, inserting any devs found.
+ */
+static int _read_devs(struct dev_set *ds, const char *dir, unsigned sysfs_depth)
+{
+ struct dirent *d;
+ DIR *dr;
+ struct stat info;
+ char path[PATH_MAX];
+ char file[PATH_MAX];
+ dev_t dev = { 0 };
+ int r = 1;
+
+ if (!(dr = opendir(dir))) {
+ log_sys_error("opendir", dir);
+ return 0;
+ }
+
+ while ((d = readdir(dr))) {
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+
+ if (dm_snprintf(path, sizeof(path), "%s/%s", dir,
+ d->d_name) < 0) {
+ log_error("sysfs path name too long: %s in %s",
+ d->d_name, dir);
+ continue;
+ }
+
+ /* devices have a "dev" file */
+ if (dm_snprintf(file, sizeof(file), "%s/dev", path) < 0) {
+ log_error("sysfs path name too long: %s in %s",
+ d->d_name, dir);
+ continue;
+ }
+
+ if (!stat(file, &info)) {
+ /* recurse if we found a device and expect subdirs */
+ if (sysfs_depth)
+ _read_devs(ds, path, sysfs_depth - 1);
+
+ /* add the device we have found */
+ if (_read_dev(file, &dev))
+ _set_insert(ds, dev);
+ }
+ }
+
+ if (closedir(dr))
+ log_sys_error("closedir", dir);
+
+ return r;
+}
+
+static int _init_devs(struct dev_set *ds)
+{
+ if (!_read_devs(ds, ds->sys_block, ds->sysfs_depth)) {
+ ds->initialised = -1;
+ return 0;
+ }
+
+ ds->initialised = 1;
+
+ return 1;
+}
+
+
+static int _accept_p(struct dev_filter *f, struct device *dev)
+{
+ struct dev_set *ds = (struct dev_set *) f->private;
+
+ if (!ds->initialised)
+ _init_devs(ds);
+
+ /* Pass through if initialisation failed */
+ if (ds->initialised != 1)
+ return 1;
+
+ if (!_set_lookup(ds, dev->dev)) {
+ log_debug("%s: Skipping (sysfs)", dev_name(dev));
+ return 0;
+ } else
+ return 1;
+}
+
+static void _destroy(struct dev_filter *f)
+{
+ struct dev_set *ds = (struct dev_set *) f->private;
+
+ if (f->use_count)
+ log_error(INTERNAL_ERROR "Destroying sysfs filter while in use %u times.", f->use_count);
+
+ dm_pool_destroy(ds->mem);
+}
+
+struct dev_filter *sysfs_filter_create(const char *sysfs_dir)
+{
+ char sys_block[PATH_MAX];
+ unsigned sysfs_depth;
+ struct dm_pool *mem;
+ struct dev_set *ds;
+ struct dev_filter *f;
+
+ if (!*sysfs_dir) {
+ log_verbose("No proc filesystem found: skipping sysfs filter");
+ return NULL;
+ }
+
+ if (!_locate_sysfs_blocks(sysfs_dir, sys_block, sizeof(sys_block), &sysfs_depth))
+ return NULL;
+
+ if (!(mem = dm_pool_create("sysfs", 256))) {
+ log_error("sysfs pool creation failed");
+ return NULL;
+ }
+
+ if (!(ds = _dev_set_create(mem, sys_block, sysfs_depth))) {
+ log_error("sysfs dev_set creation failed");
+ goto bad;
+ }
+
+ if (!(f = dm_pool_zalloc(mem, sizeof(*f))))
+ goto_bad;
+
+ f->passes_filter = _accept_p;
+ f->destroy = _destroy;
+ f->use_count = 0;
+ f->private = ds;
+ return f;
+
+ bad:
+ dm_pool_destroy(mem);
+ return NULL;
+}
+
+#else
+
+struct dev_filter *sysfs_filter_create(const char *sysfs_dir __attribute__((unused)))
+{
+ return NULL;
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_FILTER_SYSFS_H
+#define _LVM_FILTER_SYSFS_H
+
+#include "config.h"
+#include "dev-cache.h"
+
+struct dev_filter *sysfs_filter_create(const char *sysfs_dir);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "dev-cache.h"
+#include "filter.h"
+#include "lvm-string.h"
+#include "config.h"
+#include "metadata.h"
+#include "activate.h"
+
+#include <dirent.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#define NUMBER_OF_MAJORS 4096
+
+/* 0 means LVM won't use this major number. */
+static int _max_partitions_by_major[NUMBER_OF_MAJORS];
+
+typedef struct {
+ const char *name;
+ const int max_partitions;
+} device_info_t;
+
+static int _md_major = -1;
+static int _blkext_major = -1;
+static int _drbd_major = -1;
+static int _device_mapper_major = -1;
+
+int dm_major(void)
+{
+ return _device_mapper_major;
+}
+
+int md_major(void)
+{
+ return _md_major;
+}
+
+int blkext_major(void)
+{
+ return _blkext_major;
+}
+
+int dev_subsystem_part_major(const struct device *dev)
+{
+ dev_t primary_dev;
+
+ if (MAJOR(dev->dev) == -1)
+ return 0;
+
+ if (MAJOR(dev->dev) == _md_major)
+ return 1;
+
+ if (MAJOR(dev->dev) == _drbd_major)
+ return 1;
+
+ if ((MAJOR(dev->dev) == _blkext_major) &&
+ (get_primary_dev(sysfs_dir_path(), dev, &primary_dev)) &&
+ (MAJOR(primary_dev) == _md_major))
+ return 1;
+
+ return 0;
+}
+
+const char *dev_subsystem_name(const struct device *dev)
+{
+ if (MAJOR(dev->dev) == _md_major)
+ return "MD";
+
+ if (MAJOR(dev->dev) == _drbd_major)
+ return "DRBD";
+
+ if (MAJOR(dev->dev) == _blkext_major)
+ return "BLKEXT";
+
+ return "";
+}
+
+/*
+ * Devices are only checked for partition tables if their minor number
+ * is a multiple of the number corresponding to their type below
+ * i.e. this gives the granularity of whole-device minor numbers.
+ * Use 1 if the device is not partitionable.
+ *
+ * The list can be supplemented with devices/types in the config file.
+ */
+static const device_info_t device_info[] = {
+ {"ide", 64}, /* IDE disk */
+ {"sd", 16}, /* SCSI disk */
+ {"md", 1}, /* Multiple Disk driver (SoftRAID) */
+ {"mdp", 1}, /* Partitionable MD */
+ {"loop", 1}, /* Loop device */
+ {"dasd", 4}, /* DASD disk (IBM S/390, zSeries) */
+ {"dac960", 8}, /* DAC960 */
+ {"nbd", 16}, /* Network Block Device */
+ {"ida", 16}, /* Compaq SMART2 */
+ {"cciss", 16}, /* Compaq CCISS array */
+ {"ubd", 16}, /* User-mode virtual block device */
+ {"ataraid", 16}, /* ATA Raid */
+ {"drbd", 16}, /* Distributed Replicated Block Device */
+ {"emcpower", 16}, /* EMC Powerpath */
+ {"power2", 16}, /* EMC Powerpath */
+ {"i2o_block", 16}, /* i2o Block Disk */
+ {"iseries/vd", 8}, /* iSeries disks */
+ {"gnbd", 1}, /* Network block device */
+ {"ramdisk", 1}, /* RAM disk */
+ {"aoe", 16}, /* ATA over Ethernet */
+ {"device-mapper", 1}, /* Other mapped devices */
+ {"xvd", 16}, /* Xen virtual block device */
+ {"vdisk", 8}, /* SUN's LDOM virtual block device */
+ {"ps3disk", 16}, /* PlayStation 3 internal disk */
+ {"virtblk", 8}, /* VirtIO disk */
+ {"mmc", 16}, /* MMC block device */
+ {"blkext", 1}, /* Extended device partitions */
+ {NULL, 0}
+};
+
+static int _passes_lvm_type_device_filter(struct dev_filter *f __attribute__((unused)),
+ struct device *dev)
+{
+ const char *name = dev_name(dev);
+ int ret = 0;
+ uint64_t size;
+
+ /* Is this a recognised device type? */
+ if (!_max_partitions_by_major[MAJOR(dev->dev)]) {
+ log_debug("%s: Skipping: Unrecognised LVM device type %"
+ PRIu64, name, (uint64_t) MAJOR(dev->dev));
+ return 0;
+ }
+
+ /* Check it's accessible */
+ if (!dev_open_flags(dev, O_RDONLY, 0, 1)) {
+ log_debug("%s: Skipping: open failed", name);
+ return 0;
+ }
+
+ /* Check it's not too small */
+ if (!dev_get_size(dev, &size)) {
+ log_debug("%s: Skipping: dev_get_size failed", name);
+ goto out;
+ }
+
+ if (size < PV_MIN_SIZE) {
+ log_debug("%s: Skipping: Too small to hold a PV", name);
+ goto out;
+ }
+
+ if (is_partitioned_dev(dev)) {
+ log_debug("%s: Skipping: Partition table signature found",
+ name);
+ goto out;
+ }
+
+ ret = 1;
+
+ out:
+ dev_close(dev);
+
+ return ret;
+}
+
+static int _scan_proc_dev(const char *proc, const struct config_node *cn)
+{
+ char line[80];
+ char proc_devices[PATH_MAX];
+ FILE *pd = NULL;
+ int i, j = 0;
+ int line_maj = 0;
+ int blocksection = 0;
+ size_t dev_len = 0;
+ const struct config_value *cv;
+ const char *name;
+
+
+ if (!*proc) {
+ log_verbose("No proc filesystem found: using all block device "
+ "types");
+ for (i = 0; i < NUMBER_OF_MAJORS; i++)
+ _max_partitions_by_major[i] = 1;
+ return 1;
+ }
+
+ /* All types unrecognised initially */
+ memset(_max_partitions_by_major, 0, sizeof(int) * NUMBER_OF_MAJORS);
+
+ if (dm_snprintf(proc_devices, sizeof(proc_devices),
+ "%s/devices", proc) < 0) {
+ log_error("Failed to create /proc/devices string");
+ return 0;
+ }
+
+ if (!(pd = fopen(proc_devices, "r"))) {
+ log_sys_error("fopen", proc_devices);
+ return 0;
+ }
+
+ while (fgets(line, 80, pd) != NULL) {
+ i = 0;
+ while (line[i] == ' ' && line[i] != '\0')
+ i++;
+
+ /* If it's not a number it may be name of section */
+ line_maj = atoi(((char *) (line + i)));
+ if (!line_maj) {
+ blocksection = (line[i] == 'B') ? 1 : 0;
+ continue;
+ }
+
+ /* We only want block devices ... */
+ if (!blocksection)
+ continue;
+
+ /* Find the start of the device major name */
+ while (line[i] != ' ' && line[i] != '\0')
+ i++;
+ while (line[i] == ' ' && line[i] != '\0')
+ i++;
+
+ /* Look for md device */
+ if (!strncmp("md", line + i, 2) && isspace(*(line + i + 2)))
+ _md_major = line_maj;
+
+ /* Look for blkext device */
+ if (!strncmp("blkext", line + i, 6) && isspace(*(line + i + 6)))
+ _blkext_major = line_maj;
+
+ /* Look for drbd device */
+ if (!strncmp("drbd", line + i, 4) && isspace(*(line + i + 4)))
+ _drbd_major = line_maj;
+
+ /* Look for device-mapper device */
+ /* FIXME Cope with multiple majors */
+ if (!strncmp("device-mapper", line + i, 13) && isspace(*(line + i + 13)))
+ _device_mapper_major = line_maj;
+
+ /* Go through the valid device names and if there is a
+ match store max number of partitions */
+ for (j = 0; device_info[j].name != NULL; j++) {
+ dev_len = strlen(device_info[j].name);
+ if (dev_len <= strlen(line + i) &&
+ !strncmp(device_info[j].name, line + i, dev_len) &&
+ (line_maj < NUMBER_OF_MAJORS)) {
+ _max_partitions_by_major[line_maj] =
+ device_info[j].max_partitions;
+ break;
+ }
+ }
+
+ if (!cn)
+ continue;
+
+ /* Check devices/types for local variations */
+ for (cv = cn->v; cv; cv = cv->next) {
+ if (cv->type != CFG_STRING) {
+ log_error("Expecting string in devices/types "
+ "in config file");
+ if (fclose(pd))
+ log_sys_error("fclose", proc_devices);
+ return 0;
+ }
+ dev_len = strlen(cv->v.str);
+ name = cv->v.str;
+ cv = cv->next;
+ if (!cv || cv->type != CFG_INT) {
+ log_error("Max partition count missing for %s "
+ "in devices/types in config file",
+ name);
+ if (fclose(pd))
+ log_sys_error("fclose", proc_devices);
+ return 0;
+ }
+ if (!cv->v.i) {
+ log_error("Zero partition count invalid for "
+ "%s in devices/types in config file",
+ name);
+ if (fclose(pd))
+ log_sys_error("fclose", proc_devices);
+ return 0;
+ }
+ if (dev_len <= strlen(line + i) &&
+ !strncmp(name, line + i, dev_len) &&
+ (line_maj < NUMBER_OF_MAJORS)) {
+ _max_partitions_by_major[line_maj] = cv->v.i;
+ break;
+ }
+ }
+ }
+
+ if (fclose(pd))
+ log_sys_error("fclose", proc_devices);
+
+ return 1;
+}
+
+int max_partitions(int major)
+{
+ return _max_partitions_by_major[major];
+}
+
+struct dev_filter *lvm_type_filter_create(const char *proc,
+ const struct config_node *cn)
+{
+ struct dev_filter *f;
+
+ if (!(f = dm_malloc(sizeof(struct dev_filter)))) {
+ log_error("LVM type filter allocation failed");
+ return NULL;
+ }
+
+ f->passes_filter = _passes_lvm_type_device_filter;
+ f->destroy = lvm_type_filter_destroy;
+ f->use_count = 0;
+ f->private = NULL;
+
+ if (!_scan_proc_dev(proc, cn)) {
+ dm_free(f);
+ return_NULL;
+ }
+
+ return f;
+}
+
+void lvm_type_filter_destroy(struct dev_filter *f)
+{
+ if (f->use_count)
+ log_error(INTERNAL_ERROR "Destroying lvm_type filter while in use %u times.", f->use_count);
+
+ dm_free(f);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_FILTER_H
+#define _LVM_FILTER_H
+
+#include "config.h"
+
+#include <sys/stat.h>
+
+#ifdef linux
+# define MAJOR(dev) ((dev & 0xfff00) >> 8)
+# define MINOR(dev) ((dev & 0xff) | ((dev >> 12) & 0xfff00))
+# define MKDEV(ma,mi) ((mi & 0xff) | (ma << 8) | ((mi & ~0xff) << 12))
+#else
+# define MAJOR(x) major((x))
+# define MINOR(x) minor((x))
+# define MKDEV(x,y) makedev((x),(y))
+#endif
+
+struct dev_filter *lvm_type_filter_create(const char *proc,
+ const struct config_node *cn);
+
+void lvm_type_filter_destroy(struct dev_filter *f);
+
+int dm_major(void);
+int md_major(void);
+int blkext_major(void);
+int max_partitions(int major);
+
+int dev_subsystem_part_major(const struct device *dev);
+const char *dev_subsystem_name(const struct device *dev);
+
+#endif
--- /dev/null
+init_format
--- /dev/null
+#
+# Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES =\
+ disk-rep.c \
+ format1.c \
+ import-export.c \
+ import-extents.c \
+ layout.c \
+ lvm1-label.c \
+ vg_number.c
+
+LIB_SHARED = liblvm2format1.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+install: install_lvm2_plugin
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "disk-rep.h"
+#include "xlate.h"
+#include "filter.h"
+#include "lvmcache.h"
+
+#include <fcntl.h>
+
+#define xx16(v) disk->v = xlate16(disk->v)
+#define xx32(v) disk->v = xlate32(disk->v)
+#define xx64(v) disk->v = xlate64(disk->v)
+
+/*
+ * Functions to perform the endian conversion
+ * between disk and core. The same code works
+ * both ways of course.
+ */
+static void _xlate_pvd(struct pv_disk *disk)
+{
+ xx16(version);
+
+ xx32(pv_on_disk.base);
+ xx32(pv_on_disk.size);
+ xx32(vg_on_disk.base);
+ xx32(vg_on_disk.size);
+ xx32(pv_uuidlist_on_disk.base);
+ xx32(pv_uuidlist_on_disk.size);
+ xx32(lv_on_disk.base);
+ xx32(lv_on_disk.size);
+ xx32(pe_on_disk.base);
+ xx32(pe_on_disk.size);
+
+ xx32(pv_major);
+ xx32(pv_number);
+ xx32(pv_status);
+ xx32(pv_allocatable);
+ xx32(pv_size);
+ xx32(lv_cur);
+ xx32(pe_size);
+ xx32(pe_total);
+ xx32(pe_allocated);
+ xx32(pe_start);
+}
+
+static void _xlate_lvd(struct lv_disk *disk)
+{
+ xx32(lv_access);
+ xx32(lv_status);
+ xx32(lv_open);
+ xx32(lv_dev);
+ xx32(lv_number);
+ xx32(lv_mirror_copies);
+ xx32(lv_recovery);
+ xx32(lv_schedule);
+ xx32(lv_size);
+ xx32(lv_snapshot_minor);
+ xx16(lv_chunk_size);
+ xx16(dummy);
+ xx32(lv_allocated_le);
+ xx32(lv_stripes);
+ xx32(lv_stripesize);
+ xx32(lv_badblock);
+ xx32(lv_allocation);
+ xx32(lv_io_timeout);
+ xx32(lv_read_ahead);
+}
+
+static void _xlate_vgd(struct vg_disk *disk)
+{
+ xx32(vg_number);
+ xx32(vg_access);
+ xx32(vg_status);
+ xx32(lv_max);
+ xx32(lv_cur);
+ xx32(lv_open);
+ xx32(pv_max);
+ xx32(pv_cur);
+ xx32(pv_act);
+ xx32(dummy);
+ xx32(vgda);
+ xx32(pe_size);
+ xx32(pe_total);
+ xx32(pe_allocated);
+ xx32(pvg_total);
+}
+
+static void _xlate_extents(struct pe_disk *extents, uint32_t count)
+{
+ unsigned i;
+
+ for (i = 0; i < count; i++) {
+ extents[i].lv_num = xlate16(extents[i].lv_num);
+ extents[i].le_num = xlate16(extents[i].le_num);
+ }
+}
+
+/*
+ * Handle both minor metadata formats.
+ */
+static int _munge_formats(struct pv_disk *pvd)
+{
+ uint32_t pe_start;
+ unsigned b, e;
+
+ switch (pvd->version) {
+ case 1:
+ pvd->pe_start = ((pvd->pe_on_disk.base +
+ pvd->pe_on_disk.size) >> SECTOR_SHIFT);
+ break;
+
+ case 2:
+ pvd->version = 1;
+ pe_start = pvd->pe_start << SECTOR_SHIFT;
+ pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base;
+ break;
+
+ default:
+ return 0;
+ }
+
+ /* UUID too long? */
+ if (pvd->pv_uuid[ID_LEN]) {
+ /* Retain ID_LEN chars from end */
+ for (e = ID_LEN; e < sizeof(pvd->pv_uuid); e++) {
+ if (!pvd->pv_uuid[e]) {
+ e--;
+ break;
+ }
+ }
+ for (b = 0; b < ID_LEN; b++) {
+ pvd->pv_uuid[b] = pvd->pv_uuid[++e - ID_LEN];
+ /* FIXME Remove all invalid chars */
+ if (pvd->pv_uuid[b] == '/')
+ pvd->pv_uuid[b] = '#';
+ }
+ memset(&pvd->pv_uuid[ID_LEN], 0, sizeof(pvd->pv_uuid) - ID_LEN);
+ }
+
+ /* If UUID is missing, create one */
+ if (pvd->pv_uuid[0] == '\0') {
+ uuid_from_num((char *)pvd->pv_uuid, pvd->pv_number);
+ pvd->pv_uuid[ID_LEN] = '\0';
+ }
+
+ return 1;
+}
+
+/*
+ * If exported, remove "PV_EXP" from end of VG name
+ */
+static void _munge_exported_vg(struct pv_disk *pvd)
+{
+ int l;
+ size_t s;
+
+ /* Return if PV not in a VG */
+ if ((!*pvd->vg_name))
+ return;
+ /* FIXME also check vgd->status & VG_EXPORTED? */
+
+ l = strlen((char *)pvd->vg_name);
+ s = sizeof(EXPORTED_TAG);
+ if (!strncmp((char *)pvd->vg_name + l - s + 1, EXPORTED_TAG, s)) {
+ pvd->vg_name[l - s + 1] = '\0';
+ pvd->pv_status |= VG_EXPORTED;
+ }
+}
+
+int munge_pvd(struct device *dev, struct pv_disk *pvd)
+{
+ _xlate_pvd(pvd);
+
+ if (pvd->id[0] != 'H' || pvd->id[1] != 'M') {
+ log_very_verbose("%s does not have a valid LVM1 PV identifier",
+ dev_name(dev));
+ return 0;
+ }
+
+ if (!_munge_formats(pvd)) {
+ log_very_verbose("format1: Unknown metadata version %d "
+ "found on %s", pvd->version, dev_name(dev));
+ return 0;
+ }
+
+ /* If VG is exported, set VG name back to the real name */
+ _munge_exported_vg(pvd);
+
+ return 1;
+}
+
+static int _read_pvd(struct device *dev, struct pv_disk *pvd)
+{
+ if (!dev_read(dev, UINT64_C(0), sizeof(*pvd), pvd)) {
+ log_very_verbose("Failed to read PV data from %s",
+ dev_name(dev));
+ return 0;
+ }
+
+ return munge_pvd(dev, pvd);
+}
+
+static int _read_lvd(struct device *dev, uint64_t pos, struct lv_disk *disk)
+{
+ if (!dev_read(dev, pos, sizeof(*disk), disk))
+ return_0;
+
+ _xlate_lvd(disk);
+
+ return 1;
+}
+
+int read_vgd(struct device *dev, struct vg_disk *vgd, struct pv_disk *pvd)
+{
+ uint64_t pos = pvd->vg_on_disk.base;
+
+ if (!dev_read(dev, pos, sizeof(*vgd), vgd))
+ return_0;
+
+ _xlate_vgd(vgd);
+
+ if ((vgd->lv_max > MAX_LV) || (vgd->pv_max > MAX_PV))
+ return_0;
+
+ /* If UUID is missing, create one */
+ if (vgd->vg_uuid[0] == '\0')
+ uuid_from_num((char *)vgd->vg_uuid, vgd->vg_number);
+
+ return 1;
+}
+
+static int _read_uuids(struct disk_list *data)
+{
+ unsigned num_read = 0;
+ struct uuid_list *ul;
+ char buffer[NAME_LEN] __attribute__((aligned(8)));
+ uint64_t pos = data->pvd.pv_uuidlist_on_disk.base;
+ uint64_t end = pos + data->pvd.pv_uuidlist_on_disk.size;
+
+ while (pos < end && num_read < data->vgd.pv_cur) {
+ if (!dev_read(data->dev, pos, sizeof(buffer), buffer))
+ return_0;
+
+ if (!(ul = dm_pool_alloc(data->mem, sizeof(*ul))))
+ return_0;
+
+ memcpy(ul->uuid, buffer, NAME_LEN);
+ ul->uuid[NAME_LEN - 1] = '\0';
+
+ dm_list_add(&data->uuids, &ul->list);
+
+ pos += NAME_LEN;
+ num_read++;
+ }
+
+ return 1;
+}
+
+static int _check_lvd(struct lv_disk *lvd)
+{
+ return !(lvd->lv_name[0] == '\0');
+}
+
+static int _read_lvs(struct disk_list *data)
+{
+ unsigned int i, lvs_read = 0;
+ uint64_t pos;
+ struct lvd_list *ll;
+ struct vg_disk *vgd = &data->vgd;
+
+ for (i = 0; (i < vgd->lv_max) && (lvs_read < vgd->lv_cur); i++) {
+ pos = data->pvd.lv_on_disk.base + (i * sizeof(struct lv_disk));
+ ll = dm_pool_alloc(data->mem, sizeof(*ll));
+
+ if (!ll)
+ return_0;
+
+ if (!_read_lvd(data->dev, pos, &ll->lvd))
+ return_0;
+
+ if (!_check_lvd(&ll->lvd))
+ continue;
+
+ lvs_read++;
+ dm_list_add(&data->lvds, &ll->list);
+ }
+
+ return 1;
+}
+
+static int _read_extents(struct disk_list *data)
+{
+ size_t len = sizeof(struct pe_disk) * data->pvd.pe_total;
+ struct pe_disk *extents = dm_pool_alloc(data->mem, len);
+ uint64_t pos = data->pvd.pe_on_disk.base;
+
+ if (!extents)
+ return_0;
+
+ if (!dev_read(data->dev, pos, len, extents))
+ return_0;
+
+ _xlate_extents(extents, data->pvd.pe_total);
+ data->extents = extents;
+
+ return 1;
+}
+
+static void __update_lvmcache(const struct format_type *fmt,
+ struct disk_list *dl,
+ struct device *dev, const char *vgid,
+ unsigned exported)
+{
+ struct lvmcache_info *info;
+ const char *vgname = *((char *)dl->pvd.vg_name) ?
+ (char *)dl->pvd.vg_name : fmt->orphan_vg_name;
+
+ if (!(info = lvmcache_add(fmt->labeller, (char *)dl->pvd.pv_uuid, dev,
+ vgname, vgid, exported ? EXPORTED_VG : 0))) {
+ stack;
+ return;
+ }
+
+ info->device_size = xlate32(dl->pvd.pv_size) << SECTOR_SHIFT;
+ dm_list_init(&info->mdas);
+ info->status &= ~CACHE_INVALID;
+}
+
+static struct disk_list *__read_disk(const struct format_type *fmt,
+ struct device *dev, struct dm_pool *mem,
+ const char *vg_name)
+{
+ struct disk_list *dl = dm_pool_zalloc(mem, sizeof(*dl));
+ const char *name = dev_name(dev);
+
+ if (!dl)
+ return_NULL;
+
+ dl->dev = dev;
+ dl->mem = mem;
+ dm_list_init(&dl->uuids);
+ dm_list_init(&dl->lvds);
+
+ if (!_read_pvd(dev, &dl->pvd))
+ goto_bad;
+
+ /*
+ * is it an orphan ?
+ */
+ if (!*dl->pvd.vg_name) {
+ log_very_verbose("%s is not a member of any format1 VG", name);
+
+ __update_lvmcache(fmt, dl, dev, fmt->orphan_vg_name, 0);
+ return (vg_name) ? NULL : dl;
+ }
+
+ if (!read_vgd(dl->dev, &dl->vgd, &dl->pvd)) {
+ log_error("Failed to read VG data from PV (%s)", name);
+ __update_lvmcache(fmt, dl, dev, fmt->orphan_vg_name, 0);
+ goto bad;
+ }
+
+ if (vg_name && strcmp(vg_name, (char *)dl->pvd.vg_name)) {
+ log_very_verbose("%s is not a member of the VG %s",
+ name, vg_name);
+ __update_lvmcache(fmt, dl, dev, fmt->orphan_vg_name, 0);
+ goto bad;
+ }
+
+ __update_lvmcache(fmt, dl, dev, (char *)dl->vgd.vg_uuid,
+ dl->vgd.vg_status & VG_EXPORTED);
+
+ if (!_read_uuids(dl)) {
+ log_error("Failed to read PV uuid list from %s", name);
+ goto bad;
+ }
+
+ if (!_read_lvs(dl)) {
+ log_error("Failed to read LV's from %s", name);
+ goto bad;
+ }
+
+ if (!_read_extents(dl)) {
+ log_error("Failed to read extents from %s", name);
+ goto bad;
+ }
+
+ log_very_verbose("Found %s in %sVG %s", name,
+ (dl->vgd.vg_status & VG_EXPORTED) ? "exported " : "",
+ dl->pvd.vg_name);
+
+ return dl;
+
+ bad:
+ dm_pool_free(dl->mem, dl);
+ return NULL;
+}
+
+struct disk_list *read_disk(const struct format_type *fmt, struct device *dev,
+ struct dm_pool *mem, const char *vg_name)
+{
+ struct disk_list *dl;
+
+ if (!dev_open(dev))
+ return_NULL;
+
+ dl = __read_disk(fmt, dev, mem, vg_name);
+
+ if (!dev_close(dev))
+ stack;
+
+ return dl;
+}
+
+static void _add_pv_to_list(struct dm_list *head, struct disk_list *data)
+{
+ struct pv_disk *pvd;
+ struct disk_list *diskl;
+
+ dm_list_iterate_items(diskl, head) {
+ pvd = &diskl->pvd;
+ if (!strncmp((char *)data->pvd.pv_uuid, (char *)pvd->pv_uuid,
+ sizeof(pvd->pv_uuid))) {
+ if (!dev_subsystem_part_major(data->dev)) {
+ log_very_verbose("Ignoring duplicate PV %s on "
+ "%s", pvd->pv_uuid,
+ dev_name(data->dev));
+ return;
+ }
+ log_very_verbose("Duplicate PV %s - using %s %s",
+ pvd->pv_uuid, dev_subsystem_name(data->dev),
+ dev_name(data->dev));
+ dm_list_del(&diskl->list);
+ break;
+ }
+ }
+ dm_list_add(head, &data->list);
+}
+
+/*
+ * Build a list of pv_d's structures, allocated from mem.
+ * We keep track of the first object allocated from the pool
+ * so we can free off all the memory if something goes wrong.
+ */
+int read_pvs_in_vg(const struct format_type *fmt, const char *vg_name,
+ struct dev_filter *filter, struct dm_pool *mem,
+ struct dm_list *head)
+{
+ struct dev_iter *iter;
+ struct device *dev;
+ struct disk_list *data = NULL;
+ struct lvmcache_vginfo *vginfo;
+ struct lvmcache_info *info;
+
+ /* Fast path if we already saw this VG and cached the list of PVs */
+ if (vg_name && (vginfo = vginfo_from_vgname(vg_name, NULL)) &&
+ vginfo->infos.n) {
+ dm_list_iterate_items(info, &vginfo->infos) {
+ dev = info->dev;
+ if (!dev || !(data = read_disk(fmt, dev, mem, vg_name)))
+ break;
+ _add_pv_to_list(head, data);
+ }
+
+ /* Did we find the whole VG? */
+ if (!vg_name || is_orphan_vg(vg_name) ||
+ (data && *data->pvd.vg_name &&
+ dm_list_size(head) == data->vgd.pv_cur))
+ return 1;
+
+ /* Failed */
+ dm_list_init(head);
+ /* vgcache_del(vg_name); */
+ }
+
+ if (!(iter = dev_iter_create(filter, 1))) {
+ log_error("read_pvs_in_vg: dev_iter_create failed");
+ return 0;
+ }
+
+ /* Otherwise do a complete scan */
+ for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) {
+ if ((data = read_disk(fmt, dev, mem, vg_name))) {
+ _add_pv_to_list(head, data);
+ }
+ }
+ dev_iter_destroy(iter);
+
+ if (dm_list_empty(head))
+ return 0;
+
+ return 1;
+}
+
+static int _write_vgd(struct disk_list *data)
+{
+ struct vg_disk *vgd = &data->vgd;
+ uint64_t pos = data->pvd.vg_on_disk.base;
+
+ log_debug("Writing %s VG metadata to %s at %" PRIu64 " len %" PRIsize_t,
+ data->pvd.vg_name, dev_name(data->dev), pos, sizeof(*vgd));
+
+ _xlate_vgd(vgd);
+ if (!dev_write(data->dev, pos, sizeof(*vgd), vgd))
+ return_0;
+
+ _xlate_vgd(vgd);
+
+ return 1;
+}
+
+static int _write_uuids(struct disk_list *data)
+{
+ struct uuid_list *ul;
+ uint64_t pos = data->pvd.pv_uuidlist_on_disk.base;
+ uint64_t end = pos + data->pvd.pv_uuidlist_on_disk.size;
+
+ dm_list_iterate_items(ul, &data->uuids) {
+ if (pos >= end) {
+ log_error("Too many uuids to fit on %s",
+ dev_name(data->dev));
+ return 0;
+ }
+
+ log_debug("Writing %s uuidlist to %s at %" PRIu64 " len %d",
+ data->pvd.vg_name, dev_name(data->dev),
+ pos, NAME_LEN);
+
+ if (!dev_write(data->dev, pos, NAME_LEN, ul->uuid))
+ return_0;
+
+ pos += NAME_LEN;
+ }
+
+ return 1;
+}
+
+static int _write_lvd(struct device *dev, uint64_t pos, struct lv_disk *disk)
+{
+ log_debug("Writing %s LV %s metadata to %s at %" PRIu64 " len %"
+ PRIsize_t, disk->vg_name, disk->lv_name, dev_name(dev),
+ pos, sizeof(*disk));
+
+ _xlate_lvd(disk);
+ if (!dev_write(dev, pos, sizeof(*disk), disk))
+ return_0;
+
+ _xlate_lvd(disk);
+
+ return 1;
+}
+
+static int _write_lvs(struct disk_list *data)
+{
+ struct lvd_list *ll;
+ uint64_t pos, offset;
+
+ pos = data->pvd.lv_on_disk.base;
+
+ if (!dev_set(data->dev, pos, data->pvd.lv_on_disk.size, 0)) {
+ log_error("Couldn't zero lv area on device '%s'",
+ dev_name(data->dev));
+ return 0;
+ }
+
+ dm_list_iterate_items(ll, &data->lvds) {
+ offset = sizeof(struct lv_disk) * ll->lvd.lv_number;
+ if (offset + sizeof(struct lv_disk) > data->pvd.lv_on_disk.size) {
+ log_error("lv_number %d too large", ll->lvd.lv_number);
+ return 0;
+ }
+
+ if (!_write_lvd(data->dev, pos + offset, &ll->lvd))
+ return_0;
+ }
+
+ return 1;
+}
+
+static int _write_extents(struct disk_list *data)
+{
+ size_t len = sizeof(struct pe_disk) * data->pvd.pe_total;
+ struct pe_disk *extents = data->extents;
+ uint64_t pos = data->pvd.pe_on_disk.base;
+
+ log_debug("Writing %s extents metadata to %s at %" PRIu64 " len %"
+ PRIsize_t, data->pvd.vg_name, dev_name(data->dev),
+ pos, len);
+
+ _xlate_extents(extents, data->pvd.pe_total);
+ if (!dev_write(data->dev, pos, len, extents))
+ return_0;
+
+ _xlate_extents(extents, data->pvd.pe_total);
+
+ return 1;
+}
+
+static int _write_pvd(struct disk_list *data)
+{
+ char *buf;
+ uint64_t pos = data->pvd.pv_on_disk.base;
+ size_t size = data->pvd.pv_on_disk.size;
+
+ if (size < sizeof(struct pv_disk)) {
+ log_error("Invalid PV structure size.");
+ return 0;
+ }
+
+ /* Make sure that the gap between the PV structure and
+ the next one is zeroed in order to make non LVM tools
+ happy (idea from AED) */
+ buf = dm_zalloc(size);
+ if (!buf) {
+ log_error("Couldn't allocate temporary PV buffer.");
+ return 0;
+ }
+
+ memcpy(buf, &data->pvd, sizeof(struct pv_disk));
+
+ log_debug("Writing %s PV metadata to %s at %" PRIu64 " len %"
+ PRIsize_t, data->pvd.vg_name, dev_name(data->dev),
+ pos, size);
+
+ _xlate_pvd((struct pv_disk *) buf);
+ if (!dev_write(data->dev, pos, size, buf)) {
+ dm_free(buf);
+ return_0;
+ }
+
+ dm_free(buf);
+ return 1;
+}
+
+/*
+ * assumes the device has been opened.
+ */
+static int __write_all_pvd(const struct format_type *fmt __attribute__((unused)),
+ struct disk_list *data)
+{
+ const char *pv_name = dev_name(data->dev);
+
+ if (!_write_pvd(data)) {
+ log_error("Failed to write PV structure onto %s", pv_name);
+ return 0;
+ }
+
+ /* vgcache_add(data->pvd.vg_name, data->vgd.vg_uuid, data->dev, fmt); */
+ /*
+ * Stop here for orphan pv's.
+ */
+ if (data->pvd.vg_name[0] == '\0') {
+ /* if (!test_mode())
+ vgcache_add(data->pvd.vg_name, NULL, data->dev, fmt); */
+ return 1;
+ }
+
+ /* if (!test_mode())
+ vgcache_add(data->pvd.vg_name, data->vgd.vg_uuid, data->dev,
+ fmt); */
+
+ if (!_write_vgd(data)) {
+ log_error("Failed to write VG data to %s", pv_name);
+ return 0;
+ }
+
+ if (!_write_uuids(data)) {
+ log_error("Failed to write PV uuid list to %s", pv_name);
+ return 0;
+ }
+
+ if (!_write_lvs(data)) {
+ log_error("Failed to write LV's to %s", pv_name);
+ return 0;
+ }
+
+ if (!_write_extents(data)) {
+ log_error("Failed to write extents to %s", pv_name);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * opens the device and hands to the above fn.
+ */
+static int _write_all_pvd(const struct format_type *fmt, struct disk_list *data)
+{
+ int r;
+
+ if (!dev_open(data->dev))
+ return_0;
+
+ r = __write_all_pvd(fmt, data);
+
+ if (!dev_close(data->dev))
+ stack;
+
+ return r;
+}
+
+/*
+ * Writes all the given pv's to disk. Does very
+ * little sanity checking, so make sure correct
+ * data is passed to here.
+ */
+int write_disks(const struct format_type *fmt, struct dm_list *pvs)
+{
+ struct disk_list *dl;
+
+ dm_list_iterate_items(dl, pvs) {
+ if (!(_write_all_pvd(fmt, dl)))
+ return_0;
+
+ log_very_verbose("Successfully wrote data to %s",
+ dev_name(dl->dev));
+ }
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DISK_REP_FORMAT1_H
+#define DISK_REP_FORMAT1_H
+
+#include "lvm-types.h"
+#include "metadata.h"
+#include "toolcontext.h"
+
+#define MAX_PV 256
+#define MAX_LV 256
+#define MAX_VG 99
+
+#define LVM_BLK_MAJOR 58
+
+#define MAX_PV_SIZE ((uint32_t) -1) /* 2TB in sectors - 1 */
+#define MIN_PE_SIZE (8192L >> SECTOR_SHIFT) /* 8 KB in sectors */
+#define MAX_PE_SIZE (16L * 1024L * (1024L >> SECTOR_SHIFT) * 1024L)
+#define PE_SIZE_PV_SIZE_REL 5 /* PV size must be at least 5 times PE size */
+#define MAX_LE_TOTAL 65534 /* 2^16 - 2 */
+#define MAX_PE_TOTAL ((uint32_t) -2)
+
+#define UNMAPPED_EXTENT 0
+
+/* volume group */
+#define VG_ACTIVE 0x01 /* vg_status */
+#define VG_EXPORTED 0x02 /* " */
+#define VG_EXTENDABLE 0x04 /* " */
+
+#define VG_READ 0x01 /* vg_access */
+#define VG_WRITE 0x02 /* " */
+#define VG_CLUSTERED 0x04 /* " */
+#define VG_SHARED 0x08 /* " */
+
+/* logical volume */
+#define LV_ACTIVE 0x01 /* lv_status */
+#define LV_SPINDOWN 0x02 /* " */
+#define LV_PERSISTENT_MINOR 0x04 /* " */
+
+#define LV_READ 0x01 /* lv_access */
+#define LV_WRITE 0x02 /* " */
+#define LV_SNAPSHOT 0x04 /* " */
+#define LV_SNAPSHOT_ORG 0x08 /* " */
+
+#define LV_BADBLOCK_ON 0x01 /* lv_badblock */
+
+#define LV_STRICT 0x01 /* lv_allocation */
+#define LV_CONTIGUOUS 0x02 /* " */
+
+/* physical volume */
+#define PV_ACTIVE 0x01 /* pv_status */
+#define PV_ALLOCATABLE 0x02 /* pv_allocatable */
+
+#define EXPORTED_TAG "PV_EXP" /* Identifier for exported PV */
+#define IMPORTED_TAG "PV_IMP" /* Identifier for imported PV */
+
+struct data_area {
+ uint32_t base;
+ uint32_t size;
+} __attribute__ ((packed));
+
+struct pv_disk {
+ int8_t id[2];
+ uint16_t version; /* lvm version */
+ struct data_area pv_on_disk;
+ struct data_area vg_on_disk;
+ struct data_area pv_uuidlist_on_disk;
+ struct data_area lv_on_disk;
+ struct data_area pe_on_disk;
+ int8_t pv_uuid[NAME_LEN];
+ int8_t vg_name[NAME_LEN];
+ int8_t system_id[NAME_LEN]; /* for vgexport/vgimport */
+ uint32_t pv_major;
+ uint32_t pv_number;
+ uint32_t pv_status;
+ uint32_t pv_allocatable;
+ uint32_t pv_size;
+ uint32_t lv_cur;
+ uint32_t pe_size;
+ uint32_t pe_total;
+ uint32_t pe_allocated;
+
+ /* only present on version == 2 pv's */
+ uint32_t pe_start;
+} __attribute__ ((packed));
+
+struct lv_disk {
+ int8_t lv_name[NAME_LEN];
+ int8_t vg_name[NAME_LEN];
+ uint32_t lv_access;
+ uint32_t lv_status;
+ uint32_t lv_open;
+ uint32_t lv_dev;
+ uint32_t lv_number;
+ uint32_t lv_mirror_copies; /* for future use */
+ uint32_t lv_recovery; /* " */
+ uint32_t lv_schedule; /* " */
+ uint32_t lv_size;
+ uint32_t lv_snapshot_minor; /* minor number of original */
+ uint16_t lv_chunk_size; /* chunk size of snapshot */
+ uint16_t dummy;
+ uint32_t lv_allocated_le;
+ uint32_t lv_stripes;
+ uint32_t lv_stripesize;
+ uint32_t lv_badblock; /* for future use */
+ uint32_t lv_allocation;
+ uint32_t lv_io_timeout; /* for future use */
+ uint32_t lv_read_ahead;
+} __attribute__ ((packed));
+
+struct vg_disk {
+ int8_t vg_uuid[ID_LEN]; /* volume group UUID */
+ int8_t vg_name_dummy[NAME_LEN - ID_LEN]; /* rest of v1 VG name */
+ uint32_t vg_number; /* volume group number */
+ uint32_t vg_access; /* read/write */
+ uint32_t vg_status; /* active or not */
+ uint32_t lv_max; /* maximum logical volumes */
+ uint32_t lv_cur; /* current logical volumes */
+ uint32_t lv_open; /* open logical volumes */
+ uint32_t pv_max; /* maximum physical volumes */
+ uint32_t pv_cur; /* current physical volumes FU */
+ uint32_t pv_act; /* active physical volumes */
+ uint32_t dummy;
+ uint32_t vgda; /* volume group descriptor arrays FU */
+ uint32_t pe_size; /* physical extent size in sectors */
+ uint32_t pe_total; /* total of physical extents */
+ uint32_t pe_allocated; /* allocated physical extents */
+ uint32_t pvg_total; /* physical volume groups FU */
+} __attribute__ ((packed));
+
+struct pe_disk {
+ uint16_t lv_num;
+ uint16_t le_num;
+} __attribute__ ((packed));
+
+struct uuid_list {
+ struct dm_list list;
+ char uuid[NAME_LEN] __attribute__((aligned(8)));
+};
+
+struct lvd_list {
+ struct dm_list list;
+ struct lv_disk lvd;
+};
+
+struct disk_list {
+ struct dm_list list;
+ struct dm_pool *mem;
+ struct device *dev;
+
+ struct pv_disk pvd __attribute__((aligned(8)));
+ struct vg_disk vgd __attribute__((aligned(8)));
+ struct dm_list uuids __attribute__((aligned(8)));
+ struct dm_list lvds __attribute__((aligned(8)));
+ struct pe_disk *extents __attribute__((aligned(8)));
+};
+
+/*
+ * Layout constants.
+ */
+#define METADATA_ALIGN 4096UL
+#define LVM1_PE_ALIGN (65536UL >> SECTOR_SHIFT) /* PE alignment */
+
+#define METADATA_BASE 0UL
+#define PV_SIZE 1024UL
+#define VG_SIZE 4096UL
+
+/*
+ * Functions to calculate layout info.
+ */
+int calculate_layout(struct disk_list *dl);
+int calculate_extent_count(struct physical_volume *pv, uint32_t extent_size,
+ uint32_t max_extent_count, uint64_t pe_start);
+
+/*
+ * Low level io routines which read/write
+ * disk_lists.
+ */
+
+struct disk_list *read_disk(const struct format_type *fmt, struct device *dev,
+ struct dm_pool *mem, const char *vg_name);
+
+int read_pvs_in_vg(const struct format_type *fmt, const char *vg_name,
+ struct dev_filter *filter,
+ struct dm_pool *mem, struct dm_list *results);
+
+int write_disks(const struct format_type *fmt, struct dm_list *pvds);
+
+/*
+ * Functions to translate to between disk and in
+ * core structures.
+ */
+int import_pv(const struct format_type *fmt, struct dm_pool *mem,
+ struct device *dev, struct volume_group *vg,
+ struct physical_volume *pv, struct pv_disk *pvd,
+ struct vg_disk *vgd);
+int export_pv(struct cmd_context *cmd, struct dm_pool *mem,
+ struct volume_group *vg,
+ struct pv_disk *pvd, struct physical_volume *pv);
+
+int import_vg(struct dm_pool *mem,
+ struct volume_group *vg, struct disk_list *dl);
+int export_vg(struct vg_disk *vgd, struct volume_group *vg);
+
+int import_lv(struct cmd_context *cmd, struct dm_pool *mem,
+ struct logical_volume *lv, struct lv_disk *lvd);
+
+int import_extents(struct cmd_context *cmd, struct volume_group *vg,
+ struct dm_list *pvds);
+int export_extents(struct disk_list *dl, uint32_t lv_num,
+ struct logical_volume *lv, struct physical_volume *pv);
+
+int import_pvs(const struct format_type *fmt, struct dm_pool *mem,
+ struct volume_group *vg, struct dm_list *pvds);
+
+int import_lvs(struct dm_pool *mem, struct volume_group *vg, struct dm_list *pvds);
+int export_lvs(struct disk_list *dl, struct volume_group *vg,
+ struct physical_volume *pv, const char *dev_dir);
+
+int import_snapshots(struct dm_pool *mem, struct volume_group *vg,
+ struct dm_list *pvds);
+
+int export_uuids(struct disk_list *dl, struct volume_group *vg);
+
+void export_numbers(struct dm_list *pvds, struct volume_group *vg);
+
+void export_pv_act(struct dm_list *pvds);
+int munge_pvd(struct device *dev, struct pv_disk *pvd);
+int read_vgd(struct device *dev, struct vg_disk *vgd, struct pv_disk *pvd);
+
+/* blech */
+int get_free_vg_number(struct format_instance *fid, struct dev_filter *filter,
+ const char *candidate_vg, int *result);
+int export_vg_number(struct format_instance *fid, struct dm_list *pvds,
+ const char *vg_name, struct dev_filter *filter);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "disk-rep.h"
+#include "limits.h"
+#include "display.h"
+#include "toolcontext.h"
+#include "lvm1-label.h"
+#include "format1.h"
+#include "segtype.h"
+#include "pv_alloc.h"
+
+/* VG consistency checks */
+static int _check_vgs(struct dm_list *pvs, struct volume_group *vg)
+{
+ struct dm_list *pvh, *t;
+ struct disk_list *dl = NULL;
+ struct disk_list *first = NULL;
+
+ uint32_t pv_count = 0;
+ uint32_t exported = 0;
+ int first_time = 1;
+
+ /*
+ * If there are exported and unexported PVs, ignore exported ones.
+ * This means an active VG won't be affected if disks are inserted
+ * bearing an exported VG with the same name.
+ */
+ dm_list_iterate_items(dl, pvs) {
+ if (first_time) {
+ exported = dl->pvd.pv_status & VG_EXPORTED;
+ first_time = 0;
+ continue;
+ }
+
+ if (exported != (dl->pvd.pv_status & VG_EXPORTED)) {
+ /* Remove exported PVs */
+ dm_list_iterate_safe(pvh, t, pvs) {
+ dl = dm_list_item(pvh, struct disk_list);
+ if (dl->pvd.pv_status & VG_EXPORTED)
+ dm_list_del(pvh);
+ }
+ break;
+ }
+ }
+
+ /* Remove any PVs with VG structs that differ from the first */
+ dm_list_iterate_safe(pvh, t, pvs) {
+ dl = dm_list_item(pvh, struct disk_list);
+
+ if (!first)
+ first = dl;
+
+ else if (memcmp(&first->vgd, &dl->vgd, sizeof(first->vgd))) {
+ log_error("VG data differs between PVs %s and %s",
+ dev_name(first->dev), dev_name(dl->dev));
+ log_debug("VG data on %s: %s %s %" PRIu32 " %" PRIu32
+ " %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
+ PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32
+ " %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
+ PRIu32 " %" PRIu32 " %" PRIu32,
+ dev_name(first->dev), first->vgd.vg_uuid,
+ first->vgd.vg_name_dummy,
+ first->vgd.vg_number, first->vgd.vg_access,
+ first->vgd.vg_status, first->vgd.lv_max,
+ first->vgd.lv_cur, first->vgd.lv_open,
+ first->vgd.pv_max, first->vgd.pv_cur,
+ first->vgd.pv_act, first->vgd.dummy,
+ first->vgd.vgda, first->vgd.pe_size,
+ first->vgd.pe_total, first->vgd.pe_allocated,
+ first->vgd.pvg_total);
+ log_debug("VG data on %s: %s %s %" PRIu32 " %" PRIu32
+ " %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
+ PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32
+ " %" PRIu32 " %" PRIu32 " %" PRIu32 " %"
+ PRIu32 " %" PRIu32 " %" PRIu32,
+ dev_name(dl->dev), dl->vgd.vg_uuid,
+ dl->vgd.vg_name_dummy, dl->vgd.vg_number,
+ dl->vgd.vg_access, dl->vgd.vg_status,
+ dl->vgd.lv_max, dl->vgd.lv_cur,
+ dl->vgd.lv_open, dl->vgd.pv_max,
+ dl->vgd.pv_cur, dl->vgd.pv_act, dl->vgd.dummy,
+ dl->vgd.vgda, dl->vgd.pe_size,
+ dl->vgd.pe_total, dl->vgd.pe_allocated,
+ dl->vgd.pvg_total);
+ dm_list_del(pvh);
+ return 0;
+ }
+ pv_count++;
+ }
+
+ /* On entry to fn, list known to be non-empty */
+ if (pv_count != first->vgd.pv_cur) {
+ log_error("%d PV(s) found for VG %s: expected %d",
+ pv_count, first->pvd.vg_name, first->vgd.pv_cur);
+ vg->status |= PARTIAL_VG;
+ }
+
+ return 1;
+}
+
+static int _fix_partial_vg(struct volume_group *vg, struct dm_list *pvs)
+{
+ uint32_t extent_count = 0;
+ struct disk_list *dl;
+ struct dm_list *pvh;
+ struct pv_list *pvl;
+ struct lv_list *ll;
+ struct lv_segment *seg;
+
+ /*
+ * FIXME: code should remap missing segments to error segment.
+ * Also current mapping code allocates 1 segment per missing extent.
+ * For now bail out completely - allocated structures are not complete
+ */
+ dm_list_iterate_items(ll, &vg->lvs)
+ dm_list_iterate_items(seg, &ll->lv->segments) {
+
+ /* area_count is always 1 here, s == 0 */
+ if (seg_type(seg, 0) != AREA_PV)
+ continue;
+
+ if (seg_pv(seg, 0))
+ continue;
+
+ log_error("Partial mode support for missing lvm1 PVs and "
+ "partially available LVs is currently not implemented.");
+ return 0;
+ }
+
+ dm_list_iterate(pvh, pvs) {
+ dl = dm_list_item(pvh, struct disk_list);
+ extent_count += dl->pvd.pe_total;
+ }
+
+ /* FIXME: move this to one place to pv_manip */
+ if (!(pvl = dm_pool_zalloc(vg->vgmem, sizeof(*pvl))) ||
+ !(pvl->pv = dm_pool_zalloc(vg->vgmem, sizeof(*pvl->pv))))
+ return_0;
+
+ /* Use vg uuid with replaced first chars to "missing" as missing PV UUID */
+ memcpy(&pvl->pv->id.uuid, vg->id.uuid, sizeof(pvl->pv->id.uuid));
+ memcpy(&pvl->pv->id.uuid, "missing", 7);
+
+ if (!(pvl->pv->vg_name = dm_pool_strdup(vg->vgmem, vg->name)))
+ goto_out;
+ memcpy(&pvl->pv->vgid, &vg->id, sizeof(vg->id));
+ pvl->pv->status |= MISSING_PV;
+ dm_list_init(&pvl->pv->tags);
+ dm_list_init(&pvl->pv->segments);
+
+ pvl->pv->pe_size = vg->extent_size;
+ pvl->pv->pe_count = vg->extent_count - extent_count;
+ if (!alloc_pv_segment_whole_pv(vg->vgmem, pvl->pv))
+ goto_out;
+
+ add_pvl_to_vgs(vg, pvl);
+ log_debug("%s: partial VG, allocated missing PV using %d extents.",
+ vg->name, pvl->pv->pe_count);
+
+ return 1;
+out:
+ dm_pool_free(vg->vgmem, pvl);
+ return 0;
+}
+
+static struct volume_group *_build_vg(struct format_instance *fid,
+ struct dm_list *pvs,
+ struct dm_pool *mem)
+{
+ struct volume_group *vg = dm_pool_zalloc(mem, sizeof(*vg));
+ struct disk_list *dl;
+
+ if (!vg)
+ goto_bad;
+
+ if (dm_list_empty(pvs))
+ goto_bad;
+
+ vg->cmd = fid->fmt->cmd;
+ vg->vgmem = mem;
+ vg->fid = fid;
+ vg->seqno = 0;
+ dm_list_init(&vg->pvs);
+ dm_list_init(&vg->lvs);
+ dm_list_init(&vg->tags);
+ dm_list_init(&vg->removed_pvs);
+
+ if (!_check_vgs(pvs, vg))
+ goto_bad;
+
+ dl = dm_list_item(pvs->n, struct disk_list);
+
+ if (!import_vg(mem, vg, dl))
+ goto_bad;
+
+ if (!import_pvs(fid->fmt, mem, vg, pvs))
+ goto_bad;
+
+ if (!import_lvs(mem, vg, pvs))
+ goto_bad;
+
+ if (!import_extents(fid->fmt->cmd, vg, pvs))
+ goto_bad;
+
+ if (!import_snapshots(mem, vg, pvs))
+ goto_bad;
+
+ /* Fix extents counts by adding missing PV if partial VG */
+ if ((vg->status & PARTIAL_VG) && !_fix_partial_vg(vg, pvs))
+ goto_bad;
+
+ return vg;
+
+ bad:
+ dm_pool_free(mem, vg);
+ return NULL;
+}
+
+static struct volume_group *_format1_vg_read(struct format_instance *fid,
+ const char *vg_name,
+ struct metadata_area *mda __attribute__((unused)))
+{
+ struct dm_pool *mem = dm_pool_create("lvm1 vg_read", VG_MEMPOOL_CHUNK);
+ struct dm_list pvs;
+ struct volume_group *vg = NULL;
+ dm_list_init(&pvs);
+
+ if (!mem)
+ return_NULL;
+
+ /* Strip dev_dir if present */
+ vg_name = strip_dir(vg_name, fid->fmt->cmd->dev_dir);
+
+ if (!read_pvs_in_vg
+ (fid->fmt, vg_name, fid->fmt->cmd->filter, mem, &pvs))
+ goto_bad;
+
+ if (!(vg = _build_vg(fid, &pvs, mem)))
+ goto_bad;
+
+ return vg;
+bad:
+ dm_pool_destroy(mem);
+ return NULL;
+}
+
+static struct disk_list *_flatten_pv(struct format_instance *fid,
+ struct dm_pool *mem, struct volume_group *vg,
+ struct physical_volume *pv,
+ const char *dev_dir)
+{
+ struct disk_list *dl = dm_pool_alloc(mem, sizeof(*dl));
+
+ if (!dl)
+ return_NULL;
+
+ dl->mem = mem;
+ dl->dev = pv->dev;
+
+ dm_list_init(&dl->uuids);
+ dm_list_init(&dl->lvds);
+
+ if (!export_pv(fid->fmt->cmd, mem, vg, &dl->pvd, pv) ||
+ !export_vg(&dl->vgd, vg) ||
+ !export_uuids(dl, vg) ||
+ !export_lvs(dl, vg, pv, dev_dir) || !calculate_layout(dl)) {
+ dm_pool_free(mem, dl);
+ return_NULL;
+ }
+
+ return dl;
+}
+
+static int _flatten_vg(struct format_instance *fid, struct dm_pool *mem,
+ struct volume_group *vg,
+ struct dm_list *pvds, const char *dev_dir,
+ struct dev_filter *filter)
+{
+ struct pv_list *pvl;
+ struct disk_list *data;
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (!(data = _flatten_pv(fid, mem, vg, pvl->pv, dev_dir)))
+ return_0;
+
+ dm_list_add(pvds, &data->list);
+ }
+
+ export_numbers(pvds, vg);
+ export_pv_act(pvds);
+
+ if (!export_vg_number(fid, pvds, vg->name, filter))
+ return_0;
+
+ return 1;
+}
+
+static int _format1_vg_write(struct format_instance *fid, struct volume_group *vg,
+ struct metadata_area *mda __attribute__((unused)))
+{
+ struct dm_pool *mem = dm_pool_create("lvm1 vg_write", VG_MEMPOOL_CHUNK);
+ struct dm_list pvds;
+ int r = 0;
+
+ if (!mem)
+ return_0;
+
+ dm_list_init(&pvds);
+
+ r = (_flatten_vg(fid, mem, vg, &pvds, fid->fmt->cmd->dev_dir,
+ fid->fmt->cmd->filter) &&
+ write_disks(fid->fmt, &pvds));
+
+ lvmcache_update_vg(vg, 0);
+ dm_pool_destroy(mem);
+ return r;
+}
+
+static int _format1_pv_read(const struct format_type *fmt, const char *pv_name,
+ struct physical_volume *pv, struct dm_list *mdas __attribute__((unused)),
+ int scan_label_only __attribute__((unused)))
+{
+ struct dm_pool *mem = dm_pool_create("lvm1 pv_read", 1024);
+ struct disk_list *dl;
+ struct device *dev;
+ int r = 0;
+
+ log_very_verbose("Reading physical volume data %s from disk", pv_name);
+
+ if (!mem)
+ return_0;
+
+ if (!(dev = dev_cache_get(pv_name, fmt->cmd->filter)))
+ goto_out;
+
+ if (!(dl = read_disk(fmt, dev, mem, NULL)))
+ goto_out;
+
+ if (!import_pv(fmt, fmt->cmd->mem, dl->dev, NULL, pv, &dl->pvd, &dl->vgd))
+ goto_out;
+
+ pv->fmt = fmt;
+
+ r = 1;
+
+ out:
+ dm_pool_destroy(mem);
+ return r;
+}
+
+static int _format1_pv_setup(const struct format_type *fmt,
+ uint64_t pe_start, uint32_t extent_count,
+ uint32_t extent_size,
+ unsigned long data_alignment __attribute__((unused)),
+ unsigned long data_alignment_offset __attribute__((unused)),
+ int pvmetadatacopies __attribute__((unused)),
+ uint64_t pvmetadatasize __attribute__((unused)),
+ unsigned metadataignore __attribute__((unused)),
+ struct dm_list *mdas __attribute__((unused)),
+ struct physical_volume *pv, struct volume_group *vg __attribute__((unused)))
+{
+ if (pv->size > MAX_PV_SIZE)
+ pv->size--;
+ if (pv->size > MAX_PV_SIZE) {
+ log_error("Physical volumes cannot be bigger than %s",
+ display_size(fmt->cmd, (uint64_t) MAX_PV_SIZE));
+ return 0;
+ }
+
+ /* Nothing more to do if extent size isn't provided */
+ if (!extent_size)
+ return 1;
+
+ /*
+ * This works out pe_start and pe_count.
+ */
+ if (!calculate_extent_count(pv, extent_size, extent_count, pe_start))
+ return_0;
+
+ /* Retain existing extent locations exactly */
+ if (((pe_start || extent_count) && (pe_start != pv->pe_start)) ||
+ (extent_count && (extent_count != pv->pe_count))) {
+ log_error("Metadata would overwrite physical extents");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _format1_lv_setup(struct format_instance *fid, struct logical_volume *lv)
+{
+ uint64_t max_size = UINT_MAX;
+
+ if (!*lv->lvid.s)
+ lvid_from_lvnum(&lv->lvid, &lv->vg->id, find_free_lvnum(lv));
+
+ if (lv->le_count > MAX_LE_TOTAL) {
+ log_error("logical volumes cannot contain more than "
+ "%d extents.", MAX_LE_TOTAL);
+ return 0;
+ }
+ if (lv->size > max_size) {
+ log_error("logical volumes cannot be larger than %s",
+ display_size(fid->fmt->cmd, max_size));
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _format1_pv_write(const struct format_type *fmt, struct physical_volume *pv,
+ struct dm_list *mdas __attribute__((unused)), int64_t sector __attribute__((unused)))
+{
+ struct dm_pool *mem;
+ struct disk_list *dl;
+ struct dm_list pvs;
+ struct lvmcache_info *info;
+
+ if (!(info = lvmcache_add(fmt->labeller, (char *) &pv->id, pv->dev,
+ pv->vg_name, NULL, 0)))
+ return_0;
+
+ info->device_size = pv->size << SECTOR_SHIFT;
+ info->fmt = fmt;
+
+ dm_list_init(&info->mdas);
+
+ dm_list_init(&pvs);
+
+ /* Ensure any residual PE structure is gone */
+ pv->pe_size = pv->pe_count = 0;
+ pv->pe_start = LVM1_PE_ALIGN;
+
+ if (!(mem = dm_pool_create("lvm1 pv_write", 1024)))
+ return_0;
+
+ if (!(dl = dm_pool_alloc(mem, sizeof(*dl))))
+ goto_bad;
+
+ dl->mem = mem;
+ dl->dev = pv->dev;
+
+ if (!export_pv(fmt->cmd, mem, NULL, &dl->pvd, pv))
+ goto_bad;
+
+ /* must be set to be able to zero gap after PV structure in
+ dev_write in order to make other disk tools happy */
+ dl->pvd.pv_on_disk.base = METADATA_BASE;
+ dl->pvd.pv_on_disk.size = PV_SIZE;
+ dl->pvd.pe_on_disk.base = LVM1_PE_ALIGN << SECTOR_SHIFT;
+
+ dm_list_add(&pvs, &dl->list);
+ if (!write_disks(fmt, &pvs))
+ goto_bad;
+
+ dm_pool_destroy(mem);
+ return 1;
+
+ bad:
+ dm_pool_destroy(mem);
+ return 0;
+}
+
+static int _format1_vg_setup(struct format_instance *fid, struct volume_group *vg)
+{
+ /* just check max_pv and max_lv */
+ if (!vg->max_lv || vg->max_lv >= MAX_LV)
+ vg->max_lv = MAX_LV - 1;
+
+ if (!vg->max_pv || vg->max_pv >= MAX_PV)
+ vg->max_pv = MAX_PV - 1;
+
+ if (vg->extent_size > MAX_PE_SIZE || vg->extent_size < MIN_PE_SIZE) {
+ log_error("Extent size must be between %s and %s",
+ display_size(fid->fmt->cmd, (uint64_t) MIN_PE_SIZE),
+ display_size(fid->fmt->cmd, (uint64_t) MAX_PE_SIZE));
+
+ return 0;
+ }
+
+ if (vg->extent_size % MIN_PE_SIZE) {
+ log_error("Extent size must be multiple of %s",
+ display_size(fid->fmt->cmd, (uint64_t) MIN_PE_SIZE));
+ return 0;
+ }
+
+ /* Redundant? */
+ if (vg->extent_size & (vg->extent_size - 1)) {
+ log_error("Extent size must be power of 2");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _format1_segtype_supported(struct format_instance *fid __attribute__((unused)),
+ const struct segment_type *segtype)
+{
+ if (!(segtype->flags & SEG_FORMAT1_SUPPORT))
+ return_0;
+
+ return 1;
+}
+
+static struct metadata_area_ops _metadata_format1_ops = {
+ .vg_read = _format1_vg_read,
+ .vg_write = _format1_vg_write,
+};
+
+static struct format_instance *_format1_create_instance(const struct format_type *fmt,
+ const char *vgname __attribute__((unused)),
+ const char *vgid __attribute__((unused)),
+ void *private __attribute__((unused)))
+{
+ struct format_instance *fid;
+ struct metadata_area *mda;
+
+ if (!(fid = dm_pool_alloc(fmt->cmd->mem, sizeof(*fid))))
+ return_NULL;
+
+ fid->fmt = fmt;
+ dm_list_init(&fid->metadata_areas_in_use);
+ dm_list_init(&fid->metadata_areas_ignored);
+
+ /* Define a NULL metadata area */
+ if (!(mda = dm_pool_zalloc(fmt->cmd->mem, sizeof(*mda)))) {
+ dm_pool_free(fmt->cmd->mem, fid);
+ return_NULL;
+ }
+
+ mda->ops = &_metadata_format1_ops;
+ mda->metadata_locn = NULL;
+ mda->status = 0;
+ dm_list_add(&fid->metadata_areas_in_use, &mda->list);
+
+ return fid;
+}
+
+static void _format1_destroy_instance(struct format_instance *fid __attribute__((unused)))
+{
+}
+
+static void _format1_destroy(struct format_type *fmt)
+{
+ dm_free(fmt);
+}
+
+static struct format_handler _format1_ops = {
+ .pv_read = _format1_pv_read,
+ .pv_setup = _format1_pv_setup,
+ .pv_write = _format1_pv_write,
+ .lv_setup = _format1_lv_setup,
+ .vg_setup = _format1_vg_setup,
+ .segtype_supported = _format1_segtype_supported,
+ .create_instance = _format1_create_instance,
+ .destroy_instance = _format1_destroy_instance,
+ .destroy = _format1_destroy,
+};
+
+#ifdef LVM1_INTERNAL
+struct format_type *init_lvm1_format(struct cmd_context *cmd)
+#else /* Shared */
+struct format_type *init_format(struct cmd_context *cmd);
+struct format_type *init_format(struct cmd_context *cmd)
+#endif
+{
+ struct format_type *fmt = dm_malloc(sizeof(*fmt));
+
+ if (!fmt)
+ return_NULL;
+
+ fmt->cmd = cmd;
+ fmt->ops = &_format1_ops;
+ fmt->name = FMT_LVM1_NAME;
+ fmt->alias = NULL;
+ fmt->orphan_vg_name = FMT_LVM1_ORPHAN_VG_NAME;
+ fmt->features = FMT_RESTRICTED_LVIDS | FMT_ORPHAN_ALLOCATABLE |
+ FMT_RESTRICTED_READAHEAD;
+ fmt->private = NULL;
+
+ if (!(fmt->labeller = lvm1_labeller_create(fmt))) {
+ log_error("Couldn't create lvm1 label handler.");
+ return NULL;
+ }
+
+ if (!(label_register_handler(FMT_LVM1_NAME, fmt->labeller))) {
+ log_error("Couldn't register lvm1 label handler.");
+ return NULL;
+ }
+
+ log_very_verbose("Initialised format: %s", fmt->name);
+
+ return fmt;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_FORMAT1_H
+#define _LVM_FORMAT1_H
+
+#include "metadata.h"
+#include "lvmcache.h"
+
+#define FMT_LVM1_NAME "lvm1"
+#define FMT_LVM1_ORPHAN_VG_NAME ORPHAN_VG_NAME(FMT_LVM1_NAME)
+
+#ifdef LVM1_INTERNAL
+struct format_type *init_lvm1_format(struct cmd_context *cmd);
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Translates between disk and in-core formats.
+ */
+
+#include "lib.h"
+#include "disk-rep.h"
+#include "lvm-string.h"
+#include "filter.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "pv_alloc.h"
+#include "display.h"
+#include "lvmcache.h"
+#include "metadata.h"
+
+#include <time.h>
+
+static int _check_vg_name(const char *name)
+{
+ return strlen(name) < NAME_LEN;
+}
+
+/*
+ * Extracts the last part of a path.
+ */
+static char *_create_lv_name(struct dm_pool *mem, const char *full_name)
+{
+ const char *ptr = strrchr(full_name, '/');
+
+ if (!ptr)
+ ptr = full_name;
+ else
+ ptr++;
+
+ return dm_pool_strdup(mem, ptr);
+}
+
+int import_pv(const struct format_type *fmt, struct dm_pool *mem,
+ struct device *dev, struct volume_group *vg,
+ struct physical_volume *pv, struct pv_disk *pvd,
+ struct vg_disk *vgd)
+{
+ uint64_t size;
+
+ memset(pv, 0, sizeof(*pv));
+ memcpy(&pv->id, pvd->pv_uuid, ID_LEN);
+
+ pv->dev = dev;
+ if (!*pvd->vg_name)
+ pv->vg_name = fmt->orphan_vg_name;
+ else if (!(pv->vg_name = dm_pool_strdup(mem, (char *)pvd->vg_name))) {
+ log_error("Volume Group name allocation failed.");
+ return 0;
+ }
+
+ memcpy(&pv->vgid, vgd->vg_uuid, sizeof(vg->id));
+
+ /* Store system_id from first PV if PV belongs to a VG */
+ if (vg && !*vg->system_id)
+ strncpy(vg->system_id, (char *)pvd->system_id, NAME_LEN);
+
+ if (vg &&
+ strncmp(vg->system_id, (char *)pvd->system_id, sizeof(pvd->system_id)))
+ log_very_verbose("System ID %s on %s differs from %s for "
+ "volume group", pvd->system_id,
+ pv_dev_name(pv), vg->system_id);
+
+ /*
+ * If exported, we still need to flag in pv->status too because
+ * we don't always have a struct volume_group when we need this.
+ */
+ if (pvd->pv_status & VG_EXPORTED)
+ pv->status |= EXPORTED_VG;
+
+ if (pvd->pv_allocatable)
+ pv->status |= ALLOCATABLE_PV;
+
+ pv->size = pvd->pv_size;
+ pv->pe_size = pvd->pe_size;
+ pv->pe_start = pvd->pe_start;
+ pv->pe_count = pvd->pe_total;
+ pv->pe_alloc_count = 0;
+ pv->pe_align = 0;
+
+ /* Fix up pv size if missing or impossibly large */
+ if (!pv->size || pv->size > (1ULL << 62)) {
+ if (!dev_get_size(dev, &pv->size)) {
+ log_error("%s: Couldn't get size.", pv_dev_name(pv));
+ return 0;
+ }
+ log_verbose("Fixing up missing format1 size (%s) "
+ "for PV %s", display_size(fmt->cmd, pv->size),
+ pv_dev_name(pv));
+ if (vg) {
+ size = pv->pe_count * (uint64_t) vg->extent_size +
+ pv->pe_start;
+ if (size > pv->size)
+ log_warn("WARNING: Physical Volume %s is too "
+ "large for underlying device",
+ pv_dev_name(pv));
+ }
+ }
+
+ dm_list_init(&pv->tags);
+ dm_list_init(&pv->segments);
+
+ if (!alloc_pv_segment_whole_pv(mem, pv))
+ return_0;
+
+ return 1;
+}
+
+static int _system_id(struct cmd_context *cmd, char *s, const char *prefix)
+{
+
+ if (dm_snprintf(s, NAME_LEN, "%s%s%lu",
+ prefix, cmd->hostname, time(NULL)) < 0) {
+ log_error("Generated system_id too long");
+ return 0;
+ }
+
+ return 1;
+}
+
+int export_pv(struct cmd_context *cmd, struct dm_pool *mem __attribute__((unused)),
+ struct volume_group *vg,
+ struct pv_disk *pvd, struct physical_volume *pv)
+{
+ memset(pvd, 0, sizeof(*pvd));
+
+ pvd->id[0] = 'H';
+ pvd->id[1] = 'M';
+ pvd->version = 1;
+
+ memcpy(pvd->pv_uuid, pv->id.uuid, ID_LEN);
+
+ if (pv->vg_name && !is_orphan(pv)) {
+ if (!_check_vg_name(pv->vg_name))
+ return_0;
+ strncpy((char *)pvd->vg_name, pv->vg_name, sizeof(pvd->vg_name));
+ }
+
+ /* Preserve existing system_id if it exists */
+ if (vg && *vg->system_id)
+ strncpy((char *)pvd->system_id, vg->system_id, sizeof(pvd->system_id));
+
+ /* Is VG already exported or being exported? */
+ if (vg && vg_is_exported(vg)) {
+ /* Does system_id need setting? */
+ if (!*vg->system_id ||
+ strncmp(vg->system_id, EXPORTED_TAG,
+ sizeof(EXPORTED_TAG) - 1)) {
+ if (!_system_id(cmd, (char *)pvd->system_id, EXPORTED_TAG))
+ return_0;
+ }
+ if (strlen((char *)pvd->vg_name) + sizeof(EXPORTED_TAG) >
+ sizeof(pvd->vg_name)) {
+ log_error("Volume group name %s too long to export",
+ pvd->vg_name);
+ return 0;
+ }
+ strcat((char *)pvd->vg_name, EXPORTED_TAG);
+ }
+
+ /* Is VG being imported? */
+ if (vg && !vg_is_exported(vg) && *vg->system_id &&
+ !strncmp(vg->system_id, EXPORTED_TAG, sizeof(EXPORTED_TAG) - 1)) {
+ if (!_system_id(cmd, (char *)pvd->system_id, IMPORTED_TAG))
+ return_0;
+ }
+
+ /* Generate system_id if PV is in VG */
+ if (!pvd->system_id[0])
+ if (!_system_id(cmd, (char *)pvd->system_id, ""))
+ return_0;
+
+ /* Update internal system_id if we changed it */
+ if (vg &&
+ (!*vg->system_id ||
+ strncmp(vg->system_id, (char *)pvd->system_id, sizeof(pvd->system_id))))
+ strncpy(vg->system_id, (char *)pvd->system_id, NAME_LEN);
+
+ //pvd->pv_major = MAJOR(pv->dev);
+
+ if (pv->status & ALLOCATABLE_PV)
+ pvd->pv_allocatable = PV_ALLOCATABLE;
+
+ pvd->pv_size = pv->size;
+ pvd->lv_cur = 0; /* this is set when exporting the lv list */
+ if (vg)
+ pvd->pe_size = vg->extent_size;
+ else
+ pvd->pe_size = pv->pe_size;
+ pvd->pe_total = pv->pe_count;
+ pvd->pe_allocated = pv->pe_alloc_count;
+ pvd->pe_start = pv->pe_start;
+
+ return 1;
+}
+
+int import_vg(struct dm_pool *mem,
+ struct volume_group *vg, struct disk_list *dl)
+{
+ struct vg_disk *vgd = &dl->vgd;
+ memcpy(vg->id.uuid, vgd->vg_uuid, ID_LEN);
+
+ if (!_check_vg_name((char *)dl->pvd.vg_name))
+ return_0;
+
+ if (!(vg->name = dm_pool_strdup(mem, (char *)dl->pvd.vg_name)))
+ return_0;
+
+ if (!(vg->system_id = dm_pool_alloc(mem, NAME_LEN)))
+ return_0;
+
+ *vg->system_id = '\0';
+
+ if (vgd->vg_status & VG_EXPORTED)
+ vg->status |= EXPORTED_VG;
+
+ if (vgd->vg_status & VG_EXTENDABLE)
+ vg->status |= RESIZEABLE_VG;
+
+ if (vgd->vg_access & VG_READ)
+ vg->status |= LVM_READ;
+
+ if (vgd->vg_access & VG_WRITE)
+ vg->status |= LVM_WRITE;
+
+ if (vgd->vg_access & VG_CLUSTERED)
+ vg->status |= CLUSTERED;
+
+ if (vgd->vg_access & VG_SHARED)
+ vg->status |= SHARED;
+
+ vg->extent_size = vgd->pe_size;
+ vg->extent_count = vgd->pe_total;
+ vg->free_count = vgd->pe_total;
+ vg->max_lv = vgd->lv_max;
+ vg->max_pv = vgd->pv_max;
+ vg->alloc = ALLOC_NORMAL;
+
+ return 1;
+}
+
+int export_vg(struct vg_disk *vgd, struct volume_group *vg)
+{
+ memset(vgd, 0, sizeof(*vgd));
+ memcpy(vgd->vg_uuid, vg->id.uuid, ID_LEN);
+
+ if (vg->status & LVM_READ)
+ vgd->vg_access |= VG_READ;
+
+ if (vg->status & LVM_WRITE)
+ vgd->vg_access |= VG_WRITE;
+
+ if (vg_is_clustered(vg))
+ vgd->vg_access |= VG_CLUSTERED;
+
+ if (vg->status & SHARED)
+ vgd->vg_access |= VG_SHARED;
+
+ if (vg_is_exported(vg))
+ vgd->vg_status |= VG_EXPORTED;
+
+ if (vg_is_resizeable(vg))
+ vgd->vg_status |= VG_EXTENDABLE;
+
+ vgd->lv_max = vg->max_lv;
+ vgd->lv_cur = vg_visible_lvs(vg) + snapshot_count(vg);
+
+ vgd->pv_max = vg->max_pv;
+ vgd->pv_cur = vg->pv_count;
+
+ vgd->pe_size = vg->extent_size;
+ vgd->pe_total = vg->extent_count;
+ vgd->pe_allocated = vg->extent_count - vg->free_count;
+
+ return 1;
+}
+
+int import_lv(struct cmd_context *cmd, struct dm_pool *mem,
+ struct logical_volume *lv, struct lv_disk *lvd)
+{
+ if (!(lv->name = _create_lv_name(mem, (char *)lvd->lv_name)))
+ return_0;
+
+ lv->status |= VISIBLE_LV;
+
+ if (lvd->lv_status & LV_SPINDOWN)
+ lv->status |= SPINDOWN_LV;
+
+ if (lvd->lv_status & LV_PERSISTENT_MINOR) {
+ lv->status |= FIXED_MINOR;
+ lv->minor = MINOR(lvd->lv_dev);
+ lv->major = MAJOR(lvd->lv_dev);
+ } else {
+ lv->major = -1;
+ lv->minor = -1;
+ }
+
+ if (lvd->lv_access & LV_READ)
+ lv->status |= LVM_READ;
+
+ if (lvd->lv_access & LV_WRITE)
+ lv->status |= LVM_WRITE;
+
+ if (lvd->lv_badblock)
+ lv->status |= BADBLOCK_ON;
+
+ /* Drop the unused LV_STRICT here */
+ if (lvd->lv_allocation & LV_CONTIGUOUS)
+ lv->alloc = ALLOC_CONTIGUOUS;
+ else
+ lv->alloc = ALLOC_NORMAL;
+
+ if (!lvd->lv_read_ahead)
+ lv->read_ahead = cmd->default_settings.read_ahead;
+ else
+ lv->read_ahead = lvd->lv_read_ahead;
+
+ lv->size = lvd->lv_size;
+ lv->le_count = lvd->lv_allocated_le;
+
+ return 1;
+}
+
+static void _export_lv(struct lv_disk *lvd, struct volume_group *vg,
+ struct logical_volume *lv, const char *dev_dir)
+{
+ memset(lvd, 0, sizeof(*lvd));
+ snprintf((char *)lvd->lv_name, sizeof(lvd->lv_name), "%s%s/%s",
+ dev_dir, vg->name, lv->name);
+
+ strcpy((char *)lvd->vg_name, vg->name);
+
+ if (lv->status & LVM_READ)
+ lvd->lv_access |= LV_READ;
+
+ if (lv->status & LVM_WRITE)
+ lvd->lv_access |= LV_WRITE;
+
+ if (lv->status & SPINDOWN_LV)
+ lvd->lv_status |= LV_SPINDOWN;
+
+ if (lv->status & FIXED_MINOR) {
+ lvd->lv_status |= LV_PERSISTENT_MINOR;
+ lvd->lv_dev = MKDEV(lv->major, lv->minor);
+ } else {
+ lvd->lv_dev = MKDEV(LVM_BLK_MAJOR, lvnum_from_lvid(&lv->lvid));
+ }
+
+ if (lv->read_ahead == DM_READ_AHEAD_AUTO ||
+ lv->read_ahead == DM_READ_AHEAD_NONE)
+ lvd->lv_read_ahead = 0;
+ else
+ lvd->lv_read_ahead = lv->read_ahead;
+
+ lvd->lv_stripes =
+ dm_list_item(lv->segments.n, struct lv_segment)->area_count;
+ lvd->lv_stripesize =
+ dm_list_item(lv->segments.n, struct lv_segment)->stripe_size;
+
+ lvd->lv_size = lv->size;
+ lvd->lv_allocated_le = lv->le_count;
+
+ if (lv->status & BADBLOCK_ON)
+ lvd->lv_badblock = LV_BADBLOCK_ON;
+
+ if (lv->alloc == ALLOC_CONTIGUOUS)
+ lvd->lv_allocation |= LV_CONTIGUOUS;
+}
+
+int export_extents(struct disk_list *dl, uint32_t lv_num,
+ struct logical_volume *lv, struct physical_volume *pv)
+{
+ struct pe_disk *ped;
+ struct lv_segment *seg;
+ uint32_t pe, s;
+
+ dm_list_iterate_items(seg, &lv->segments) {
+ for (s = 0; s < seg->area_count; s++) {
+ if (!(seg->segtype->flags & SEG_FORMAT1_SUPPORT)) {
+ log_error("Segment type %s in LV %s: "
+ "unsupported by format1",
+ seg->segtype->name, lv->name);
+ return 0;
+ }
+ if (seg_type(seg, s) != AREA_PV) {
+ log_error("Non-PV stripe found in LV %s: "
+ "unsupported by format1", lv->name);
+ return 0;
+ }
+ if (seg_pv(seg, s) != pv)
+ continue; /* not our pv */
+
+ for (pe = 0; pe < (seg->len / seg->area_count); pe++) {
+ ped = &dl->extents[pe + seg_pe(seg, s)];
+ ped->lv_num = lv_num;
+ ped->le_num = (seg->le / seg->area_count) + pe +
+ s * (lv->le_count / seg->area_count);
+ }
+ }
+ }
+
+ return 1;
+}
+
+int import_pvs(const struct format_type *fmt, struct dm_pool *mem,
+ struct volume_group *vg, struct dm_list *pvds)
+{
+ struct disk_list *dl;
+ struct pv_list *pvl;
+
+ vg->pv_count = 0;
+ dm_list_iterate_items(dl, pvds) {
+ if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl))) ||
+ !(pvl->pv = dm_pool_alloc(mem, sizeof(*pvl->pv))))
+ return_0;
+
+ if (!import_pv(fmt, mem, dl->dev, vg, pvl->pv, &dl->pvd, &dl->vgd))
+ return_0;
+
+ pvl->pv->fmt = fmt;
+ add_pvl_to_vgs(vg, pvl);
+ }
+
+ return 1;
+}
+
+static struct logical_volume *_add_lv(struct dm_pool *mem,
+ struct volume_group *vg,
+ struct lv_disk *lvd)
+{
+ struct logical_volume *lv;
+
+ if (!(lv = alloc_lv(mem)))
+ return_NULL;
+
+ lvid_from_lvnum(&lv->lvid, &vg->id, lvd->lv_number);
+
+ if (!import_lv(vg->cmd, mem, lv, lvd))
+ goto_bad;
+
+ if (!link_lv_to_vg(vg, lv))
+ goto_bad;
+
+ return lv;
+bad:
+ dm_pool_free(mem, lv);
+ return NULL;
+}
+
+int import_lvs(struct dm_pool *mem, struct volume_group *vg, struct dm_list *pvds)
+{
+ struct disk_list *dl;
+ struct lvd_list *ll;
+ struct lv_disk *lvd;
+
+ dm_list_iterate_items(dl, pvds) {
+ dm_list_iterate_items(ll, &dl->lvds) {
+ lvd = &ll->lvd;
+
+ if (!find_lv(vg, (char *)lvd->lv_name) &&
+ !_add_lv(mem, vg, lvd))
+ return_0;
+ }
+ }
+
+ return 1;
+}
+
+/* FIXME: tidy */
+int export_lvs(struct disk_list *dl, struct volume_group *vg,
+ struct physical_volume *pv, const char *dev_dir)
+{
+ int r = 0;
+ struct lv_list *ll;
+ struct lvd_list *lvdl;
+ size_t len;
+ uint32_t lv_num;
+ struct dm_hash_table *lvd_hash;
+
+ if (!_check_vg_name(vg->name))
+ return_0;
+
+ if (!(lvd_hash = dm_hash_create(32)))
+ return_0;
+
+ /*
+ * setup the pv's extents array
+ */
+ len = sizeof(struct pe_disk) * dl->pvd.pe_total;
+ if (!(dl->extents = dm_pool_zalloc(dl->mem, len)))
+ goto_out;
+
+ dm_list_iterate_items(ll, &vg->lvs) {
+ if (ll->lv->status & SNAPSHOT)
+ continue;
+
+ if (!(lvdl = dm_pool_alloc(dl->mem, sizeof(*lvdl))))
+ goto_out;
+
+ _export_lv(&lvdl->lvd, vg, ll->lv, dev_dir);
+
+ lv_num = lvnum_from_lvid(&ll->lv->lvid);
+ lvdl->lvd.lv_number = lv_num;
+
+ if (!dm_hash_insert(lvd_hash, ll->lv->name, &lvdl->lvd))
+ goto_out;
+
+ if (!export_extents(dl, lv_num + 1, ll->lv, pv))
+ goto_out;
+
+ if (lv_is_origin(ll->lv))
+ lvdl->lvd.lv_access |= LV_SNAPSHOT_ORG;
+
+ if (lv_is_cow(ll->lv)) {
+ lvdl->lvd.lv_access |= LV_SNAPSHOT;
+ lvdl->lvd.lv_chunk_size = ll->lv->snapshot->chunk_size;
+ lvdl->lvd.lv_snapshot_minor =
+ lvnum_from_lvid(&ll->lv->snapshot->origin->lvid);
+ }
+
+ dm_list_add(&dl->lvds, &lvdl->list);
+ dl->pvd.lv_cur++;
+ }
+
+ r = 1;
+
+ out:
+ dm_hash_destroy(lvd_hash);
+ return r;
+}
+
+/*
+ * FIXME: More inefficient code.
+ */
+int import_snapshots(struct dm_pool *mem __attribute__((unused)), struct volume_group *vg,
+ struct dm_list *pvds)
+{
+ struct logical_volume *lvs[MAX_LV];
+ struct disk_list *dl;
+ struct lvd_list *ll;
+ struct lv_disk *lvd;
+ int lvnum;
+ struct logical_volume *org, *cow;
+
+ /* build an index of lv numbers */
+ memset(lvs, 0, sizeof(lvs));
+ dm_list_iterate_items(dl, pvds) {
+ dm_list_iterate_items(ll, &dl->lvds) {
+ lvd = &ll->lvd;
+
+ lvnum = lvd->lv_number;
+
+ if (lvnum >= MAX_LV) {
+ log_error("Logical volume number "
+ "out of bounds.");
+ return 0;
+ }
+
+ if (!lvs[lvnum] &&
+ !(lvs[lvnum] = find_lv(vg, (char *)lvd->lv_name))) {
+ log_error("Couldn't find logical volume '%s'.",
+ lvd->lv_name);
+ return 0;
+ }
+ }
+ }
+
+ /*
+ * Now iterate through yet again adding the snapshots.
+ */
+ dm_list_iterate_items(dl, pvds) {
+ dm_list_iterate_items(ll, &dl->lvds) {
+ lvd = &ll->lvd;
+
+ if (!(lvd->lv_access & LV_SNAPSHOT))
+ continue;
+
+ lvnum = lvd->lv_number;
+ cow = lvs[lvnum];
+ if (!(org = lvs[lvd->lv_snapshot_minor])) {
+ log_error("Couldn't find origin logical volume "
+ "for snapshot '%s'.", lvd->lv_name);
+ return 0;
+ }
+
+ /* we may have already added this snapshot */
+ if (lv_is_cow(cow))
+ continue;
+
+ /* insert the snapshot */
+ if (!vg_add_snapshot(org, cow, NULL,
+ org->le_count,
+ lvd->lv_chunk_size)) {
+ log_error("Couldn't add snapshot.");
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+int export_uuids(struct disk_list *dl, struct volume_group *vg)
+{
+ struct uuid_list *ul;
+ struct pv_list *pvl;
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (!(ul = dm_pool_alloc(dl->mem, sizeof(*ul))))
+ return_0;
+
+ memset(ul->uuid, 0, sizeof(ul->uuid));
+ memcpy(ul->uuid, pvl->pv->id.uuid, ID_LEN);
+
+ dm_list_add(&dl->uuids, &ul->list);
+ }
+ return 1;
+}
+
+/*
+ * This calculates the nasty pv_number field
+ * used by LVM1.
+ */
+void export_numbers(struct dm_list *pvds, struct volume_group *vg __attribute__((unused)))
+{
+ struct disk_list *dl;
+ int pv_num = 1;
+
+ dm_list_iterate_items(dl, pvds)
+ dl->pvd.pv_number = pv_num++;
+}
+
+/*
+ * Calculate vg_disk->pv_act.
+ */
+void export_pv_act(struct dm_list *pvds)
+{
+ struct disk_list *dl;
+ int act = 0;
+
+ dm_list_iterate_items(dl, pvds)
+ if (dl->pvd.pv_status & PV_ACTIVE)
+ act++;
+
+ dm_list_iterate_items(dl, pvds)
+ dl->vgd.pv_act = act;
+}
+
+int export_vg_number(struct format_instance *fid, struct dm_list *pvds,
+ const char *vg_name, struct dev_filter *filter)
+{
+ struct disk_list *dl;
+ int vg_num;
+
+ if (!get_free_vg_number(fid, filter, vg_name, &vg_num))
+ return_0;
+
+ dm_list_iterate_items(dl, pvds)
+ dl->vgd.vg_number = vg_num;
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "disk-rep.h"
+#include "lv_alloc.h"
+#include "display.h"
+#include "segtype.h"
+
+/*
+ * After much thought I have decided it is easier,
+ * and probably no less efficient, to convert the
+ * pe->le map to a full le->pe map, and then
+ * process this to get the segments form that
+ * we're after. Any code which goes directly from
+ * the pe->le map to segments would be gladly
+ * accepted, if it is less complicated than this
+ * file.
+ */
+struct pe_specifier {
+ struct physical_volume *pv;
+ uint32_t pe;
+};
+
+struct lv_map {
+ struct logical_volume *lv;
+ uint32_t stripes;
+ uint32_t stripe_size;
+ struct pe_specifier *map;
+};
+
+static struct dm_hash_table *_create_lv_maps(struct dm_pool *mem,
+ struct volume_group *vg)
+{
+ struct dm_hash_table *maps = dm_hash_create(32);
+ struct lv_list *ll;
+ struct lv_map *lvm;
+
+ if (!maps) {
+ log_error("Unable to create hash table for holding "
+ "extent maps.");
+ return NULL;
+ }
+
+ dm_list_iterate_items(ll, &vg->lvs) {
+ if (ll->lv->status & SNAPSHOT)
+ continue;
+
+ if (!(lvm = dm_pool_alloc(mem, sizeof(*lvm))))
+ goto_bad;
+
+ lvm->lv = ll->lv;
+ if (!(lvm->map = dm_pool_zalloc(mem, sizeof(*lvm->map)
+ * ll->lv->le_count)))
+ goto_bad;
+
+ if (!dm_hash_insert(maps, ll->lv->name, lvm))
+ goto_bad;
+ }
+
+ return maps;
+
+ bad:
+ dm_hash_destroy(maps);
+ return NULL;
+}
+
+static int _fill_lv_array(struct lv_map **lvs,
+ struct dm_hash_table *maps, struct disk_list *dl)
+{
+ struct lvd_list *ll;
+ struct lv_map *lvm;
+
+ memset(lvs, 0, sizeof(*lvs) * MAX_LV);
+
+ dm_list_iterate_items(ll, &dl->lvds) {
+ if (!(lvm = dm_hash_lookup(maps, strrchr((char *)ll->lvd.lv_name, '/')
+ + 1))) {
+ log_error("Physical volume (%s) contains an "
+ "unknown logical volume (%s).",
+ dev_name(dl->dev), ll->lvd.lv_name);
+ return 0;
+ }
+
+ lvm->stripes = ll->lvd.lv_stripes;
+ lvm->stripe_size = ll->lvd.lv_stripesize;
+
+ lvs[ll->lvd.lv_number] = lvm;
+ }
+
+ return 1;
+}
+
+static int _fill_maps(struct dm_hash_table *maps, struct volume_group *vg,
+ struct dm_list *pvds)
+{
+ struct disk_list *dl;
+ struct physical_volume *pv;
+ struct lv_map *lvms[MAX_LV], *lvm;
+ struct pe_disk *e;
+ uint32_t i, lv_num, le;
+
+ dm_list_iterate_items(dl, pvds) {
+ pv = find_pv(vg, dl->dev);
+ e = dl->extents;
+
+ /* build an array of lv's for this pv */
+ if (!_fill_lv_array(lvms, maps, dl))
+ return_0;
+
+ for (i = 0; i < dl->pvd.pe_total; i++) {
+ lv_num = e[i].lv_num;
+
+ if (lv_num == UNMAPPED_EXTENT)
+ continue;
+
+ else {
+ lv_num--;
+ lvm = lvms[lv_num];
+
+ if (!lvm) {
+ log_error("Invalid LV in extent map "
+ "(PV %s, PE %" PRIu32
+ ", LV %" PRIu32
+ ", LE %" PRIu32 ")",
+ dev_name(pv->dev), i,
+ lv_num, e[i].le_num);
+ return 0;
+ }
+
+ le = e[i].le_num;
+
+ if (le >= lvm->lv->le_count) {
+ log_error("logical extent number "
+ "out of bounds");
+ return 0;
+ }
+
+ if (lvm->map[le].pv) {
+ log_error("logical extent (%u) "
+ "already mapped.", le);
+ return 0;
+ }
+
+ lvm->map[le].pv = pv;
+ lvm->map[le].pe = i;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int _check_single_map(struct lv_map *lvm)
+{
+ uint32_t i;
+
+ for (i = 0; i < lvm->lv->le_count; i++) {
+ if (!lvm->map[i].pv) {
+ log_error("Logical volume (%s) contains an incomplete "
+ "mapping table.", lvm->lv->name);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int _check_maps_are_complete(struct dm_hash_table *maps)
+{
+ struct dm_hash_node *n;
+ struct lv_map *lvm;
+
+ for (n = dm_hash_get_first(maps); n; n = dm_hash_get_next(maps, n)) {
+ lvm = (struct lv_map *) dm_hash_get_data(maps, n);
+
+ if (!_check_single_map(lvm))
+ return_0;
+ }
+ return 1;
+}
+
+static uint32_t _area_length(struct lv_map *lvm, uint32_t le)
+{
+ uint32_t len = 0;
+
+ do
+ len++;
+ while ((lvm->map[le + len].pv == lvm->map[le].pv) &&
+ (lvm->map[le].pv &&
+ lvm->map[le + len].pe == lvm->map[le].pe + len));
+
+ return len;
+}
+
+static int _read_linear(struct cmd_context *cmd, struct lv_map *lvm)
+{
+ uint32_t le = 0, len;
+ struct lv_segment *seg;
+ struct segment_type *segtype;
+
+ if (!(segtype = get_segtype_from_string(cmd, "striped")))
+ return_0;
+
+ while (le < lvm->lv->le_count) {
+ len = _area_length(lvm, le);
+
+ if (!(seg = alloc_lv_segment(cmd->mem, segtype, lvm->lv, le,
+ len, 0, 0, NULL, 1, len, 0, 0, 0, NULL))) {
+ log_error("Failed to allocate linear segment.");
+ return 0;
+ }
+
+ if (!set_lv_segment_area_pv(seg, 0, lvm->map[le].pv,
+ lvm->map[le].pe))
+ return_0;
+
+ dm_list_add(&lvm->lv->segments, &seg->list);
+
+ le += seg->len;
+ }
+
+ return 1;
+}
+
+static int _check_stripe(struct lv_map *lvm, uint32_t area_count,
+ uint32_t area_len, uint32_t base_le,
+ uint32_t total_area_len)
+{
+ uint32_t st;
+
+ /*
+ * Is the next physical extent in every stripe adjacent to the last?
+ */
+ for (st = 0; st < area_count; st++)
+ if ((lvm->map[base_le + st * total_area_len + area_len].pv !=
+ lvm->map[base_le + st * total_area_len].pv) ||
+ (lvm->map[base_le + st * total_area_len].pv &&
+ lvm->map[base_le + st * total_area_len + area_len].pe !=
+ lvm->map[base_le + st * total_area_len].pe + area_len))
+ return 0;
+
+ return 1;
+}
+
+static int _read_stripes(struct cmd_context *cmd, struct lv_map *lvm)
+{
+ uint32_t st, first_area_le = 0, total_area_len;
+ uint32_t area_len;
+ struct lv_segment *seg;
+ struct segment_type *segtype;
+
+ /*
+ * Work out overall striped length
+ */
+ if (lvm->lv->le_count % lvm->stripes) {
+ log_error("Number of stripes (%u) incompatible "
+ "with logical extent count (%u) for %s",
+ lvm->stripes, lvm->lv->le_count, lvm->lv->name);
+ }
+
+ total_area_len = lvm->lv->le_count / lvm->stripes;
+
+ if (!(segtype = get_segtype_from_string(cmd, "striped")))
+ return_0;
+
+ while (first_area_le < total_area_len) {
+ area_len = 1;
+
+ /*
+ * Find how many extents are contiguous in all stripes
+ * and so can form part of this segment
+ */
+ while (_check_stripe(lvm, lvm->stripes,
+ area_len, first_area_le, total_area_len))
+ area_len++;
+
+ if (!(seg = alloc_lv_segment(cmd->mem, segtype, lvm->lv,
+ lvm->stripes * first_area_le,
+ lvm->stripes * area_len,
+ 0, lvm->stripe_size, NULL,
+ lvm->stripes,
+ area_len, 0, 0, 0, NULL))) {
+ log_error("Failed to allocate striped segment.");
+ return 0;
+ }
+
+ /*
+ * Set up start positions of each stripe in this segment
+ */
+ for (st = 0; st < seg->area_count; st++)
+ if (!set_lv_segment_area_pv(seg, st,
+ lvm->map[first_area_le + st * total_area_len].pv,
+ lvm->map[first_area_le + st * total_area_len].pe))
+ return_0;
+
+ dm_list_add(&lvm->lv->segments, &seg->list);
+
+ first_area_le += area_len;
+ }
+
+ return 1;
+}
+
+static int _build_segments(struct cmd_context *cmd, struct lv_map *lvm)
+{
+ return (lvm->stripes > 1 ? _read_stripes(cmd, lvm) :
+ _read_linear(cmd, lvm));
+}
+
+static int _build_all_segments(struct cmd_context *cmd, struct dm_hash_table *maps)
+{
+ struct dm_hash_node *n;
+ struct lv_map *lvm;
+
+ for (n = dm_hash_get_first(maps); n; n = dm_hash_get_next(maps, n)) {
+ lvm = (struct lv_map *) dm_hash_get_data(maps, n);
+ if (!_build_segments(cmd, lvm))
+ return_0;
+ }
+
+ return 1;
+}
+
+int import_extents(struct cmd_context *cmd, struct volume_group *vg,
+ struct dm_list *pvds)
+{
+ int r = 0;
+ struct dm_pool *scratch = dm_pool_create("lvm1 import_extents", 10 * 1024);
+ struct dm_hash_table *maps;
+
+ if (!scratch)
+ return_0;
+
+ if (!(maps = _create_lv_maps(scratch, vg))) {
+ log_error("Couldn't allocate logical volume maps.");
+ goto out;
+ }
+
+ if (!_fill_maps(maps, vg, pvds)) {
+ log_error("Couldn't fill logical volume maps.");
+ goto out;
+ }
+
+ if (!_check_maps_are_complete(maps) && !(vg->status & PARTIAL_VG))
+ goto_out;
+
+ if (!_build_all_segments(cmd, maps)) {
+ log_error("Couldn't build extent segments.");
+ goto out;
+ }
+ r = 1;
+
+ out:
+ if (maps)
+ dm_hash_destroy(maps);
+ dm_pool_destroy(scratch);
+ return r;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "disk-rep.h"
+
+/*
+ * Only works with powers of 2.
+ */
+static uint32_t _round_up(uint32_t n, uint32_t size)
+{
+ size--;
+ return (n + size) & ~size;
+}
+
+/* Unused.
+static uint32_t _div_up(uint32_t n, uint32_t size)
+{
+ return _round_up(n, size) / size;
+}
+*/
+
+/*
+ * Each chunk of metadata should be aligned to
+ * METADATA_ALIGN.
+ */
+static uint32_t _next_base(struct data_area *area)
+{
+ return _round_up(area->base + area->size, METADATA_ALIGN);
+}
+
+/*
+ * Quick calculation based on pe_start.
+ */
+static int _adjust_pe_on_disk(struct pv_disk *pvd)
+{
+ uint32_t pe_start = pvd->pe_start << SECTOR_SHIFT;
+
+ if (pe_start < pvd->pe_on_disk.base + pvd->pe_on_disk.size)
+ return 0;
+
+ pvd->pe_on_disk.size = pe_start - pvd->pe_on_disk.base;
+ return 1;
+}
+
+static void _calc_simple_layout(struct pv_disk *pvd)
+{
+ pvd->pv_on_disk.base = METADATA_BASE;
+ pvd->pv_on_disk.size = PV_SIZE;
+
+ pvd->vg_on_disk.base = _next_base(&pvd->pv_on_disk);
+ pvd->vg_on_disk.size = VG_SIZE;
+
+ pvd->pv_uuidlist_on_disk.base = _next_base(&pvd->vg_on_disk);
+ pvd->pv_uuidlist_on_disk.size = MAX_PV * NAME_LEN;
+
+ pvd->lv_on_disk.base = _next_base(&pvd->pv_uuidlist_on_disk);
+ pvd->lv_on_disk.size = MAX_LV * sizeof(struct lv_disk);
+
+ pvd->pe_on_disk.base = _next_base(&pvd->lv_on_disk);
+ pvd->pe_on_disk.size = pvd->pe_total * sizeof(struct pe_disk);
+}
+
+static int _check_vg_limits(struct disk_list *dl)
+{
+ if (dl->vgd.lv_max > MAX_LV) {
+ log_error("MaxLogicalVolumes of %d exceeds format limit of %d "
+ "for VG '%s'", dl->vgd.lv_max, MAX_LV - 1,
+ dl->pvd.vg_name);
+ return 0;
+ }
+
+ if (dl->vgd.pv_max > MAX_PV) {
+ log_error("MaxPhysicalVolumes of %d exceeds format limit of %d "
+ "for VG '%s'", dl->vgd.pv_max, MAX_PV - 1,
+ dl->pvd.vg_name);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * This assumes pe_count and pe_start have already
+ * been calculated correctly.
+ */
+int calculate_layout(struct disk_list *dl)
+{
+ struct pv_disk *pvd = &dl->pvd;
+
+ _calc_simple_layout(pvd);
+ if (!_adjust_pe_on_disk(pvd)) {
+ log_error("Insufficient space for metadata and PE's.");
+ return 0;
+ }
+
+ if (!_check_vg_limits(dl))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * The number of extents that can fit on a disk is metadata format dependant.
+ * pe_start is any existing value for pe_start
+ */
+int calculate_extent_count(struct physical_volume *pv, uint32_t extent_size,
+ uint32_t max_extent_count, uint64_t pe_start)
+{
+ struct pv_disk *pvd = dm_malloc(sizeof(*pvd));
+ uint32_t end;
+
+ if (!pvd)
+ return_0;
+
+ /*
+ * Guess how many extents will fit, bearing in mind that
+ * one is going to be knocked off at the start of the
+ * next loop.
+ */
+ if (max_extent_count)
+ pvd->pe_total = max_extent_count + 1;
+ else
+ pvd->pe_total = (pv->size / extent_size);
+
+ if (pvd->pe_total < PE_SIZE_PV_SIZE_REL) {
+ log_error("Too few extents on %s. Try smaller extent size.",
+ pv_dev_name(pv));
+ dm_free(pvd);
+ return 0;
+ }
+
+ do {
+ pvd->pe_total--;
+ _calc_simple_layout(pvd);
+ end = ((pvd->pe_on_disk.base + pvd->pe_on_disk.size +
+ SECTOR_SIZE - 1) >> SECTOR_SHIFT);
+
+ if (pe_start && end < pe_start)
+ end = pe_start;
+
+ pvd->pe_start = _round_up(end, LVM1_PE_ALIGN);
+
+ } while ((pvd->pe_start + (pvd->pe_total * extent_size))
+ > pv->size);
+
+ if (pvd->pe_total > MAX_PE_TOTAL) {
+ log_error("Metadata extent limit (%u) exceeded for %s - "
+ "%u required", MAX_PE_TOTAL, pv_dev_name(pv),
+ pvd->pe_total);
+ dm_free(pvd);
+ return 0;
+ }
+
+ pv->pe_count = pvd->pe_total;
+ pv->pe_start = pvd->pe_start;
+ /* We can't set pe_size here without breaking LVM1 compatibility */
+ dm_free(pvd);
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "lvm1-label.h"
+#include "disk-rep.h"
+#include "label.h"
+#include "metadata.h"
+#include "xlate.h"
+#include "format1.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static void _not_supported(const char *op)
+{
+ log_error("The '%s' operation is not supported for the lvm1 labeller.",
+ op);
+}
+
+static int _lvm1_can_handle(struct labeller *l __attribute__((unused)), void *buf, uint64_t sector)
+{
+ struct pv_disk *pvd = (struct pv_disk *) buf;
+ uint32_t version;
+
+ /* LVM1 label must always be in first sector */
+ if (sector)
+ return 0;
+
+ version = xlate16(pvd->version);
+
+ if (pvd->id[0] == 'H' && pvd->id[1] == 'M' &&
+ (version == 1 || version == 2))
+ return 1;
+
+ return 0;
+}
+
+static int _lvm1_write(struct label *label __attribute__((unused)), void *buf __attribute__((unused)))
+{
+ _not_supported("write");
+ return 0;
+}
+
+static int _lvm1_read(struct labeller *l, struct device *dev, void *buf,
+ struct label **label)
+{
+ struct pv_disk *pvd = (struct pv_disk *) buf;
+ struct vg_disk vgd;
+ struct lvmcache_info *info;
+ const char *vgid = FMT_LVM1_ORPHAN_VG_NAME;
+ const char *vgname = FMT_LVM1_ORPHAN_VG_NAME;
+ unsigned exported = 0;
+
+ munge_pvd(dev, pvd);
+
+ if (*pvd->vg_name) {
+ if (!read_vgd(dev, &vgd, pvd))
+ return_0;
+ vgid = (char *) vgd.vg_uuid;
+ vgname = (char *) pvd->vg_name;
+ exported = pvd->pv_status & VG_EXPORTED;
+ }
+
+ if (!(info = lvmcache_add(l, (char *)pvd->pv_uuid, dev, vgname, vgid,
+ exported)))
+ return_0;
+ *label = info->label;
+
+ info->device_size = xlate32(pvd->pv_size) << SECTOR_SHIFT;
+ dm_list_init(&info->mdas);
+
+ info->status &= ~CACHE_INVALID;
+
+ return 1;
+}
+
+static int _lvm1_initialise_label(struct labeller *l __attribute__((unused)), struct label *label)
+{
+ strcpy(label->type, "LVM1");
+
+ return 1;
+}
+
+static void _lvm1_destroy_label(struct labeller *l __attribute__((unused)), struct label *label __attribute__((unused)))
+{
+}
+
+static void _lvm1_destroy(struct labeller *l)
+{
+ dm_free(l);
+}
+
+struct label_ops _lvm1_ops = {
+ .can_handle = _lvm1_can_handle,
+ .write = _lvm1_write,
+ .read = _lvm1_read,
+ .verify = _lvm1_can_handle,
+ .initialise_label = _lvm1_initialise_label,
+ .destroy_label = _lvm1_destroy_label,
+ .destroy = _lvm1_destroy,
+};
+
+struct labeller *lvm1_labeller_create(struct format_type *fmt)
+{
+ struct labeller *l;
+
+ if (!(l = dm_malloc(sizeof(*l)))) {
+ log_error("Couldn't allocate labeller object.");
+ return NULL;
+ }
+
+ l->ops = &_lvm1_ops;
+ l->private = (const void *) fmt;
+
+ return l;
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_LVM1_LABEL_H
+#define _LVM_LVM1_LABEL_H
+
+#include "metadata.h"
+
+struct labeller *lvm1_labeller_create(struct format_type *fmt);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "disk-rep.h"
+
+/*
+ * FIXME: Quick hack. We can use caching to
+ * prevent a total re-read, even so vg_number
+ * causes the tools to check *every* pv. Yuck.
+ * Put in separate file so it wouldn't contaminate
+ * other code.
+ */
+int get_free_vg_number(struct format_instance *fid, struct dev_filter *filter,
+ const char *candidate_vg, int *result)
+{
+ struct dm_list all_pvs;
+ struct disk_list *dl;
+ struct dm_pool *mem = dm_pool_create("lvm1 vg_number", 10 * 1024);
+ int numbers[MAX_VG], i, r = 0;
+
+ dm_list_init(&all_pvs);
+
+ if (!mem)
+ return_0;
+
+ if (!read_pvs_in_vg(fid->fmt, NULL, filter, mem, &all_pvs))
+ goto_out;
+
+ memset(numbers, 0, sizeof(numbers));
+
+ dm_list_iterate_items(dl, &all_pvs) {
+ if (!*dl->pvd.vg_name || !strcmp((char *)dl->pvd.vg_name, candidate_vg))
+ continue;
+
+ numbers[dl->vgd.vg_number] = 1;
+ }
+
+ for (i = 0; i < MAX_VG; i++) {
+ if (!numbers[i]) {
+ r = 1;
+ *result = i;
+ break;
+ }
+ }
+
+ out:
+ dm_pool_destroy(mem);
+ return r;
+}
--- /dev/null
+init_format
--- /dev/null
+#
+# Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES =\
+ disk_rep.c \
+ format_pool.c \
+ import_export.c \
+ pool_label.c
+
+LIB_SHARED = liblvm2formatpool.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+install: install_lvm2_plugin
--- /dev/null
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "label.h"
+#include "metadata.h"
+#include "lvmcache.h"
+#include "filter.h"
+#include "xlate.h"
+#include "disk_rep.h"
+
+#include <assert.h>
+
+/* FIXME: memcpy might not be portable */
+#define CPIN_8(x, y, z) {memcpy((x), (y), (z));}
+#define CPOUT_8(x, y, z) {memcpy((y), (x), (z));}
+#define CPIN_16(x, y) {(x) = xlate16_be((y));}
+#define CPOUT_16(x, y) {(y) = xlate16_be((x));}
+#define CPIN_32(x, y) {(x) = xlate32_be((y));}
+#define CPOUT_32(x, y) {(y) = xlate32_be((x));}
+#define CPIN_64(x, y) {(x) = xlate64_be((y));}
+#define CPOUT_64(x, y) {(y) = xlate64_be((x));}
+
+static int __read_pool_disk(const struct format_type *fmt, struct device *dev,
+ struct dm_pool *mem __attribute__((unused)), struct pool_list *pl,
+ const char *vg_name __attribute__((unused)))
+{
+ char buf[512] __attribute__((aligned(8)));
+
+ /* FIXME: Need to check the cache here first */
+ if (!dev_read(dev, UINT64_C(0), 512, buf)) {
+ log_very_verbose("Failed to read PV data from %s",
+ dev_name(dev));
+ return 0;
+ }
+
+ if (!read_pool_label(pl, fmt->labeller, dev, buf, NULL))
+ return_0;
+
+ return 1;
+}
+
+static void _add_pl_to_list(struct dm_list *head, struct pool_list *data)
+{
+ struct pool_list *pl;
+
+ dm_list_iterate_items(pl, head) {
+ if (id_equal(&data->pv_uuid, &pl->pv_uuid)) {
+ char uuid[ID_LEN + 7] __attribute__((aligned(8)));
+
+ id_write_format(&pl->pv_uuid, uuid, ID_LEN + 7);
+
+ if (!dev_subsystem_part_major(data->dev)) {
+ log_very_verbose("Ignoring duplicate PV %s on "
+ "%s", uuid,
+ dev_name(data->dev));
+ return;
+ }
+ log_very_verbose("Duplicate PV %s - using %s %s",
+ uuid, dev_subsystem_name(data->dev),
+ dev_name(data->dev));
+ dm_list_del(&pl->list);
+ break;
+ }
+ }
+ dm_list_add(head, &data->list);
+}
+
+int read_pool_label(struct pool_list *pl, struct labeller *l,
+ struct device *dev, char *buf, struct label **label)
+{
+ struct lvmcache_info *info;
+ struct id pvid;
+ struct id vgid;
+ char uuid[ID_LEN + 7] __attribute__((aligned(8)));
+ struct pool_disk *pd = &pl->pd;
+
+ pool_label_in(pd, buf);
+
+ get_pool_pv_uuid(&pvid, pd);
+ id_write_format(&pvid, uuid, ID_LEN + 7);
+ log_debug("Calculated uuid %s for %s", uuid, dev_name(dev));
+
+ get_pool_vg_uuid(&vgid, pd);
+ id_write_format(&vgid, uuid, ID_LEN + 7);
+ log_debug("Calculated uuid %s for %s", uuid, pd->pl_pool_name);
+
+ if (!(info = lvmcache_add(l, (char *) &pvid, dev, pd->pl_pool_name,
+ (char *) &vgid, 0)))
+ return_0;
+ if (label)
+ *label = info->label;
+
+ info->device_size = xlate32_be(pd->pl_blocks) << SECTOR_SHIFT;
+ dm_list_init(&info->mdas);
+
+ info->status &= ~CACHE_INVALID;
+
+ pl->dev = dev;
+ pl->pv = NULL;
+ memcpy(&pl->pv_uuid, &pvid, sizeof(pvid));
+
+ return 1;
+}
+
+/**
+ * pool_label_out - copies a pool_label_t into a char buffer
+ * @pl: ptr to a pool_label_t struct
+ * @buf: ptr to raw space where label info will be copied
+ *
+ * This function is important because it takes care of all of
+ * the endian issues when copying to disk. This way, when
+ * machines of different architectures are used, they will
+ * be able to interpret ondisk labels correctly. Always use
+ * this function before writing to disk.
+ */
+void pool_label_out(struct pool_disk *pl, void *buf)
+{
+ struct pool_disk *bufpl = (struct pool_disk *) buf;
+
+ CPOUT_64(pl->pl_magic, bufpl->pl_magic);
+ CPOUT_64(pl->pl_pool_id, bufpl->pl_pool_id);
+ CPOUT_8(pl->pl_pool_name, bufpl->pl_pool_name, POOL_NAME_SIZE);
+ CPOUT_32(pl->pl_version, bufpl->pl_version);
+ CPOUT_32(pl->pl_subpools, bufpl->pl_subpools);
+ CPOUT_32(pl->pl_sp_id, bufpl->pl_sp_id);
+ CPOUT_32(pl->pl_sp_devs, bufpl->pl_sp_devs);
+ CPOUT_32(pl->pl_sp_devid, bufpl->pl_sp_devid);
+ CPOUT_32(pl->pl_sp_type, bufpl->pl_sp_type);
+ CPOUT_64(pl->pl_blocks, bufpl->pl_blocks);
+ CPOUT_32(pl->pl_striping, bufpl->pl_striping);
+ CPOUT_32(pl->pl_sp_dmepdevs, bufpl->pl_sp_dmepdevs);
+ CPOUT_32(pl->pl_sp_dmepid, bufpl->pl_sp_dmepid);
+ CPOUT_32(pl->pl_sp_weight, bufpl->pl_sp_weight);
+ CPOUT_32(pl->pl_minor, bufpl->pl_minor);
+ CPOUT_32(pl->pl_padding, bufpl->pl_padding);
+ CPOUT_8(pl->pl_reserve, bufpl->pl_reserve, 184);
+}
+
+/**
+ * pool_label_in - copies a char buffer into a pool_label_t
+ * @pl: ptr to a pool_label_t struct
+ * @buf: ptr to raw space where label info is copied from
+ *
+ * This function is important because it takes care of all of
+ * the endian issues when information from disk is about to be
+ * used. This way, when machines of different architectures
+ * are used, they will be able to interpret ondisk labels
+ * correctly. Always use this function before using labels that
+ * were read from disk.
+ */
+void pool_label_in(struct pool_disk *pl, void *buf)
+{
+ struct pool_disk *bufpl = (struct pool_disk *) buf;
+
+ CPIN_64(pl->pl_magic, bufpl->pl_magic);
+ CPIN_64(pl->pl_pool_id, bufpl->pl_pool_id);
+ CPIN_8(pl->pl_pool_name, bufpl->pl_pool_name, POOL_NAME_SIZE);
+ CPIN_32(pl->pl_version, bufpl->pl_version);
+ CPIN_32(pl->pl_subpools, bufpl->pl_subpools);
+ CPIN_32(pl->pl_sp_id, bufpl->pl_sp_id);
+ CPIN_32(pl->pl_sp_devs, bufpl->pl_sp_devs);
+ CPIN_32(pl->pl_sp_devid, bufpl->pl_sp_devid);
+ CPIN_32(pl->pl_sp_type, bufpl->pl_sp_type);
+ CPIN_64(pl->pl_blocks, bufpl->pl_blocks);
+ CPIN_32(pl->pl_striping, bufpl->pl_striping);
+ CPIN_32(pl->pl_sp_dmepdevs, bufpl->pl_sp_dmepdevs);
+ CPIN_32(pl->pl_sp_dmepid, bufpl->pl_sp_dmepid);
+ CPIN_32(pl->pl_sp_weight, bufpl->pl_sp_weight);
+ CPIN_32(pl->pl_minor, bufpl->pl_minor);
+ CPIN_32(pl->pl_padding, bufpl->pl_padding);
+ CPIN_8(pl->pl_reserve, bufpl->pl_reserve, 184);
+}
+
+static char _calc_char(unsigned int id)
+{
+ /*
+ * [0-9A-Za-z!#] - 64 printable chars (6-bits)
+ */
+
+ if (id < 10)
+ return id + 48;
+ if (id < 36)
+ return (id - 10) + 65;
+ if (id < 62)
+ return (id - 36) + 97;
+ if (id == 62)
+ return '!';
+ if (id == 63)
+ return '#';
+
+ return '%';
+}
+
+void get_pool_uuid(char *uuid, uint64_t poolid, uint32_t spid, uint32_t devid)
+{
+ int i;
+ unsigned shifter = 0x003F;
+
+ assert(ID_LEN == 32);
+ memset(uuid, 0, ID_LEN);
+ strcat(uuid, "POOL0000000000");
+
+ /* We grab the entire 64 bits (+2 that get shifted in) */
+ for (i = 13; i < 24; i++) {
+ uuid[i] = _calc_char(((unsigned) poolid) & shifter);
+ poolid = poolid >> 6;
+ }
+
+ /* We grab the entire 32 bits (+4 that get shifted in) */
+ for (i = 24; i < 30; i++) {
+ uuid[i] = _calc_char((unsigned) (spid & shifter));
+ spid = spid >> 6;
+ }
+
+ /*
+ * Since we can only have 128 devices, we only worry about the
+ * last 12 bits
+ */
+ for (i = 30; i < 32; i++) {
+ uuid[i] = _calc_char((unsigned) (devid & shifter));
+ devid = devid >> 6;
+ }
+
+}
+
+static int _read_vg_pds(const struct format_type *fmt, struct dm_pool *mem,
+ struct lvmcache_vginfo *vginfo, struct dm_list *head,
+ uint32_t *devcount)
+{
+ struct lvmcache_info *info;
+ struct pool_list *pl = NULL;
+ struct dm_pool *tmpmem;
+
+ uint32_t sp_count = 0;
+ uint32_t *sp_devs = NULL;
+ uint32_t i;
+
+ /* FIXME: maybe should return a different error in memory
+ * allocation failure */
+ if (!(tmpmem = dm_pool_create("pool read_vg", 512)))
+ return_0;
+
+ dm_list_iterate_items(info, &vginfo->infos) {
+ if (info->dev &&
+ !(pl = read_pool_disk(fmt, info->dev, mem, vginfo->vgname)))
+ break;
+ /*
+ * We need to keep track of the total expected number
+ * of devices per subpool
+ */
+ if (!sp_count) {
+ /* FIXME pl left uninitialised if !info->dev */
+ if (!pl) {
+ log_error(INTERNAL_ERROR "device is missing");
+ dm_pool_destroy(tmpmem);
+ return 0;
+ }
+ sp_count = pl->pd.pl_subpools;
+ if (!(sp_devs =
+ dm_pool_zalloc(tmpmem,
+ sizeof(uint32_t) * sp_count))) {
+ log_error("Unable to allocate %d 32-bit uints",
+ sp_count);
+ dm_pool_destroy(tmpmem);
+ return 0;
+ }
+ }
+ /*
+ * watch out for a pool label with a different subpool
+ * count than the original - give up if it does
+ */
+ if (sp_count != pl->pd.pl_subpools)
+ break;
+
+ _add_pl_to_list(head, pl);
+
+ if (sp_count > pl->pd.pl_sp_id && sp_devs[pl->pd.pl_sp_id] == 0)
+ sp_devs[pl->pd.pl_sp_id] = pl->pd.pl_sp_devs;
+ }
+
+ *devcount = 0;
+ for (i = 0; i < sp_count; i++)
+ *devcount += sp_devs[i];
+
+ dm_pool_destroy(tmpmem);
+
+ if (pl && *pl->pd.pl_pool_name)
+ return 1;
+
+ return 0;
+
+}
+
+int read_pool_pds(const struct format_type *fmt, const char *vg_name,
+ struct dm_pool *mem, struct dm_list *pdhead)
+{
+ struct lvmcache_vginfo *vginfo;
+ uint32_t totaldevs;
+ int full_scan = -1;
+
+ do {
+ /*
+ * If the cache scanning doesn't work, this will never work
+ */
+ if (vg_name && (vginfo = vginfo_from_vgname(vg_name, NULL)) &&
+ vginfo->infos.n) {
+
+ if (_read_vg_pds(fmt, mem, vginfo, pdhead, &totaldevs)) {
+ /*
+ * If we found all the devices we were
+ * expecting, return success
+ */
+ if (dm_list_size(pdhead) == totaldevs)
+ return 1;
+
+ /*
+ * accept partial pool if we've done a full
+ * rescan of the cache
+ */
+ if (full_scan > 0)
+ return 1;
+ }
+ }
+ /* Failed */
+ dm_list_init(pdhead);
+
+ full_scan++;
+ if (full_scan > 1) {
+ log_debug("No devices for vg %s found in cache",
+ vg_name);
+ return 0;
+ }
+ lvmcache_label_scan(fmt->cmd, full_scan);
+
+ } while (1);
+
+}
+
+struct pool_list *read_pool_disk(const struct format_type *fmt,
+ struct device *dev, struct dm_pool *mem,
+ const char *vg_name)
+{
+ struct pool_list *pl;
+
+ if (!dev_open(dev))
+ return_NULL;
+
+ if (!(pl = dm_pool_zalloc(mem, sizeof(*pl)))) {
+ log_error("Unable to allocate pool list structure");
+ return 0;
+ }
+
+ if (!__read_pool_disk(fmt, dev, mem, pl, vg_name))
+ return_NULL;
+
+ if (!dev_close(dev))
+ stack;
+
+ return pl;
+
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DISK_REP_FORMAT_POOL_H
+#define DISK_REP_FORMAT_POOL_H
+
+#include "label.h"
+#include "metadata.h"
+
+#define MINOR_OFFSET 65536
+
+/* From NSP.cf */
+#define NSPMajorVersion 4
+#define NSPMinorVersion 1
+#define NSPUpdateLevel 3
+
+/* From pool_std.h */
+#define POOL_NAME_SIZE (256)
+#define POOL_MAGIC 0x011670
+#define POOL_MAJOR (121)
+#define POOL_MAX_DEVICES 128
+
+/* When checking for version matching, the first two numbers **
+** are important for metadata formats, a.k.a pool labels. **
+** All the numbers are important when checking if the user **
+** space tools match up with the kernel module............. */
+#define POOL_VERSION (NSPMajorVersion << 16 | \
+ NSPMinorVersion << 8 | \
+ NSPUpdateLevel)
+
+/* Pool label is at the head of every pool disk partition */
+#define SIZEOF_POOL_LABEL (8192)
+
+/* in sectors */
+#define POOL_PE_SIZE (SIZEOF_POOL_LABEL >> SECTOR_SHIFT)
+#define POOL_PE_START (SIZEOF_POOL_LABEL >> SECTOR_SHIFT)
+
+/* Helper fxns */
+#define get_pool_vg_uuid(id, pd) do { get_pool_uuid((char *)(id), \
+ (pd)->pl_pool_id, 0, 0); \
+ } while(0)
+#define get_pool_pv_uuid(id, pd) do { get_pool_uuid((char *)(id), \
+ (pd)->pl_pool_id, \
+ (pd)->pl_sp_id, \
+ (pd)->pl_sp_devid); \
+ } while(0)
+#define get_pool_lv_uuid(id, pd) do { get_pool_uuid((char *)&(id)[0], \
+ (pd)->pl_pool_id, 0, 0); \
+ get_pool_uuid((char*)&(id)[1], \
+ (pd)->pl_pool_id, 0, 0); \
+ } while(0)
+
+struct pool_disk;
+struct pool_list;
+struct user_subpool;
+struct user_device;
+
+struct pool_disk {
+ uint64_t pl_magic; /* Pool magic number */
+ uint64_t pl_pool_id; /* Unique pool identifier */
+ char pl_pool_name[POOL_NAME_SIZE]; /* Name of pool */
+ uint32_t pl_version; /* Pool version */
+ uint32_t pl_subpools; /* Number of subpools in this pool */
+ uint32_t pl_sp_id; /* Subpool number within pool */
+ uint32_t pl_sp_devs; /* Number of data partitions in this subpool */
+ uint32_t pl_sp_devid; /* Partition number within subpool */
+ uint32_t pl_sp_type; /* Partition type */
+ uint64_t pl_blocks; /* Number of blocks in this partition */
+ uint32_t pl_striping; /* Striping size within subpool */
+ /*
+ * If the number of DMEP devices is zero, then the next field **
+ * ** (pl_sp_dmepid) becomes the subpool ID for redirection. In **
+ * ** other words, if this subpool does not have the capability **
+ * ** to do DMEP, then it must specify which subpool will do it **
+ * ** in it's place
+ */
+
+ /*
+ * While the next 3 field are no longer used, they must stay to keep **
+ * ** backward compatibility...........................................
+ */
+ uint32_t pl_sp_dmepdevs;/* Number of dmep devices in this subpool */
+ uint32_t pl_sp_dmepid; /* Dmep device number within subpool */
+ uint32_t pl_sp_weight; /* if dmep dev, pref to using it */
+
+ uint32_t pl_minor; /* the pool minor number */
+ uint32_t pl_padding; /* reminder - think about alignment */
+
+ /*
+ * Even though we're zeroing out 8k at the front of the disk before
+ * writing the label, putting this in
+ */
+ char pl_reserve[184]; /* bump the structure size out to 512 bytes */
+};
+
+struct pool_list {
+ struct dm_list list;
+ struct pool_disk pd;
+ struct physical_volume *pv;
+ struct id pv_uuid;
+ struct device *dev;
+};
+
+struct user_subpool {
+ uint32_t initialized;
+ uint32_t id;
+ uint32_t striping;
+ uint32_t num_devs;
+ uint32_t type;
+ uint32_t dummy;
+ struct user_device *devs;
+};
+
+struct user_device {
+ uint32_t initialized;
+ uint32_t sp_id;
+ uint32_t devid;
+ uint32_t dummy;
+ uint64_t blocks;
+ struct physical_volume *pv;
+};
+
+int read_pool_label(struct pool_list *pl, struct labeller *l,
+ struct device *dev, char *buf, struct label **label);
+void pool_label_out(struct pool_disk *pl, void *buf);
+void pool_label_in(struct pool_disk *pl, void *buf);
+void get_pool_uuid(char *uuid, uint64_t poolid, uint32_t spid, uint32_t devid);
+int import_pool_vg(struct volume_group *vg, struct dm_pool *mem, struct dm_list *pls);
+int import_pool_lvs(struct volume_group *vg, struct dm_pool *mem,
+ struct dm_list *pls);
+int import_pool_pvs(const struct format_type *fmt, struct volume_group *vg,
+ struct dm_pool *mem, struct dm_list *pls);
+int import_pool_pv(const struct format_type *fmt, struct dm_pool *mem,
+ struct volume_group *vg, struct physical_volume *pv,
+ struct pool_list *pl);
+int import_pool_segments(struct dm_list *lvs, struct dm_pool *mem,
+ struct user_subpool *usp, int sp_count);
+int read_pool_pds(const struct format_type *fmt, const char *vgname,
+ struct dm_pool *mem, struct dm_list *head);
+struct pool_list *read_pool_disk(const struct format_type *fmt,
+ struct device *dev, struct dm_pool *mem,
+ const char *vg_name);
+
+#endif /* DISK_REP_POOL_FORMAT_H */
--- /dev/null
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "label.h"
+#include "metadata.h"
+#include "limits.h"
+#include "display.h"
+#include "toolcontext.h"
+#include "lvmcache.h"
+#include "disk_rep.h"
+#include "format_pool.h"
+#include "pool_label.h"
+
+/* Must be called after pvs are imported */
+static struct user_subpool *_build_usp(struct dm_list *pls, struct dm_pool *mem,
+ int *sps)
+{
+ struct pool_list *pl;
+ struct user_subpool *usp = NULL, *cur_sp = NULL;
+ struct user_device *cur_dev = NULL;
+
+ /*
+ * FIXME: Need to do some checks here - I'm tempted to add a
+ * user_pool structure and build the entire thing to check against.
+ */
+ dm_list_iterate_items(pl, pls) {
+ *sps = pl->pd.pl_subpools;
+ if (!usp && (!(usp = dm_pool_zalloc(mem, sizeof(*usp) * (*sps))))) {
+ log_error("Unable to allocate %d subpool structures",
+ *sps);
+ return 0;
+ }
+
+ if (cur_sp != &usp[pl->pd.pl_sp_id]) {
+ cur_sp = &usp[pl->pd.pl_sp_id];
+
+ cur_sp->id = pl->pd.pl_sp_id;
+ cur_sp->striping = pl->pd.pl_striping;
+ cur_sp->num_devs = pl->pd.pl_sp_devs;
+ cur_sp->type = pl->pd.pl_sp_type;
+ cur_sp->initialized = 1;
+ }
+
+ if (!cur_sp->devs &&
+ (!(cur_sp->devs =
+ dm_pool_zalloc(mem,
+ sizeof(*usp->devs) * pl->pd.pl_sp_devs)))) {
+
+ log_error("Unable to allocate %d pool_device "
+ "structures", pl->pd.pl_sp_devs);
+ return 0;
+ }
+
+ cur_dev = &cur_sp->devs[pl->pd.pl_sp_devid];
+ cur_dev->sp_id = cur_sp->id;
+ cur_dev->devid = pl->pd.pl_sp_id;
+ cur_dev->blocks = pl->pd.pl_blocks;
+ cur_dev->pv = pl->pv;
+ cur_dev->initialized = 1;
+ }
+
+ return usp;
+}
+
+static int _check_usp(const char *vgname, struct user_subpool *usp, int sp_count)
+{
+ int i;
+ unsigned j;
+
+ for (i = 0; i < sp_count; i++) {
+ if (!usp[i].initialized) {
+ log_error("Missing subpool %d in pool %s", i, vgname);
+ return 0;
+ }
+ for (j = 0; j < usp[i].num_devs; j++) {
+ if (!usp[i].devs[j].initialized) {
+ log_error("Missing device %u for subpool %d"
+ " in pool %s", j, i, vgname);
+ return 0;
+ }
+
+ }
+ }
+
+ return 1;
+}
+
+static struct volume_group *_build_vg_from_pds(struct format_instance
+ *fid, struct dm_pool *mem,
+ struct dm_list *pds)
+{
+ struct dm_pool *smem = fid->fmt->cmd->mem;
+ struct volume_group *vg = NULL;
+ struct user_subpool *usp = NULL;
+ int sp_count;
+
+ if (!(vg = dm_pool_zalloc(smem, sizeof(*vg)))) {
+ log_error("Unable to allocate volume group structure");
+ return NULL;
+ }
+
+ vg->cmd = fid->fmt->cmd;
+ vg->vgmem = mem;
+ vg->fid = fid;
+ vg->name = NULL;
+ vg->status = 0;
+ vg->extent_count = 0;
+ vg->pv_count = 0;
+ vg->seqno = 1;
+ vg->system_id = NULL;
+ dm_list_init(&vg->pvs);
+ dm_list_init(&vg->lvs);
+ dm_list_init(&vg->tags);
+ dm_list_init(&vg->removed_pvs);
+
+ if (!import_pool_vg(vg, smem, pds))
+ return_NULL;
+
+ if (!import_pool_pvs(fid->fmt, vg, smem, pds))
+ return_NULL;
+
+ if (!import_pool_lvs(vg, smem, pds))
+ return_NULL;
+
+ /*
+ * I need an intermediate subpool structure that contains all the
+ * relevant info for this. Then i can iterate through the subpool
+ * structures for checking, and create the segments
+ */
+ if (!(usp = _build_usp(pds, mem, &sp_count)))
+ return_NULL;
+
+ /*
+ * check the subpool structures - we can't handle partial VGs in
+ * the pool format, so this will error out if we're missing PVs
+ */
+ if (!_check_usp(vg->name, usp, sp_count))
+ return_NULL;
+
+ if (!import_pool_segments(&vg->lvs, smem, usp, sp_count))
+ return_NULL;
+
+ return vg;
+}
+
+static struct volume_group *_pool_vg_read(struct format_instance *fid,
+ const char *vg_name,
+ struct metadata_area *mda __attribute__((unused)))
+{
+ struct dm_pool *mem = dm_pool_create("pool vg_read", VG_MEMPOOL_CHUNK);
+ struct dm_list pds;
+ struct volume_group *vg = NULL;
+
+ dm_list_init(&pds);
+
+ /* We can safely ignore the mda passed in */
+
+ if (!mem)
+ return_NULL;
+
+ /* Strip dev_dir if present */
+ vg_name = strip_dir(vg_name, fid->fmt->cmd->dev_dir);
+
+ /* Read all the pvs in the vg */
+ if (!read_pool_pds(fid->fmt, vg_name, mem, &pds))
+ goto_out;
+
+ /* Do the rest of the vg stuff */
+ if (!(vg = _build_vg_from_pds(fid, mem, &pds)))
+ goto_out;
+
+ return vg;
+out:
+ dm_pool_destroy(mem);
+ return NULL;
+}
+
+static int _pool_pv_setup(const struct format_type *fmt __attribute__((unused)),
+ uint64_t pe_start __attribute__((unused)),
+ uint32_t extent_count __attribute__((unused)),
+ uint32_t extent_size __attribute__((unused)),
+ unsigned long data_alignment __attribute__((unused)),
+ unsigned long data_alignment_offset __attribute__((unused)),
+ int pvmetadatacopies __attribute__((unused)),
+ uint64_t pvmetadatasize __attribute__((unused)),
+ unsigned metadataignore __attribute__((unused)),
+ struct dm_list *mdas __attribute__((unused)),
+ struct physical_volume *pv __attribute__((unused)),
+ struct volume_group *vg __attribute__((unused)))
+{
+ return 1;
+}
+
+static int _pool_pv_read(const struct format_type *fmt, const char *pv_name,
+ struct physical_volume *pv,
+ struct dm_list *mdas __attribute__((unused)),
+ int scan_label_only __attribute__((unused)))
+{
+ struct dm_pool *mem = dm_pool_create("pool pv_read", 1024);
+ struct pool_list *pl;
+ struct device *dev;
+ int r = 0;
+
+ log_very_verbose("Reading physical volume data %s from disk", pv_name);
+
+ if (!mem)
+ return_0;
+
+ if (!(dev = dev_cache_get(pv_name, fmt->cmd->filter)))
+ goto_out;
+
+ /*
+ * I need to read the disk and populate a pv structure here
+ * I'll probably need to abstract some of this later for the
+ * vg_read code
+ */
+ if (!(pl = read_pool_disk(fmt, dev, mem, NULL)))
+ goto_out;
+
+ if (!import_pool_pv(fmt, fmt->cmd->mem, NULL, pv, pl))
+ goto_out;
+
+ pv->fmt = fmt;
+
+ r = 1;
+
+ out:
+ dm_pool_destroy(mem);
+ return r;
+}
+
+/* *INDENT-OFF* */
+static struct metadata_area_ops _metadata_format_pool_ops = {
+ .vg_read = _pool_vg_read,
+};
+/* *INDENT-ON* */
+
+static struct format_instance *_pool_create_instance(const struct format_type *fmt,
+ const char *vgname __attribute__((unused)),
+ const char *vgid __attribute__((unused)),
+ void *private __attribute__((unused)))
+{
+ struct format_instance *fid;
+ struct metadata_area *mda;
+
+ if (!(fid = dm_pool_zalloc(fmt->cmd->mem, sizeof(*fid)))) {
+ log_error("Unable to allocate format instance structure for "
+ "pool format");
+ return NULL;
+ }
+
+ fid->fmt = fmt;
+ dm_list_init(&fid->metadata_areas_in_use);
+ dm_list_init(&fid->metadata_areas_ignored);
+
+ /* Define a NULL metadata area */
+ if (!(mda = dm_pool_zalloc(fmt->cmd->mem, sizeof(*mda)))) {
+ log_error("Unable to allocate metadata area structure "
+ "for pool format");
+ dm_pool_free(fmt->cmd->mem, fid);
+ return NULL;
+ }
+
+ mda->ops = &_metadata_format_pool_ops;
+ mda->metadata_locn = NULL;
+ mda->status = 0;
+ dm_list_add(&fid->metadata_areas_in_use, &mda->list);
+
+ return fid;
+}
+
+static void _pool_destroy_instance(struct format_instance *fid __attribute__((unused)))
+{
+}
+
+static void _pool_destroy(struct format_type *fmt)
+{
+ dm_free(fmt);
+}
+
+/* *INDENT-OFF* */
+static struct format_handler _format_pool_ops = {
+ .pv_read = _pool_pv_read,
+ .pv_setup = _pool_pv_setup,
+ .create_instance = _pool_create_instance,
+ .destroy_instance = _pool_destroy_instance,
+ .destroy = _pool_destroy,
+};
+/* *INDENT-ON */
+
+#ifdef POOL_INTERNAL
+struct format_type *init_pool_format(struct cmd_context *cmd)
+#else /* Shared */
+struct format_type *init_format(struct cmd_context *cmd);
+struct format_type *init_format(struct cmd_context *cmd)
+#endif
+{
+ struct format_type *fmt = dm_malloc(sizeof(*fmt));
+
+ if (!fmt) {
+ log_error("Unable to allocate format type structure for pool "
+ "format");
+ return NULL;
+ }
+
+ fmt->cmd = cmd;
+ fmt->ops = &_format_pool_ops;
+ fmt->name = FMT_POOL_NAME;
+ fmt->alias = NULL;
+ fmt->orphan_vg_name = FMT_POOL_ORPHAN_VG_NAME;
+ fmt->features = 0;
+ fmt->private = NULL;
+
+ if (!(fmt->labeller = pool_labeller_create(fmt))) {
+ log_error("Couldn't create pool label handler.");
+ return NULL;
+ }
+
+ if (!(label_register_handler(FMT_POOL_NAME, fmt->labeller))) {
+ log_error("Couldn't register pool label handler.");
+ return NULL;
+ }
+
+ log_very_verbose("Initialised format: %s", fmt->name);
+
+ return fmt;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_FORMAT_POOL_H
+#define _LVM_FORMAT_POOL_H
+
+#include "metadata.h"
+
+#define FMT_POOL_NAME "pool"
+#define FMT_POOL_ORPHAN_VG_NAME ORPHAN_VG_NAME(FMT_POOL_NAME)
+
+#ifdef POOL_INTERNAL
+struct format_type *init_pool_format(struct cmd_context *cmd);
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "label.h"
+#include "metadata.h"
+#include "lvmcache.h"
+#include "disk_rep.h"
+#include "sptype_names.h"
+#include "lv_alloc.h"
+#include "pv_alloc.h"
+#include "str_list.h"
+#include "display.h"
+#include "segtype.h"
+#include "toolcontext.h"
+
+/* This file contains only imports at the moment... */
+
+int import_pool_vg(struct volume_group *vg, struct dm_pool *mem, struct dm_list *pls)
+{
+ struct pool_list *pl;
+
+ dm_list_iterate_items(pl, pls) {
+ vg->extent_count +=
+ ((pl->pd.pl_blocks) / POOL_PE_SIZE);
+
+ vg->free_count = vg->extent_count;
+
+ if (vg->name)
+ continue;
+
+ vg->name = dm_pool_strdup(mem, pl->pd.pl_pool_name);
+ get_pool_vg_uuid(&vg->id, &pl->pd);
+ vg->extent_size = POOL_PE_SIZE;
+ vg->status |= LVM_READ | LVM_WRITE | CLUSTERED | SHARED;
+ vg->max_lv = 1;
+ vg->max_pv = POOL_MAX_DEVICES;
+ vg->alloc = ALLOC_NORMAL;
+ }
+
+ return 1;
+}
+
+int import_pool_lvs(struct volume_group *vg, struct dm_pool *mem, struct dm_list *pls)
+{
+ struct pool_list *pl;
+ struct logical_volume *lv;
+
+ if (!(lv = alloc_lv(mem)))
+ return_0;
+
+ lv->status = 0;
+ lv->alloc = ALLOC_NORMAL;
+ lv->size = 0;
+ lv->name = NULL;
+ lv->le_count = 0;
+ lv->read_ahead = vg->cmd->default_settings.read_ahead;
+
+ dm_list_iterate_items(pl, pls) {
+ lv->size += pl->pd.pl_blocks;
+
+ if (lv->name)
+ continue;
+
+ if (!(lv->name = dm_pool_strdup(mem, pl->pd.pl_pool_name)))
+ return_0;
+
+ get_pool_lv_uuid(lv->lvid.id, &pl->pd);
+ log_debug("Calculated lv uuid for lv %s: %s", lv->name,
+ lv->lvid.s);
+
+ lv->status |= VISIBLE_LV | LVM_READ | LVM_WRITE;
+ lv->major = POOL_MAJOR;
+
+ /* for pool a minor of 0 is dynamic */
+ if (pl->pd.pl_minor) {
+ lv->status |= FIXED_MINOR;
+ lv->minor = pl->pd.pl_minor + MINOR_OFFSET;
+ } else {
+ lv->minor = -1;
+ }
+ }
+
+ lv->le_count = lv->size / POOL_PE_SIZE;
+
+ return link_lv_to_vg(vg, lv);
+}
+
+int import_pool_pvs(const struct format_type *fmt, struct volume_group *vg,
+ struct dm_pool *mem, struct dm_list *pls)
+{
+ struct pv_list *pvl;
+ struct pool_list *pl;
+
+ dm_list_iterate_items(pl, pls) {
+ if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl)))) {
+ log_error("Unable to allocate pv list structure");
+ return 0;
+ }
+ if (!(pvl->pv = dm_pool_zalloc(mem, sizeof(*pvl->pv)))) {
+ log_error("Unable to allocate pv structure");
+ return 0;
+ }
+ if (!import_pool_pv(fmt, mem, vg, pvl->pv, pl)) {
+ return 0;
+ }
+ pl->pv = pvl->pv;
+ pvl->mdas = NULL;
+ pvl->pe_ranges = NULL;
+ add_pvl_to_vgs(vg, pvl);
+ }
+
+ return 1;
+}
+
+int import_pool_pv(const struct format_type *fmt, struct dm_pool *mem,
+ struct volume_group *vg, struct physical_volume *pv,
+ struct pool_list *pl)
+{
+ struct pool_disk *pd = &pl->pd;
+
+ memset(pv, 0, sizeof(*pv));
+
+ get_pool_pv_uuid(&pv->id, pd);
+ pv->fmt = fmt;
+
+ pv->dev = pl->dev;
+ if (!(pv->vg_name = dm_pool_strdup(mem, pd->pl_pool_name))) {
+ log_error("Unable to duplicate vg_name string");
+ return 0;
+ }
+ if (vg != NULL)
+ memcpy(&pv->vgid, &vg->id, sizeof(vg->id));
+ pv->status = 0;
+ pv->size = pd->pl_blocks;
+ pv->pe_size = POOL_PE_SIZE;
+ pv->pe_start = POOL_PE_START;
+ pv->pe_count = pv->size / POOL_PE_SIZE;
+ pv->pe_alloc_count = 0;
+ pv->pe_align = 0;
+
+ dm_list_init(&pv->tags);
+ dm_list_init(&pv->segments);
+
+ if (!alloc_pv_segment_whole_pv(mem, pv))
+ return_0;
+
+ return 1;
+}
+
+static const char *_cvt_sptype(uint32_t sptype)
+{
+ int i;
+ for (i = 0; sptype_names[i].name[0]; i++) {
+ if (sptype == sptype_names[i].label) {
+ break;
+ }
+ }
+ log_debug("Found sptype %X and converted it to %s",
+ sptype, sptype_names[i].name);
+ return sptype_names[i].name;
+}
+
+static int _add_stripe_seg(struct dm_pool *mem,
+ struct user_subpool *usp, struct logical_volume *lv,
+ uint32_t *le_cur)
+{
+ struct lv_segment *seg;
+ struct segment_type *segtype;
+ unsigned j;
+ uint32_t area_len;
+
+ if (usp->striping & (usp->striping - 1)) {
+ log_error("Stripe size must be a power of 2");
+ return 0;
+ }
+
+ area_len = (usp->devs[0].blocks) / POOL_PE_SIZE;
+
+ if (!(segtype = get_segtype_from_string(lv->vg->cmd,
+ "striped")))
+ return_0;
+
+ if (!(seg = alloc_lv_segment(mem, segtype, lv, *le_cur,
+ area_len * usp->num_devs, 0,
+ usp->striping, NULL, usp->num_devs,
+ area_len, 0, 0, 0, NULL))) {
+ log_error("Unable to allocate striped lv_segment structure");
+ return 0;
+ }
+
+ for (j = 0; j < usp->num_devs; j++)
+ if (!set_lv_segment_area_pv(seg, j, usp->devs[j].pv, 0))
+ return_0;
+
+ /* add the subpool type to the segment tag list */
+ if (!str_list_add(mem, &seg->tags, _cvt_sptype(usp->type))) {
+ log_error("Allocation failed for str_list.");
+ return 0;
+ }
+
+ dm_list_add(&lv->segments, &seg->list);
+
+ *le_cur += seg->len;
+
+ return 1;
+}
+
+static int _add_linear_seg(struct dm_pool *mem,
+ struct user_subpool *usp, struct logical_volume *lv,
+ uint32_t *le_cur)
+{
+ struct lv_segment *seg;
+ struct segment_type *segtype;
+ unsigned j;
+ uint32_t area_len;
+
+ if (!(segtype = get_segtype_from_string(lv->vg->cmd, "striped")))
+ return_0;
+
+ for (j = 0; j < usp->num_devs; j++) {
+ area_len = (usp->devs[j].blocks) / POOL_PE_SIZE;
+
+ if (!(seg = alloc_lv_segment(mem, segtype, lv, *le_cur,
+ area_len, 0, usp->striping,
+ NULL, 1, area_len,
+ POOL_PE_SIZE, 0, 0, NULL))) {
+ log_error("Unable to allocate linear lv_segment "
+ "structure");
+ return 0;
+ }
+
+ /* add the subpool type to the segment tag list */
+ if (!str_list_add(mem, &seg->tags, _cvt_sptype(usp->type))) {
+ log_error("Allocation failed for str_list.");
+ return 0;
+ }
+
+ if (!set_lv_segment_area_pv(seg, 0, usp->devs[j].pv, 0))
+ return_0;
+ dm_list_add(&lv->segments, &seg->list);
+
+ *le_cur += seg->len;
+ }
+
+ return 1;
+}
+
+int import_pool_segments(struct dm_list *lvs, struct dm_pool *mem,
+ struct user_subpool *usp, int subpools)
+{
+ struct lv_list *lvl;
+ struct logical_volume *lv;
+ uint32_t le_cur = 0;
+ int i;
+
+ dm_list_iterate_items(lvl, lvs) {
+ lv = lvl->lv;
+
+ if (lv->status & SNAPSHOT)
+ continue;
+
+ for (i = 0; i < subpools; i++) {
+ if (usp[i].striping) {
+ if (!_add_stripe_seg(mem, &usp[i], lv, &le_cur))
+ return_0;
+ } else {
+ if (!_add_linear_seg(mem, &usp[i], lv, &le_cur))
+ return_0;
+ }
+ }
+ }
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "label.h"
+#include "metadata.h"
+#include "xlate.h"
+#include "disk_rep.h"
+#include "pool_label.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static void _pool_not_supported(const char *op)
+{
+ log_error("The '%s' operation is not supported for the pool labeller.",
+ op);
+}
+
+static int _pool_can_handle(struct labeller *l __attribute__((unused)), void *buf, uint64_t sector)
+{
+
+ struct pool_disk pd;
+
+ /*
+ * POOL label must always be in first sector
+ */
+ if (sector)
+ return 0;
+
+ pool_label_in(&pd, buf);
+
+ /* can ignore 8 rightmost bits for ondisk format check */
+ if ((pd.pl_magic == POOL_MAGIC) &&
+ (pd.pl_version >> 8 == POOL_VERSION >> 8))
+ return 1;
+
+ return 0;
+}
+
+static int _pool_write(struct label *label __attribute__((unused)), void *buf __attribute__((unused)))
+{
+ _pool_not_supported("write");
+ return 0;
+}
+
+static int _pool_read(struct labeller *l, struct device *dev, void *buf,
+ struct label **label)
+{
+ struct pool_list pl;
+
+ return read_pool_label(&pl, l, dev, buf, label);
+}
+
+static int _pool_initialise_label(struct labeller *l __attribute__((unused)), struct label *label)
+{
+ strcpy(label->type, "POOL");
+
+ return 1;
+}
+
+static void _pool_destroy_label(struct labeller *l __attribute__((unused)), struct label *label __attribute__((unused)))
+{
+}
+
+static void _label_pool_destroy(struct labeller *l)
+{
+ dm_free(l);
+}
+
+struct label_ops _pool_ops = {
+ .can_handle = _pool_can_handle,
+ .write = _pool_write,
+ .read = _pool_read,
+ .verify = _pool_can_handle,
+ .initialise_label = _pool_initialise_label,
+ .destroy_label = _pool_destroy_label,
+ .destroy = _label_pool_destroy,
+};
+
+struct labeller *pool_labeller_create(struct format_type *fmt)
+{
+ struct labeller *l;
+
+ if (!(l = dm_malloc(sizeof(*l)))) {
+ log_error("Couldn't allocate labeller object.");
+ return NULL;
+ }
+
+ l->ops = &_pool_ops;
+ l->private = (const void *) fmt;
+
+ return l;
+}
--- /dev/null
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_POOL_LABEL_H
+#define _LVM_POOL_LABEL_H
+
+#include "metadata.h"
+
+struct labeller *pool_labeller_create(struct format_type *fmt);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SPTYPE_NAMES_H
+#define SPTYPE_NAMES_H
+
+/* This must be kept up to date with sistina/pool/module/pool_sptypes.h */
+
+/* Generic Labels */
+#define SPTYPE_DATA (0x00000000)
+
+/* GFS specific labels */
+#define SPTYPE_GFS_DATA (0x68011670)
+#define SPTYPE_GFS_JOURNAL (0x69011670)
+
+struct sptype_name {
+ const char *name;
+ uint32_t label;
+};
+
+static const struct sptype_name sptype_names[] = {
+ {"data", SPTYPE_DATA},
+
+ {"gfs_data", SPTYPE_GFS_DATA},
+ {"gfs_journal", SPTYPE_GFS_JOURNAL},
+
+ {"", 0x0} /* This must be the last flag. */
+};
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "format-text.h"
+
+#include "config.h"
+#include "import-export.h"
+#include "lvm-string.h"
+#include "lvm-file.h"
+#include "toolcontext.h"
+
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <time.h>
+
+#define SECS_PER_DAY 86400 /* 24*60*60 */
+
+/*
+ * The format instance is given a directory path upon creation.
+ * Each file in this directory whose name is of the form
+ * '(.*)_[0-9]*.vg' is a config file (see lib/config.[hc]), which
+ * contains a description of a single volume group.
+ *
+ * The prefix ($1 from the above regex) of the config file gives
+ * the volume group name.
+ *
+ * Backup files that have expired will be removed.
+ */
+
+/*
+ * A list of these is built up for our volume group. Ordered
+ * with the least recent at the head.
+ */
+struct archive_file {
+ struct dm_list list;
+
+ const char *path;
+ uint32_t index;
+};
+
+/*
+ * Extract vg name and version number from a filename.
+ */
+static int _split_vg(const char *filename, char *vgname, size_t vgsize,
+ uint32_t *ix)
+{
+ size_t len, vg_len;
+ const char *dot, *underscore;
+
+ len = strlen(filename);
+ if (len < 7)
+ return 0;
+
+ dot = (filename + len - 3);
+ if (strcmp(".vg", dot))
+ return 0;
+
+ if (!(underscore = strrchr(filename, '_')))
+ return 0;
+
+ if (sscanf(underscore + 1, "%u", ix) != 1)
+ return 0;
+
+ vg_len = underscore - filename;
+ if (vg_len + 1 > vgsize)
+ return 0;
+
+ strncpy(vgname, filename, vg_len);
+ vgname[vg_len] = '\0';
+
+ return 1;
+}
+
+static void _insert_archive_file(struct dm_list *head, struct archive_file *b)
+{
+ struct archive_file *bf = NULL;
+
+ if (dm_list_empty(head)) {
+ dm_list_add(head, &b->list);
+ return;
+ }
+
+ /* index reduces through list */
+ dm_list_iterate_items(bf, head) {
+ if (b->index > bf->index) {
+ dm_list_add(&bf->list, &b->list);
+ return;
+ }
+ }
+
+ dm_list_add_h(&bf->list, &b->list);
+}
+
+static char *_join_file_to_dir(struct dm_pool *mem, const char *dir, const char *name)
+{
+ if (!dm_pool_begin_object(mem, 32) ||
+ !dm_pool_grow_object(mem, dir, strlen(dir)) ||
+ !dm_pool_grow_object(mem, "/", 1) ||
+ !dm_pool_grow_object(mem, name, strlen(name)) ||
+ !dm_pool_grow_object(mem, "\0", 1))
+ return_NULL;
+
+ return dm_pool_end_object(mem);
+}
+
+/*
+ * Returns a list of archive_files.
+ */
+static struct dm_list *_scan_archive(struct dm_pool *mem,
+ const char *vgname, const char *dir)
+{
+ int i, count;
+ uint32_t ix;
+ char vgname_found[64], *path;
+ struct dirent **dirent;
+ struct archive_file *af;
+ struct dm_list *results;
+
+ if (!(results = dm_pool_alloc(mem, sizeof(*results))))
+ return_NULL;
+
+ dm_list_init(results);
+
+ /* Sort fails beyond 5-digit indexes */
+ if ((count = scandir(dir, &dirent, NULL, alphasort)) < 0) {
+ log_error("Couldn't scan the archive directory (%s).", dir);
+ return 0;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (!strcmp(dirent[i]->d_name, ".") ||
+ !strcmp(dirent[i]->d_name, ".."))
+ continue;
+
+ /* check the name is the correct format */
+ if (!_split_vg(dirent[i]->d_name, vgname_found,
+ sizeof(vgname_found), &ix))
+ continue;
+
+ /* is it the vg we're interested in ? */
+ if (strcmp(vgname, vgname_found))
+ continue;
+
+ if (!(path = _join_file_to_dir(mem, dir, dirent[i]->d_name)))
+ goto_out;
+
+ /*
+ * Create a new archive_file.
+ */
+ if (!(af = dm_pool_alloc(mem, sizeof(*af)))) {
+ log_error("Couldn't create new archive file.");
+ results = NULL;
+ goto out;
+ }
+
+ af->index = ix;
+ af->path = path;
+
+ /*
+ * Insert it to the correct part of the list.
+ */
+ _insert_archive_file(results, af);
+ }
+
+ out:
+ for (i = 0; i < count; i++)
+ free(dirent[i]);
+ free(dirent);
+
+ return results;
+}
+
+static void _remove_expired(struct dm_list *archives, uint32_t archives_size,
+ uint32_t retain_days, uint32_t min_archive)
+{
+ struct archive_file *bf;
+ struct stat sb;
+ time_t retain_time;
+
+ /* Make sure there are enough archives to even bother looking for
+ * expired ones... */
+ if (archives_size <= min_archive)
+ return;
+
+ /* Convert retain_days into the time after which we must retain */
+ retain_time = time(NULL) - (time_t) retain_days *SECS_PER_DAY;
+
+ /* Assume list is ordered newest first (by index) */
+ dm_list_iterate_back_items(bf, archives) {
+ /* Get the mtime of the file and unlink if too old */
+ if (stat(bf->path, &sb)) {
+ log_sys_error("stat", bf->path);
+ continue;
+ }
+
+ if (sb.st_mtime > retain_time)
+ return;
+
+ log_very_verbose("Expiring archive %s", bf->path);
+ if (unlink(bf->path))
+ log_sys_error("unlink", bf->path);
+
+ /* Don't delete any more if we've reached the minimum */
+ if (--archives_size <= min_archive)
+ return;
+ }
+}
+
+int archive_vg(struct volume_group *vg,
+ const char *dir, const char *desc,
+ uint32_t retain_days, uint32_t min_archive)
+{
+ int i, fd, rnum, renamed = 0;
+ uint32_t ix = 0;
+ struct archive_file *last;
+ FILE *fp = NULL;
+ char temp_file[PATH_MAX], archive_name[PATH_MAX];
+ struct dm_list *archives;
+
+ /*
+ * Write the vg out to a temporary file.
+ */
+ if (!create_temp_name(dir, temp_file, sizeof(temp_file), &fd,
+ &vg->cmd->rand_seed)) {
+ log_error("Couldn't create temporary archive name.");
+ return 0;
+ }
+
+ if (!(fp = fdopen(fd, "w"))) {
+ log_error("Couldn't create FILE object for archive.");
+ if (close(fd))
+ log_sys_error("close", temp_file);
+ return 0;
+ }
+
+ if (!text_vg_export_file(vg, desc, fp)) {
+ if (fclose(fp))
+ log_sys_error("fclose", temp_file);
+ return_0;
+ }
+
+ if (lvm_fclose(fp, temp_file))
+ return_0; /* Leave file behind as evidence of failure */
+
+ /*
+ * Now we want to rename this file to <vg>_index.vg.
+ */
+ if (!(archives = _scan_archive(vg->cmd->mem, vg->name, dir)))
+ return_0;
+
+ if (dm_list_empty(archives))
+ ix = 0;
+ else {
+ last = dm_list_item(dm_list_first(archives), struct archive_file);
+ ix = last->index + 1;
+ }
+
+ rnum = rand_r(&vg->cmd->rand_seed);
+
+ for (i = 0; i < 10; i++) {
+ if (dm_snprintf(archive_name, sizeof(archive_name),
+ "%s/%s_%05u-%d.vg",
+ dir, vg->name, ix, rnum) < 0) {
+ log_error("Archive file name too long.");
+ return 0;
+ }
+
+ if ((renamed = lvm_rename(temp_file, archive_name)))
+ break;
+
+ ix++;
+ }
+
+ if (!renamed)
+ log_error("Archive rename failed for %s", temp_file);
+
+ _remove_expired(archives, dm_list_size(archives) + renamed, retain_days,
+ min_archive);
+
+ return 1;
+}
+
+static void _display_archive(struct cmd_context *cmd, struct archive_file *af)
+{
+ struct volume_group *vg = NULL;
+ struct format_instance *tf;
+ time_t when;
+ char *desc;
+ void *context;
+
+ log_print(" ");
+ log_print("File:\t\t%s", af->path);
+
+ if (!(context = create_text_context(cmd, af->path, NULL)) ||
+ !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL,
+ NULL, context))) {
+ log_error("Couldn't create text instance object.");
+ return;
+ }
+
+ /*
+ * Read the archive file to ensure that it is valid, and
+ * retrieve the archive time and description.
+ */
+ /* FIXME Use variation on _vg_read */
+ if (!(vg = text_vg_import_file(tf, af->path, &when, &desc))) {
+ log_error("Unable to read archive file.");
+ tf->fmt->ops->destroy_instance(tf);
+ return;
+ }
+
+ log_print("VG name: \t%s", vg->name);
+ log_print("Description:\t%s", desc ? : "<No description>");
+ log_print("Backup Time:\t%s", ctime(&when));
+
+ free_vg(vg);
+ tf->fmt->ops->destroy_instance(tf);
+}
+
+int archive_list(struct cmd_context *cmd, const char *dir, const char *vgname)
+{
+ struct dm_list *archives;
+ struct archive_file *af;
+
+ if (!(archives = _scan_archive(cmd->mem, vgname, dir)))
+ return_0;
+
+ if (dm_list_empty(archives))
+ log_print("No archives found in %s.", dir);
+
+ dm_list_iterate_back_items(af, archives)
+ _display_archive(cmd, af);
+
+ dm_pool_free(cmd->mem, archives);
+
+ return 1;
+}
+
+int archive_list_file(struct cmd_context *cmd, const char *file)
+{
+ struct archive_file af;
+
+ af.path = file;
+
+ if (!path_exists(af.path)) {
+ log_error("Archive file %s not found.", af.path);
+ return 0;
+ }
+
+ _display_archive(cmd, &af);
+
+ return 1;
+}
+
+int backup_list(struct cmd_context *cmd, const char *dir, const char *vgname)
+{
+ struct archive_file af;
+
+ if (!(af.path = _join_file_to_dir(cmd->mem, dir, vgname)))
+ return_0;
+
+ if (path_exists(af.path))
+ _display_archive(cmd, &af);
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "archiver.h"
+#include "format-text.h"
+#include "lvm-file.h"
+#include "lvm-string.h"
+#include "lvmcache.h"
+#include "toolcontext.h"
+#include "locking.h"
+
+#include <unistd.h>
+
+struct archive_params {
+ int enabled;
+ char *dir;
+ unsigned int keep_days;
+ unsigned int keep_number;
+};
+
+struct backup_params {
+ int enabled;
+ char *dir;
+};
+
+int archive_init(struct cmd_context *cmd, const char *dir,
+ unsigned int keep_days, unsigned int keep_min,
+ int enabled)
+{
+ archive_exit(cmd);
+
+ if (!(cmd->archive_params = dm_pool_zalloc(cmd->libmem,
+ sizeof(*cmd->archive_params)))) {
+ log_error("archive_params alloc failed");
+ return 0;
+ }
+
+ cmd->archive_params->dir = NULL;
+
+ if (!*dir)
+ return 1;
+
+ if (!(cmd->archive_params->dir = dm_strdup(dir))) {
+ log_error("Couldn't copy archive directory name.");
+ return 0;
+ }
+
+ cmd->archive_params->keep_days = keep_days;
+ cmd->archive_params->keep_number = keep_min;
+ archive_enable(cmd, enabled);
+
+ return 1;
+}
+
+void archive_exit(struct cmd_context *cmd)
+{
+ if (!cmd->archive_params)
+ return;
+ if (cmd->archive_params->dir)
+ dm_free(cmd->archive_params->dir);
+ memset(cmd->archive_params, 0, sizeof(*cmd->archive_params));
+}
+
+void archive_enable(struct cmd_context *cmd, int flag)
+{
+ cmd->archive_params->enabled = flag;
+}
+
+static char *_build_desc(struct dm_pool *mem, const char *line, int before)
+{
+ size_t len = strlen(line) + 32;
+ char *buffer;
+
+ if (!(buffer = dm_pool_zalloc(mem, strlen(line) + 32)))
+ return_NULL;
+
+ if (snprintf(buffer, len,
+ "Created %s executing '%s'",
+ before ? "*before*" : "*after*", line) < 0)
+ return_NULL;
+
+ return buffer;
+}
+
+static int __archive(struct volume_group *vg)
+{
+ char *desc;
+
+ if (!(desc = _build_desc(vg->cmd->mem, vg->cmd->cmd_line, 1)))
+ return_0;
+
+ return archive_vg(vg, vg->cmd->archive_params->dir, desc,
+ vg->cmd->archive_params->keep_days,
+ vg->cmd->archive_params->keep_number);
+}
+
+int archive(struct volume_group *vg)
+{
+ if (!vg->cmd->archive_params->enabled || !vg->cmd->archive_params->dir)
+ return 1;
+
+ if (test_mode()) {
+ log_verbose("Test mode: Skipping archiving of volume group.");
+ return 1;
+ }
+
+ if (!dm_create_dir(vg->cmd->archive_params->dir))
+ return 0;
+
+ /* Trap a read-only file system */
+ if ((access(vg->cmd->archive_params->dir, R_OK | W_OK | X_OK) == -1) &&
+ (errno == EROFS))
+ return 0;
+
+ log_verbose("Archiving volume group \"%s\" metadata (seqno %u).", vg->name,
+ vg->seqno);
+ if (!__archive(vg)) {
+ log_error("Volume group \"%s\" metadata archive failed.",
+ vg->name);
+ return 0;
+ }
+
+ return 1;
+}
+
+int archive_display(struct cmd_context *cmd, const char *vg_name)
+{
+ int r1, r2;
+
+ r1 = archive_list(cmd, cmd->archive_params->dir, vg_name);
+ r2 = backup_list(cmd, cmd->backup_params->dir, vg_name);
+
+ return r1 && r2;
+}
+
+int archive_display_file(struct cmd_context *cmd, const char *file)
+{
+ int r;
+
+ r = archive_list_file(cmd, file);
+
+ return r;
+}
+
+int backup_init(struct cmd_context *cmd, const char *dir,
+ int enabled)
+{
+ backup_exit(cmd);
+
+ if (!(cmd->backup_params = dm_pool_zalloc(cmd->libmem,
+ sizeof(*cmd->backup_params)))) {
+ log_error("backup_params alloc failed");
+ return 0;
+ }
+
+ cmd->backup_params->dir = NULL;
+ if (!*dir)
+ return 1;
+
+ if (!(cmd->backup_params->dir = dm_strdup(dir))) {
+ log_error("Couldn't copy backup directory name.");
+ return 0;
+ }
+ backup_enable(cmd, enabled);
+
+ return 1;
+}
+
+void backup_exit(struct cmd_context *cmd)
+{
+ if (!cmd->backup_params)
+ return;
+ if (cmd->backup_params->dir)
+ dm_free(cmd->backup_params->dir);
+ memset(cmd->backup_params, 0, sizeof(*cmd->backup_params));
+}
+
+void backup_enable(struct cmd_context *cmd, int flag)
+{
+ cmd->backup_params->enabled = flag;
+}
+
+static int __backup(struct volume_group *vg)
+{
+ char name[PATH_MAX];
+ char *desc;
+
+ if (!(desc = _build_desc(vg->cmd->mem, vg->cmd->cmd_line, 0)))
+ return_0;
+
+ if (dm_snprintf(name, sizeof(name), "%s/%s",
+ vg->cmd->backup_params->dir, vg->name) < 0) {
+ log_error("Failed to generate volume group metadata backup "
+ "filename.");
+ return 0;
+ }
+
+ return backup_to_file(name, desc, vg);
+}
+
+int backup_locally(struct volume_group *vg)
+{
+ if (!vg->cmd->backup_params->enabled || !vg->cmd->backup_params->dir) {
+ log_warn("WARNING: This metadata update is NOT backed up");
+ return 1;
+ }
+
+ if (test_mode()) {
+ log_verbose("Test mode: Skipping volume group backup.");
+ return 1;
+ }
+
+ if (!dm_create_dir(vg->cmd->backup_params->dir))
+ return 0;
+
+ /* Trap a read-only file system */
+ if ((access(vg->cmd->backup_params->dir, R_OK | W_OK | X_OK) == -1) &&
+ (errno == EROFS))
+ return 0;
+
+ if (!__backup(vg)) {
+ log_error("Backup of volume group %s metadata failed.",
+ vg->name);
+ return 0;
+ }
+
+ return 1;
+}
+
+int backup(struct volume_group *vg)
+{
+ if (vg_is_clustered(vg))
+ remote_backup_metadata(vg);
+
+ return backup_locally(vg);
+}
+
+int backup_remove(struct cmd_context *cmd, const char *vg_name)
+{
+ char path[PATH_MAX];
+
+ if (dm_snprintf(path, sizeof(path), "%s/%s",
+ cmd->backup_params->dir, vg_name) < 0) {
+ log_error("Failed to generate backup filename (for removal).");
+ return 0;
+ }
+
+ /*
+ * Let this fail silently.
+ */
+ unlink(path);
+ return 1;
+}
+
+struct volume_group *backup_read_vg(struct cmd_context *cmd,
+ const char *vg_name, const char *file)
+{
+ struct volume_group *vg = NULL;
+ struct format_instance *tf;
+ struct metadata_area *mda;
+ void *context;
+
+ if (!(context = create_text_context(cmd, file,
+ cmd->cmd_line)) ||
+ !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL,
+ NULL, context))) {
+ log_error("Couldn't create text format object.");
+ return NULL;
+ }
+
+ dm_list_iterate_items(mda, &tf->metadata_areas_in_use) {
+ if (!(vg = mda->ops->vg_read(tf, vg_name, mda)))
+ stack;
+ break;
+ }
+
+ tf->fmt->ops->destroy_instance(tf);
+ return vg;
+}
+
+/* ORPHAN and VG locks held before calling this */
+int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg)
+{
+ struct pv_list *pvl;
+ struct physical_volume *pv;
+ struct lvmcache_info *info;
+
+ /*
+ * FIXME: Check that the PVs referenced in the backup are
+ * not members of other existing VGs.
+ */
+
+ /* Attempt to write out using currently active format */
+ if (!(vg->fid = cmd->fmt->ops->create_instance(cmd->fmt, vg->name,
+ NULL, NULL))) {
+ log_error("Failed to allocate format instance");
+ return 0;
+ }
+
+ /*
+ * Setting vg->old_name to a blank value will explicitly
+ * disable any attempt to check VG name in existing metadata.
+ */
+ vg->old_name = dm_pool_strdup(vg->vgmem, "");
+
+ /* Add any metadata areas on the PVs */
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ pv = pvl->pv;
+ if (!(info = info_from_pvid(pv->dev->pvid, 0))) {
+ log_error("PV %s missing from cache",
+ pv_dev_name(pv));
+ return 0;
+ }
+ if (cmd->fmt != info->fmt) {
+ log_error("PV %s is a different format (seqno %s)",
+ pv_dev_name(pv), info->fmt->name);
+ return 0;
+ }
+ if (!vg->fid->fmt->ops->
+ pv_setup(vg->fid->fmt, UINT64_C(0), 0, 0, 0, 0, 0UL,
+ UINT64_C(0), 0,
+ &vg->fid->metadata_areas_in_use, pv, vg)) {
+ log_error("Format-specific setup for %s failed",
+ pv_dev_name(pv));
+ return 0;
+ }
+ }
+
+ if (!vg_write(vg) || !vg_commit(vg))
+ return_0;
+
+ return 1;
+}
+
+/* ORPHAN and VG locks held before calling this */
+int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name,
+ const char *file)
+{
+ struct volume_group *vg;
+ int missing_pvs, r = 0;
+
+ /*
+ * Read in the volume group from the text file.
+ */
+ if (!(vg = backup_read_vg(cmd, vg_name, file)))
+ return_0;
+
+ missing_pvs = vg_missing_pv_count(vg);
+ if (missing_pvs == 0)
+ r = backup_restore_vg(cmd, vg);
+ else
+ log_error("Cannot restore Volume Group %s with %i PVs "
+ "marked as missing.", vg->name, missing_pvs);
+
+ free_vg(vg);
+ return r;
+}
+
+int backup_restore(struct cmd_context *cmd, const char *vg_name)
+{
+ char path[PATH_MAX];
+
+ if (dm_snprintf(path, sizeof(path), "%s/%s",
+ cmd->backup_params->dir, vg_name) < 0) {
+ log_error("Failed to generate backup filename (for restore).");
+ return 0;
+ }
+
+ return backup_restore_from_file(cmd, vg_name, path);
+}
+
+int backup_to_file(const char *file, const char *desc, struct volume_group *vg)
+{
+ int r = 0;
+ struct format_instance *tf;
+ struct metadata_area *mda;
+ void *context;
+ struct cmd_context *cmd;
+
+ cmd = vg->cmd;
+
+ log_verbose("Creating volume group backup \"%s\" (seqno %u).", file, vg->seqno);
+
+ if (!(context = create_text_context(cmd, file, desc)) ||
+ !(tf = cmd->fmt_backup->ops->create_instance(cmd->fmt_backup, NULL,
+ NULL, context))) {
+ log_error("Couldn't create backup object.");
+ return 0;
+ }
+
+ if (!dm_list_size(&tf->metadata_areas_in_use)) {
+ log_error(INTERNAL_ERROR "No in use metadata areas to write.");
+ return 0;
+ }
+
+ /* Write and commit the metadata area */
+ dm_list_iterate_items(mda, &tf->metadata_areas_in_use) {
+ if (!(r = mda->ops->vg_write(tf, vg, mda))) {
+ stack;
+ continue;
+ }
+ if (mda->ops->vg_commit &&
+ !(r = mda->ops->vg_commit(tf, vg, mda))) {
+ stack;
+ }
+ }
+
+ tf->fmt->ops->destroy_instance(tf);
+ return r;
+}
+
+/*
+ * Update backup (and archive) if they're out-of-date or don't exist.
+ */
+void check_current_backup(struct volume_group *vg)
+{
+ char path[PATH_MAX];
+ struct volume_group *vg_backup;
+ int old_suppress;
+
+ if (vg_is_exported(vg))
+ return;
+
+ if (dm_snprintf(path, sizeof(path), "%s/%s",
+ vg->cmd->backup_params->dir, vg->name) < 0) {
+ log_debug("Failed to generate backup filename.");
+ return;
+ }
+
+ old_suppress = log_suppress(1);
+ /* Up-to-date backup exists? */
+ if ((vg_backup = backup_read_vg(vg->cmd, vg->name, path)) &&
+ (vg->seqno == vg_backup->seqno) &&
+ (id_equal(&vg->id, &vg_backup->id))) {
+ log_suppress(old_suppress);
+ free_vg(vg_backup);
+ return;
+ }
+ log_suppress(old_suppress);
+
+ if (vg_backup) {
+ archive(vg_backup);
+ free_vg(vg_backup);
+ }
+ archive(vg);
+ backup_locally(vg);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_TOOL_ARCHIVE_H
+#define _LVM_TOOL_ARCHIVE_H
+
+#include "metadata-exported.h"
+
+/*
+ * There are two operations that come under the general area of
+ * backups. 'Archiving' occurs just before a volume group
+ * configuration is changed. The user may configure when
+ * archived files are expired. Typically archives will be stored
+ * in /etc/lvm/archive.
+ *
+ * A 'backup' is a redundant copy of the *current* volume group
+ * configuration. As such it should be taken just after the
+ * volume group is changed. Only 1 backup file will exist.
+ * Typically backups will be stored in /etc/lvm/backups.
+ */
+
+int archive_init(struct cmd_context *cmd, const char *dir,
+ unsigned int keep_days, unsigned int keep_min,
+ int enabled);
+void archive_exit(struct cmd_context *cmd);
+
+void archive_enable(struct cmd_context *cmd, int flag);
+int archive(struct volume_group *vg);
+int archive_display(struct cmd_context *cmd, const char *vg_name);
+int archive_display_file(struct cmd_context *cmd, const char *file);
+
+int backup_init(struct cmd_context *cmd, const char *dir, int enabled);
+void backup_exit(struct cmd_context *cmd);
+
+void backup_enable(struct cmd_context *cmd, int flag);
+int backup(struct volume_group *vg);
+int backup_locally(struct volume_group *vg);
+int backup_remove(struct cmd_context *cmd, const char *vg_name);
+
+struct volume_group *backup_read_vg(struct cmd_context *cmd,
+ const char *vg_name, const char *file);
+int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg);
+int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name,
+ const char *file);
+int backup_restore(struct cmd_context *cmd, const char *vg_name);
+
+int backup_to_file(const char *file, const char *desc, struct volume_group *vg);
+
+void check_current_backup(struct volume_group *vg);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "import-export.h"
+#include "metadata.h"
+#include "display.h"
+#include "lvm-string.h"
+#include "segtype.h"
+#include "text_export.h"
+#include "lvm-version.h"
+
+#include <stdarg.h>
+#include <time.h>
+#include <sys/utsname.h>
+
+struct formatter;
+typedef int (*out_with_comment_fn) (struct formatter * f, const char *comment,
+ const char *fmt, va_list ap);
+typedef int (*nl_fn) (struct formatter * f);
+
+/*
+ * Macro for formatted output.
+ * out_with_comment_fn returns -1 if data didn't fit and buffer was expanded.
+ * Then argument list is reset and out_with_comment_fn is called again.
+ */
+#define _out_with_comment(f, buffer, fmt, ap) \
+ do { \
+ va_start(ap, fmt); \
+ r = f->out_with_comment(f, buffer, fmt, ap); \
+ va_end(ap); \
+ } while (r == -1)
+
+/*
+ * The first half of this file deals with
+ * exporting the vg, ie. writing it to a file.
+ */
+struct formatter {
+ struct dm_pool *mem; /* pv names allocated from here */
+ struct dm_hash_table *pv_names; /* dev_name -> pv_name (eg, pv1) */
+
+ union {
+ FILE *fp; /* where we're writing to */
+ struct {
+ char *start;
+ uint32_t size;
+ uint32_t used;
+ } buf;
+ } data;
+
+ out_with_comment_fn out_with_comment;
+ nl_fn nl;
+
+ int indent; /* current level of indentation */
+ int error;
+ int header; /* 1 => comments at start; 0 => end */
+};
+
+static struct utsname _utsname;
+
+static void _init(void)
+{
+ static int _initialised = 0;
+
+ if (_initialised)
+ return;
+
+ if (uname(&_utsname)) {
+ log_error("uname failed: %s", strerror(errno));
+ memset(&_utsname, 0, sizeof(_utsname));
+ }
+
+ _initialised = 1;
+}
+
+/*
+ * Formatting functions.
+ */
+
+#define MAX_INDENT 5
+static void _inc_indent(struct formatter *f)
+{
+ if (++f->indent > MAX_INDENT)
+ f->indent = MAX_INDENT;
+}
+
+static void _dec_indent(struct formatter *f)
+{
+ if (!f->indent--) {
+ log_error(INTERNAL_ERROR "problem tracking indentation");
+ f->indent = 0;
+ }
+}
+
+/*
+ * Newline function for prettier layout.
+ */
+static int _nl_file(struct formatter *f)
+{
+ fprintf(f->data.fp, "\n");
+
+ return 1;
+}
+
+static int _extend_buffer(struct formatter *f)
+{
+ char *newbuf;
+
+ log_debug("Doubling metadata output buffer to %" PRIu32,
+ f->data.buf.size * 2);
+ if (!(newbuf = dm_realloc(f->data.buf.start,
+ f->data.buf.size * 2))) {
+ log_error("Buffer reallocation failed.");
+ return 0;
+ }
+ f->data.buf.start = newbuf;
+ f->data.buf.size *= 2;
+
+ return 1;
+}
+
+static int _nl_raw(struct formatter *f)
+{
+ /* If metadata doesn't fit, extend buffer */
+ if ((f->data.buf.used + 2 > f->data.buf.size) &&
+ (!_extend_buffer(f)))
+ return_0;
+
+ *(f->data.buf.start + f->data.buf.used) = '\n';
+ f->data.buf.used += 1;
+
+ *(f->data.buf.start + f->data.buf.used) = '\0';
+
+ return 1;
+}
+
+#define COMMENT_TAB 6
+static int _out_with_comment_file(struct formatter *f, const char *comment,
+ const char *fmt, va_list ap)
+{
+ int i;
+ char white_space[MAX_INDENT + 1];
+
+ if (ferror(f->data.fp))
+ return 0;
+
+ for (i = 0; i < f->indent; i++)
+ white_space[i] = '\t';
+ white_space[i] = '\0';
+ fputs(white_space, f->data.fp);
+ i = vfprintf(f->data.fp, fmt, ap);
+
+ if (comment) {
+ /*
+ * line comments up if possible.
+ */
+ i += 8 * f->indent;
+ i /= 8;
+ i++;
+
+ do
+ fputc('\t', f->data.fp);
+
+ while (++i < COMMENT_TAB);
+
+ fputs(comment, f->data.fp);
+ }
+ fputc('\n', f->data.fp);
+
+ return 1;
+}
+
+static int _out_with_comment_raw(struct formatter *f,
+ const char *comment __attribute__((unused)),
+ const char *fmt, va_list ap)
+{
+ int n;
+
+ n = vsnprintf(f->data.buf.start + f->data.buf.used,
+ f->data.buf.size - f->data.buf.used, fmt, ap);
+
+ /* If metadata doesn't fit, extend buffer */
+ if (n < 0 || (n + f->data.buf.used + 2 > f->data.buf.size)) {
+ if (!_extend_buffer(f))
+ return_0;
+ return -1; /* Retry */
+ }
+
+ f->data.buf.used += n;
+
+ outnl(f);
+
+ return 1;
+}
+
+/*
+ * Formats a string, converting a size specified
+ * in 512-byte sectors to a more human readable
+ * form (eg, megabytes). We may want to lift this
+ * for other code to use.
+ */
+static int _sectors_to_units(uint64_t sectors, char *buffer, size_t s)
+{
+ static const char *_units[] = {
+ "Kilobytes",
+ "Megabytes",
+ "Gigabytes",
+ "Terabytes",
+ "Petabytes",
+ "Exabytes",
+ NULL
+ };
+
+ int i;
+ double d = (double) sectors;
+
+ /* to convert to K */
+ d /= 2.0;
+
+ for (i = 0; (d > 1024.0) && _units[i]; i++)
+ d /= 1024.0;
+
+ return dm_snprintf(buffer, s, "# %g %s", d, _units[i]) > 0;
+}
+
+/* increment indention level */
+void out_inc_indent(struct formatter *f)
+{
+ _inc_indent(f);
+}
+
+/* decrement indention level */
+void out_dec_indent(struct formatter *f)
+{
+ _dec_indent(f);
+}
+
+/* insert new line */
+int out_newline(struct formatter *f)
+{
+ return f->nl(f);
+}
+
+/*
+ * Appends a comment giving a size in more easily
+ * readable form (eg, 4M instead of 8096).
+ */
+int out_size(struct formatter *f, uint64_t size, const char *fmt, ...)
+{
+ char buffer[64];
+ va_list ap;
+ int r;
+
+ if (!_sectors_to_units(size, buffer, sizeof(buffer)))
+ return 0;
+
+ _out_with_comment(f, buffer, fmt, ap);
+
+ return r;
+}
+
+/*
+ * Appends a comment indicating that the line is
+ * only a hint.
+ */
+int out_hint(struct formatter *f, const char *fmt, ...)
+{
+ va_list ap;
+ int r;
+
+ _out_with_comment(f, "# Hint only", fmt, ap);
+
+ return r;
+}
+
+/*
+ * The normal output function with comment
+ */
+int out_text_with_comment(struct formatter *f, const char *comment, const char *fmt, ...)
+{
+ va_list ap;
+ int r;
+
+ _out_with_comment(f, comment, fmt, ap);
+
+ return r;
+}
+
+/*
+ * The normal output function.
+ */
+int out_text(struct formatter *f, const char *fmt, ...)
+{
+ va_list ap;
+ int r;
+
+ _out_with_comment(f, NULL, fmt, ap);
+
+ return r;
+}
+
+static int _out_line(const char *line, void *_f) {
+ struct formatter *f = (struct formatter *) _f;
+ return out_text(f, "%s", line);
+}
+
+int out_config_node(struct formatter *f, const struct config_node *cn)
+{
+ return write_config_node(cn, _out_line, f);
+}
+
+static int _print_header(struct formatter *f,
+ const char *desc)
+{
+ char *buf;
+ time_t t;
+
+ t = time(NULL);
+
+ outf(f, "# Generated by LVM2 version %s: %s", LVM_VERSION, ctime(&t));
+ outf(f, CONTENTS_FIELD " = \"" CONTENTS_VALUE "\"");
+ outf(f, FORMAT_VERSION_FIELD " = %d", FORMAT_VERSION_VALUE);
+ outnl(f);
+
+ if (!(buf = alloca(escaped_len(desc)))) {
+ log_error("temporary stack allocation for description"
+ "string failed");
+ return 0;
+ }
+ outf(f, "description = \"%s\"", escape_double_quotes(buf, desc));
+ outnl(f);
+ outf(f, "creation_host = \"%s\"\t# %s %s %s %s %s", _utsname.nodename,
+ _utsname.sysname, _utsname.nodename, _utsname.release,
+ _utsname.version, _utsname.machine);
+ outf(f, "creation_time = %lu\t# %s", t, ctime(&t));
+
+ return 1;
+}
+
+static int _print_flag_config(struct formatter *f, uint64_t status, int type)
+{
+ char buffer[4096];
+ if (!print_flags(status, type | STATUS_FLAG, buffer, sizeof(buffer)))
+ return_0;
+ outf(f, "status = %s", buffer);
+
+ if (!print_flags(status, type, buffer, sizeof(buffer)))
+ return_0;
+ outf(f, "flags = %s", buffer);
+
+ return 1;
+}
+
+
+static int _out_tags(struct formatter *f, struct dm_list *tags)
+{
+ char *tag_buffer;
+
+ if (!dm_list_empty(tags)) {
+ if (!(tag_buffer = alloc_printed_tags(tags)))
+ return_0;
+ if (!out_text(f, "tags = %s", tag_buffer)) {
+ dm_free(tag_buffer);
+ return_0;
+ }
+ dm_free(tag_buffer);
+ }
+
+ return 1;
+}
+
+static int _print_vg(struct formatter *f, struct volume_group *vg)
+{
+ char buffer[4096];
+
+ if (!id_write_format(&vg->id, buffer, sizeof(buffer)))
+ return_0;
+
+ outf(f, "id = \"%s\"", buffer);
+
+ outf(f, "seqno = %u", vg->seqno);
+
+ if (!_print_flag_config(f, vg->status, VG_FLAGS))
+ return_0;
+
+ if (!_out_tags(f, &vg->tags))
+ return_0;
+
+ if (vg->system_id && *vg->system_id)
+ outf(f, "system_id = \"%s\"", vg->system_id);
+
+ outsize(f, (uint64_t) vg->extent_size, "extent_size = %u",
+ vg->extent_size);
+ outf(f, "max_lv = %u", vg->max_lv);
+ outf(f, "max_pv = %u", vg->max_pv);
+
+ /* Default policy is NORMAL; INHERIT is meaningless */
+ if (vg->alloc != ALLOC_NORMAL && vg->alloc != ALLOC_INHERIT) {
+ outnl(f);
+ outf(f, "allocation_policy = \"%s\"",
+ get_alloc_string(vg->alloc));
+ }
+ outf(f, "metadata_copies = %u", vg->mda_copies);
+
+ return 1;
+}
+
+/*
+ * Get the pv%d name from the formatters hash
+ * table.
+ */
+static const char *_get_pv_name_from_uuid(struct formatter *f, char *uuid)
+{
+ return dm_hash_lookup(f->pv_names, uuid);
+}
+
+static const char *_get_pv_name(struct formatter *f, struct physical_volume *pv)
+{
+ char uuid[64] __attribute__((aligned(8)));
+
+ if (!pv || !id_write_format(&pv->id, uuid, sizeof(uuid)))
+ return_NULL;
+
+ return _get_pv_name_from_uuid(f, uuid);
+}
+
+static int _print_pvs(struct formatter *f, struct volume_group *vg)
+{
+ struct pv_list *pvl;
+ struct physical_volume *pv;
+ char buffer[4096];
+ char *buf;
+ const char *name;
+
+ outf(f, "physical_volumes {");
+ _inc_indent(f);
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ pv = pvl->pv;
+
+ if (!id_write_format(&pv->id, buffer, sizeof(buffer)))
+ return_0;
+
+ if (!(name = _get_pv_name_from_uuid(f, buffer)))
+ return_0;
+
+ outnl(f);
+ outf(f, "%s {", name);
+ _inc_indent(f);
+
+ outf(f, "id = \"%s\"", buffer);
+
+ if (!(buf = alloca(escaped_len(pv_dev_name(pv))))) {
+ log_error("temporary stack allocation for device name"
+ "string failed");
+ return 0;
+ }
+
+ outhint(f, "device = \"%s\"",
+ escape_double_quotes(buf, pv_dev_name(pv)));
+ outnl(f);
+
+ if (!_print_flag_config(f, pv->status, PV_FLAGS))
+ return_0;
+
+ if (!_out_tags(f, &pv->tags))
+ return_0;
+
+ outsize(f, pv->size, "dev_size = %" PRIu64, pv->size);
+
+ outf(f, "pe_start = %" PRIu64, pv->pe_start);
+ outsize(f, vg->extent_size * (uint64_t) pv->pe_count,
+ "pe_count = %u", pv->pe_count);
+
+ _dec_indent(f);
+ outf(f, "}");
+ }
+
+ _dec_indent(f);
+ outf(f, "}");
+ return 1;
+}
+
+static int _print_segment(struct formatter *f, struct volume_group *vg,
+ int count, struct lv_segment *seg)
+{
+ outf(f, "segment%u {", count);
+ _inc_indent(f);
+
+ outf(f, "start_extent = %u", seg->le);
+ outsize(f, (uint64_t) seg->len * vg->extent_size,
+ "extent_count = %u", seg->len);
+
+ outnl(f);
+ outf(f, "type = \"%s\"", seg->segtype->name);
+
+ if (!_out_tags(f, &seg->tags))
+ return_0;
+
+ if (seg->segtype->ops->text_export &&
+ !seg->segtype->ops->text_export(seg, f))
+ return_0;
+
+ _dec_indent(f);
+ outf(f, "}");
+
+ return 1;
+}
+
+int out_areas(struct formatter *f, const struct lv_segment *seg,
+ const char *type)
+{
+ const char *name;
+ unsigned int s;
+
+ outnl(f);
+
+ outf(f, "%ss = [", type);
+ _inc_indent(f);
+
+ for (s = 0; s < seg->area_count; s++) {
+ switch (seg_type(seg, s)) {
+ case AREA_PV:
+ if (!(name = _get_pv_name(f, seg_pv(seg, s))))
+ return_0;
+
+ outf(f, "\"%s\", %u%s", name,
+ seg_pe(seg, s),
+ (s == seg->area_count - 1) ? "" : ",");
+ break;
+ case AREA_LV:
+ outf(f, "\"%s\", %u%s",
+ seg_lv(seg, s)->name,
+ seg_le(seg, s),
+ (s == seg->area_count - 1) ? "" : ",");
+ break;
+ case AREA_UNASSIGNED:
+ return 0;
+ }
+ }
+
+ _dec_indent(f);
+ outf(f, "]");
+ return 1;
+}
+
+static int _print_lv(struct formatter *f, struct logical_volume *lv)
+{
+ struct lv_segment *seg;
+ char buffer[4096];
+ int seg_count;
+
+ outnl(f);
+ outf(f, "%s {", lv->name);
+ _inc_indent(f);
+
+ /* FIXME: Write full lvid */
+ if (!id_write_format(&lv->lvid.id[1], buffer, sizeof(buffer)))
+ return_0;
+
+ outf(f, "id = \"%s\"", buffer);
+
+ if (!_print_flag_config(f, lv->status, LV_FLAGS))
+ return_0;
+
+ if (!_out_tags(f, &lv->tags))
+ return_0;
+
+ if (lv->alloc != ALLOC_INHERIT)
+ outf(f, "allocation_policy = \"%s\"",
+ get_alloc_string(lv->alloc));
+
+ switch (lv->read_ahead) {
+ case DM_READ_AHEAD_NONE:
+ outfc(f, "# None", "read_ahead = -1");
+ break;
+ case DM_READ_AHEAD_AUTO:
+ /* No output - use default */
+ break;
+ default:
+ outf(f, "read_ahead = %u", lv->read_ahead);
+ }
+
+ if (lv->major >= 0)
+ outf(f, "major = %d", lv->major);
+ if (lv->minor >= 0)
+ outf(f, "minor = %d", lv->minor);
+ outf(f, "segment_count = %u", dm_list_size(&lv->segments));
+ outnl(f);
+
+ seg_count = 1;
+ dm_list_iterate_items(seg, &lv->segments) {
+ if (!_print_segment(f, lv->vg, seg_count++, seg))
+ return_0;
+ }
+
+ _dec_indent(f);
+ outf(f, "}");
+
+ return 1;
+}
+
+static int _print_lvs(struct formatter *f, struct volume_group *vg)
+{
+ struct lv_list *lvl;
+
+ /*
+ * Don't bother with an lv section if there are no lvs.
+ */
+ if (dm_list_empty(&vg->lvs))
+ return 1;
+
+ outf(f, "logical_volumes {");
+ _inc_indent(f);
+
+ /*
+ * Write visible LVs first
+ */
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (!(lv_is_visible(lvl->lv)))
+ continue;
+ if (!_print_lv(f, lvl->lv))
+ return_0;
+ }
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if ((lv_is_visible(lvl->lv)))
+ continue;
+ if (!_print_lv(f, lvl->lv))
+ return_0;
+ }
+
+ _dec_indent(f);
+ outf(f, "}");
+
+ return 1;
+}
+
+/*
+ * In the text format we refer to pv's as 'pv1',
+ * 'pv2' etc. This function builds a hash table
+ * to enable a quick lookup from device -> name.
+ */
+static int _build_pv_names(struct formatter *f, struct volume_group *vg)
+{
+ int count = 0;
+ struct pv_list *pvl;
+ struct physical_volume *pv;
+ char buffer[32], *uuid, *name;
+
+ if (!(f->mem = dm_pool_create("text pv_names", 512)))
+ return_0;
+
+ if (!(f->pv_names = dm_hash_create(128)))
+ return_0;
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ pv = pvl->pv;
+
+ /* FIXME But skip if there's already an LV called pv%d ! */
+ if (dm_snprintf(buffer, sizeof(buffer), "pv%d", count++) < 0)
+ return_0;
+
+ if (!(name = dm_pool_strdup(f->mem, buffer)))
+ return_0;
+
+ if (!(uuid = dm_pool_zalloc(f->mem, 64)) ||
+ !id_write_format(&pv->id, uuid, 64))
+ return_0;
+
+ if (!dm_hash_insert(f->pv_names, uuid, name))
+ return_0;
+ }
+
+ return 1;
+}
+
+static int _text_vg_export(struct formatter *f,
+ struct volume_group *vg, const char *desc)
+{
+ int r = 0;
+
+ if (!_build_pv_names(f, vg))
+ goto_out;
+
+ if (f->header && !_print_header(f, desc))
+ goto_out;
+
+ if (!out_text(f, "%s {", vg->name))
+ goto_out;
+
+ _inc_indent(f);
+
+ if (!_print_vg(f, vg))
+ goto_out;
+
+ outnl(f);
+ if (!_print_pvs(f, vg))
+ goto_out;
+
+ outnl(f);
+ if (!_print_lvs(f, vg))
+ goto_out;
+
+ _dec_indent(f);
+ if (!out_text(f, "}"))
+ goto_out;
+
+ if (!f->header && !_print_header(f, desc))
+ goto_out;
+
+ r = 1;
+
+ out:
+ if (f->mem)
+ dm_pool_destroy(f->mem);
+
+ if (f->pv_names)
+ dm_hash_destroy(f->pv_names);
+
+ return r;
+}
+
+int text_vg_export_file(struct volume_group *vg, const char *desc, FILE *fp)
+{
+ struct formatter *f;
+ int r;
+
+ _init();
+
+ if (!(f = dm_zalloc(sizeof(*f))))
+ return_0;
+
+ f->data.fp = fp;
+ f->indent = 0;
+ f->header = 1;
+ f->out_with_comment = &_out_with_comment_file;
+ f->nl = &_nl_file;
+
+ r = _text_vg_export(f, vg, desc);
+ if (r)
+ r = !ferror(f->data.fp);
+ dm_free(f);
+ return r;
+}
+
+/* Returns amount of buffer used incl. terminating NUL */
+int text_vg_export_raw(struct volume_group *vg, const char *desc, char **buf)
+{
+ struct formatter *f;
+ int r = 0;
+
+ _init();
+
+ if (!(f = dm_zalloc(sizeof(*f))))
+ return_0;
+
+ f->data.buf.size = 65536; /* Initial metadata limit */
+ if (!(f->data.buf.start = dm_malloc(f->data.buf.size))) {
+ log_error("text_export buffer allocation failed");
+ goto out;
+ }
+
+ f->indent = 0;
+ f->header = 0;
+ f->out_with_comment = &_out_with_comment_raw;
+ f->nl = &_nl_raw;
+
+ if (!_text_vg_export(f, vg, desc)) {
+ dm_free(f->data.buf.start);
+ goto_out;
+ }
+
+ r = f->data.buf.used + 1;
+ *buf = f->data.buf.start;
+
+ out:
+ dm_free(f);
+ return r;
+}
+
+int export_vg_to_buffer(struct volume_group *vg, char **buf)
+{
+ return text_vg_export_raw(vg, "", buf);
+}
+
+#undef outf
+#undef outnl
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "import-export.h"
+#include "lvm-string.h"
+
+/*
+ * Bitsets held in the 'status' flags get
+ * converted into arrays of strings.
+ */
+struct flag {
+ const uint64_t mask;
+ const char *description;
+ int kind;
+};
+
+static const struct flag _vg_flags[] = {
+ {EXPORTED_VG, "EXPORTED", STATUS_FLAG},
+ {RESIZEABLE_VG, "RESIZEABLE", STATUS_FLAG},
+ {PVMOVE, "PVMOVE", STATUS_FLAG},
+ {LVM_READ, "READ", STATUS_FLAG},
+ {LVM_WRITE, "WRITE", STATUS_FLAG},
+ {CLUSTERED, "CLUSTERED", STATUS_FLAG},
+ {SHARED, "SHARED", STATUS_FLAG},
+ {PARTIAL_VG, NULL, 0},
+ {PRECOMMITTED, NULL, 0},
+ {0, NULL, 0}
+};
+
+static const struct flag _pv_flags[] = {
+ {ALLOCATABLE_PV, "ALLOCATABLE", STATUS_FLAG},
+ {EXPORTED_VG, "EXPORTED", STATUS_FLAG},
+ {MISSING_PV, "MISSING", COMPATIBLE_FLAG},
+ {0, NULL, 0}
+};
+
+static const struct flag _lv_flags[] = {
+ {LVM_READ, "READ", STATUS_FLAG},
+ {LVM_WRITE, "WRITE", STATUS_FLAG},
+ {FIXED_MINOR, "FIXED_MINOR", STATUS_FLAG},
+ {VISIBLE_LV, "VISIBLE", STATUS_FLAG},
+ {PVMOVE, "PVMOVE", STATUS_FLAG},
+ {LOCKED, "LOCKED", STATUS_FLAG},
+ {MIRROR_NOTSYNCED, "NOTSYNCED", STATUS_FLAG},
+ {MIRROR_IMAGE, NULL, 0},
+ {MIRROR_LOG, NULL, 0},
+ {MIRRORED, NULL, 0},
+ {VIRTUAL, NULL, 0},
+ {SNAPSHOT, NULL, 0},
+ {MERGING, NULL, 0},
+ {ACTIVATE_EXCL, NULL, 0},
+ {CONVERTING, NULL, 0},
+ {PARTIAL_LV, NULL, 0},
+ {POSTORDER_FLAG, NULL, 0},
+ {VIRTUAL_ORIGIN, NULL, 0},
+ {REPLICATOR, NULL, 0},
+ {REPLICATOR_LOG, NULL, 0},
+ {0, NULL, 0}
+};
+
+static const struct flag *_get_flags(int type)
+{
+ switch (type & ~STATUS_FLAG) {
+ case VG_FLAGS:
+ return _vg_flags;
+
+ case PV_FLAGS:
+ return _pv_flags;
+
+ case LV_FLAGS:
+ return _lv_flags;
+ }
+
+ log_error("Unknown flag set requested.");
+ return NULL;
+}
+
+/*
+ * Converts a bitset to an array of string values,
+ * using one of the tables defined at the top of
+ * the file.
+ */
+int print_flags(uint64_t status, int type, char *buffer, size_t size)
+{
+ int f, first = 1;
+ const struct flag *flags;
+
+ if (!(flags = _get_flags(type)))
+ return_0;
+
+ if (!emit_to_buffer(&buffer, &size, "["))
+ return 0;
+
+ for (f = 0; flags[f].mask; f++) {
+ if (status & flags[f].mask) {
+ status &= ~flags[f].mask;
+
+ if ((type & STATUS_FLAG) != flags[f].kind)
+ continue;
+
+ /* Internal-only flag? */
+ if (!flags[f].description)
+ continue;
+
+ if (!first) {
+ if (!emit_to_buffer(&buffer, &size, ", "))
+ return 0;
+ } else
+ first = 0;
+
+ if (!emit_to_buffer(&buffer, &size, "\"%s\"",
+ flags[f].description))
+ return 0;
+ }
+ }
+
+ if (!emit_to_buffer(&buffer, &size, "]"))
+ return 0;
+
+ if (status)
+ log_error("Metadata inconsistency: Not all flags successfully "
+ "exported.");
+
+ return 1;
+}
+
+int read_flags(uint64_t *status, int type, const struct config_value *cv)
+{
+ int f;
+ uint64_t s = UINT64_C(0);
+ const struct flag *flags;
+
+ if (!(flags = _get_flags(type)))
+ return_0;
+
+ if (cv->type == CFG_EMPTY_ARRAY)
+ goto out;
+
+ while (cv) {
+ if (cv->type != CFG_STRING) {
+ log_error("Status value is not a string.");
+ return 0;
+ }
+
+ for (f = 0; flags[f].description; f++)
+ if (!strcmp(flags[f].description, cv->v.str)) {
+ s |= flags[f].mask;
+ break;
+ }
+
+ if (type == VG_FLAGS && !strcmp(cv->v.str, "PARTIAL")) {
+ /*
+ * Exception: We no longer write this flag out, but it
+ * might be encountered in old backup files, so restore
+ * it in that case. It is never part of live metadata
+ * though, so only vgcfgrestore needs to be concerned
+ * by this case.
+ */
+ s |= PARTIAL_VG;
+ } else if (!flags[f].description && (type & STATUS_FLAG)) {
+ log_error("Unknown status flag '%s'.", cv->v.str);
+ return 0;
+ }
+
+ cv = cv->next;
+ }
+
+ out:
+ *status |= s;
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "format-text.h"
+#include "import-export.h"
+#include "device.h"
+#include "lvm-file.h"
+#include "config.h"
+#include "display.h"
+#include "toolcontext.h"
+#include "lvm-string.h"
+#include "uuid.h"
+#include "layout.h"
+#include "crc.h"
+#include "xlate.h"
+#include "label.h"
+#include "memlock.h"
+#include "lvmcache.h"
+
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/param.h>
+#include <limits.h>
+#include <dirent.h>
+#include <ctype.h>
+
+static struct format_instance *_text_create_text_instance(const struct format_type
+ *fmt, const char *vgname,
+ const char *vgid,
+ void *context);
+
+struct text_fid_context {
+ char *raw_metadata_buf;
+ uint32_t raw_metadata_buf_size;
+};
+
+struct dir_list {
+ struct dm_list list;
+ char dir[0];
+};
+
+struct raw_list {
+ struct dm_list list;
+ struct device_area dev_area;
+};
+
+struct text_context {
+ char *path_live; /* Path to file holding live metadata */
+ char *path_edit; /* Path to file holding edited metadata */
+ char *desc; /* Description placed inside file */
+};
+
+int rlocn_is_ignored(const struct raw_locn *rlocn)
+{
+ return (rlocn->flags & RAW_LOCN_IGNORED ? 1 : 0);
+}
+
+void rlocn_set_ignored(struct raw_locn *rlocn, unsigned mda_ignored)
+{
+ if (mda_ignored)
+ rlocn->flags |= RAW_LOCN_IGNORED;
+ else
+ rlocn->flags &= ~RAW_LOCN_IGNORED;
+}
+
+/*
+ * NOTE: Currently there can be only one vg per text file.
+ */
+
+static int _text_vg_setup(struct format_instance *fid __attribute__((unused)),
+ struct volume_group *vg)
+{
+ if (vg->extent_size & (vg->extent_size - 1)) {
+ log_error("Extent size must be power of 2");
+ return 0;
+ }
+
+ return 1;
+}
+
+static uint64_t _mda_free_sectors_raw(struct metadata_area *mda)
+{
+ struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+
+ return mdac->free_sectors;
+}
+
+static uint64_t _mda_total_sectors_raw(struct metadata_area *mda)
+{
+ struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+
+ return mdac->area.size >> SECTOR_SHIFT;
+}
+
+/*
+ * Check if metadata area belongs to vg
+ */
+static int _mda_in_vg_raw(struct format_instance *fid __attribute__((unused)),
+ struct volume_group *vg, struct metadata_area *mda)
+{
+ struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+ struct pv_list *pvl;
+
+ dm_list_iterate_items(pvl, &vg->pvs)
+ if (pvl->pv->dev == mdac->area.dev)
+ return 1;
+
+ return 0;
+}
+
+static unsigned _mda_locns_match_raw(struct metadata_area *mda1,
+ struct metadata_area *mda2)
+{
+ struct mda_context *mda1c = (struct mda_context *) mda1->metadata_locn;
+ struct mda_context *mda2c = (struct mda_context *) mda2->metadata_locn;
+
+ if ((mda1c->area.dev == mda2c->area.dev) &&
+ (mda1c->area.start == mda2c->area.start) &&
+ (mda1c->area.size == mda2c->area.size))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * For circular region between region_start and region_start + region_size,
+ * back up one SECTOR_SIZE from 'region_ptr' and return the value.
+ * This allows reverse traversal through text metadata area to find old
+ * metadata.
+ *
+ * Parameters:
+ * region_start: start of the region (bytes)
+ * region_size: size of the region (bytes)
+ * region_ptr: pointer within the region (bytes)
+ * NOTE: region_start <= region_ptr <= region_start + region_size
+ */
+static uint64_t _get_prev_sector_circular(uint64_t region_start,
+ uint64_t region_size,
+ uint64_t region_ptr)
+{
+ if (region_ptr >= region_start + SECTOR_SIZE)
+ return region_ptr - SECTOR_SIZE;
+ else
+ return (region_start + region_size - SECTOR_SIZE);
+}
+
+/*
+ * Analyze a metadata area for old metadata records in the circular buffer.
+ * This function just looks through and makes a first pass at the data in
+ * the sectors for particular things.
+ * FIXME: do something with each metadata area (try to extract vg, write
+ * raw data to file, etc)
+ */
+static int _pv_analyze_mda_raw (const struct format_type * fmt,
+ struct metadata_area *mda)
+{
+ struct mda_header *mdah;
+ struct raw_locn *rlocn;
+ uint64_t area_start;
+ uint64_t area_size;
+ uint64_t prev_sector, prev_sector2;
+ uint64_t latest_mrec_offset;
+ uint64_t offset;
+ uint64_t offset2;
+ size_t size;
+ size_t size2;
+ char *buf=NULL;
+ struct device_area *area;
+ struct mda_context *mdac;
+ int r=0;
+
+ mdac = (struct mda_context *) mda->metadata_locn;
+
+ log_print("Found text metadata area: offset=%" PRIu64 ", size=%"
+ PRIu64, mdac->area.start, mdac->area.size);
+ area = &mdac->area;
+
+ if (!dev_open(area->dev))
+ return_0;
+
+ if (!(mdah = raw_read_mda_header(fmt, area)))
+ goto_out;
+
+ rlocn = mdah->raw_locns;
+
+ /*
+ * The device area includes the metadata header as well as the
+ * records, so remove the metadata header from the start and size
+ */
+ area_start = area->start + MDA_HEADER_SIZE;
+ area_size = area->size - MDA_HEADER_SIZE;
+ latest_mrec_offset = rlocn->offset + area->start;
+
+ /*
+ * Start searching at rlocn (point of live metadata) and go
+ * backwards.
+ */
+ prev_sector = _get_prev_sector_circular(area_start, area_size,
+ latest_mrec_offset);
+ offset = prev_sector;
+ size = SECTOR_SIZE;
+ offset2 = size2 = 0;
+
+ while (prev_sector != latest_mrec_offset) {
+ prev_sector2 = prev_sector;
+ prev_sector = _get_prev_sector_circular(area_start, area_size,
+ prev_sector);
+ if (prev_sector > prev_sector2)
+ goto_out;
+ /*
+ * FIXME: for some reason, the whole metadata region from
+ * area->start to area->start+area->size is not used.
+ * Only ~32KB seems to contain valid metadata records
+ * (LVM2 format - format_text). As a result, I end up with
+ * "maybe_config_section" returning true when there's no valid
+ * metadata in a sector (sectors with all nulls).
+ */
+ if (!(buf = dm_pool_alloc(fmt->cmd->mem, size + size2)))
+ goto_out;
+
+ if (!dev_read_circular(area->dev, offset, size,
+ offset2, size2, buf))
+ goto_out;
+
+ /*
+ * FIXME: We could add more sophisticated metadata detection
+ */
+ if (maybe_config_section(buf, size + size2)) {
+ /* FIXME: Validate region, pull out timestamp?, etc */
+ /* FIXME: Do something with this region */
+ log_verbose ("Found LVM2 metadata record at "
+ "offset=%"PRIu64", size=%"PRIsize_t", "
+ "offset2=%"PRIu64" size2=%"PRIsize_t,
+ offset, size, offset2, size2);
+ offset = prev_sector;
+ size = SECTOR_SIZE;
+ offset2 = size2 = 0;
+ } else {
+ /*
+ * Not a complete metadata record, assume we have
+ * metadata and just increase the size and offset.
+ * Start the second region if the previous sector is
+ * wrapping around towards the end of the disk.
+ */
+ if (prev_sector > offset) {
+ offset2 = prev_sector;
+ size2 += SECTOR_SIZE;
+ } else {
+ offset = prev_sector;
+ size += SECTOR_SIZE;
+ }
+ }
+ dm_pool_free(fmt->cmd->mem, buf);
+ buf = NULL;
+ }
+
+ r = 1;
+ out:
+ if (buf)
+ dm_pool_free(fmt->cmd->mem, buf);
+ if (!dev_close(area->dev))
+ stack;
+ return r;
+}
+
+
+
+static int _text_lv_setup(struct format_instance *fid __attribute__((unused)),
+ struct logical_volume *lv)
+{
+/******** FIXME Any LV size restriction?
+ uint64_t max_size = UINT_MAX;
+
+ if (lv->size > max_size) {
+ char *dummy = display_size(max_size);
+ log_error("logical volumes cannot be larger than %s", dummy);
+ dm_free(dummy);
+ return 0;
+ }
+*/
+
+ if (!*lv->lvid.s && !lvid_create(&lv->lvid, &lv->vg->id)) {
+ log_error("Random lvid creation failed for %s/%s.",
+ lv->vg->name, lv->name);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void _xlate_mdah(struct mda_header *mdah)
+{
+ struct raw_locn *rl;
+
+ mdah->version = xlate32(mdah->version);
+ mdah->start = xlate64(mdah->start);
+ mdah->size = xlate64(mdah->size);
+
+ rl = &mdah->raw_locns[0];
+ while (rl->offset) {
+ rl->checksum = xlate32(rl->checksum);
+ rl->offset = xlate64(rl->offset);
+ rl->size = xlate64(rl->size);
+ rl++;
+ }
+}
+
+struct mda_header *raw_read_mda_header(const struct format_type *fmt,
+ struct device_area *dev_area)
+{
+ struct mda_header *mdah;
+
+ if (!(mdah = dm_pool_alloc(fmt->cmd->mem, MDA_HEADER_SIZE))) {
+ log_error("struct mda_header allocation failed");
+ return NULL;
+ }
+
+ if (!dev_read(dev_area->dev, dev_area->start, MDA_HEADER_SIZE, mdah))
+ goto_bad;
+
+ if (mdah->checksum_xl != xlate32(calc_crc(INITIAL_CRC, (uint8_t *)mdah->magic,
+ MDA_HEADER_SIZE -
+ sizeof(mdah->checksum_xl)))) {
+ log_error("Incorrect metadata area header checksum on %s"
+ " at offset %"PRIu64, dev_name(dev_area->dev),
+ dev_area->start);
+ goto bad;
+ }
+
+ _xlate_mdah(mdah);
+
+ if (strncmp((char *)mdah->magic, FMTT_MAGIC, sizeof(mdah->magic))) {
+ log_error("Wrong magic number in metadata area header on %s"
+ " at offset %"PRIu64, dev_name(dev_area->dev),
+ dev_area->start);
+ goto bad;
+ }
+
+ if (mdah->version != FMTT_VERSION) {
+ log_error("Incompatible metadata area header version: %d on %s"
+ " at offset %"PRIu64, mdah->version,
+ dev_name(dev_area->dev), dev_area->start);
+ goto bad;
+ }
+
+ if (mdah->start != dev_area->start) {
+ log_error("Incorrect start sector in metadata area header: %"
+ PRIu64" on %s at offset %"PRIu64, mdah->start,
+ dev_name(dev_area->dev), dev_area->start);
+ goto bad;
+ }
+
+ return mdah;
+
+bad:
+ dm_pool_free(fmt->cmd->mem, mdah);
+ return NULL;
+}
+
+static int _raw_write_mda_header(const struct format_type *fmt,
+ struct device *dev,
+ uint64_t start_byte, struct mda_header *mdah)
+{
+ strncpy((char *)mdah->magic, FMTT_MAGIC, sizeof(mdah->magic));
+ mdah->version = FMTT_VERSION;
+ mdah->start = start_byte;
+
+ _xlate_mdah(mdah);
+ mdah->checksum_xl = xlate32(calc_crc(INITIAL_CRC, (uint8_t *)mdah->magic,
+ MDA_HEADER_SIZE -
+ sizeof(mdah->checksum_xl)));
+
+ if (!dev_write(dev, start_byte, MDA_HEADER_SIZE, mdah))
+ return_0;
+
+ return 1;
+}
+
+static struct raw_locn *_find_vg_rlocn(struct device_area *dev_area,
+ struct mda_header *mdah,
+ const char *vgname,
+ int *precommitted)
+{
+ size_t len;
+ char vgnamebuf[NAME_LEN + 2] __attribute__((aligned(8)));
+ struct raw_locn *rlocn, *rlocn_precommitted;
+ struct lvmcache_info *info;
+
+ rlocn = mdah->raw_locns; /* Slot 0 */
+ rlocn_precommitted = rlocn + 1; /* Slot 1 */
+
+ /* Should we use precommitted metadata? */
+ if (*precommitted && rlocn_precommitted->size &&
+ (rlocn_precommitted->offset != rlocn->offset)) {
+ rlocn = rlocn_precommitted;
+ } else
+ *precommitted = 0;
+
+ /* Do not check non-existent metadata. */
+ if (!rlocn->offset && !rlocn->size)
+ return NULL;
+
+ /*
+ * Don't try to check existing metadata
+ * if given vgname is an empty string.
+ */
+ if (!*vgname)
+ return rlocn;
+
+ /* FIXME Loop through rlocns two-at-a-time. List null-terminated. */
+ /* FIXME Ignore if checksum incorrect!!! */
+ if (!dev_read(dev_area->dev, dev_area->start + rlocn->offset,
+ sizeof(vgnamebuf), vgnamebuf))
+ goto_bad;
+
+ if (!strncmp(vgnamebuf, vgname, len = strlen(vgname)) &&
+ (isspace(vgnamebuf[len]) || vgnamebuf[len] == '{'))
+ return rlocn;
+ else
+ log_debug("Volume group name found in metadata does "
+ "not match expected name %s.", vgname);
+
+ bad:
+ if ((info = info_from_pvid(dev_area->dev->pvid, 0)))
+ lvmcache_update_vgname_and_id(info, FMT_TEXT_ORPHAN_VG_NAME,
+ FMT_TEXT_ORPHAN_VG_NAME, 0, NULL);
+
+ return NULL;
+}
+
+/*
+ * Determine offset for uncommitted metadata
+ */
+static uint64_t _next_rlocn_offset(struct raw_locn *rlocn,
+ struct mda_header *mdah)
+{
+ if (!rlocn)
+ /* Find an empty slot */
+ /* FIXME Assume only one VG per mdah for now */
+ return MDA_HEADER_SIZE;
+
+ /* Start of free space - round up to next sector; circular */
+ return ((rlocn->offset + rlocn->size +
+ (SECTOR_SIZE - rlocn->size % SECTOR_SIZE) -
+ MDA_HEADER_SIZE) % (mdah->size - MDA_HEADER_SIZE))
+ + MDA_HEADER_SIZE;
+}
+
+static int _raw_holds_vgname(struct format_instance *fid,
+ struct device_area *dev_area, const char *vgname)
+{
+ int r = 0;
+ int noprecommit = 0;
+ struct mda_header *mdah;
+
+ if (!dev_open(dev_area->dev))
+ return_0;
+
+ if (!(mdah = raw_read_mda_header(fid->fmt, dev_area)))
+ return_0;
+
+ if (_find_vg_rlocn(dev_area, mdah, vgname, &noprecommit))
+ r = 1;
+
+ if (!dev_close(dev_area->dev))
+ stack;
+
+ return r;
+}
+
+static struct volume_group *_vg_read_raw_area(struct format_instance *fid,
+ const char *vgname,
+ struct device_area *area,
+ int precommitted)
+{
+ struct volume_group *vg = NULL;
+ struct raw_locn *rlocn;
+ struct mda_header *mdah;
+ time_t when;
+ char *desc;
+ uint32_t wrap = 0;
+
+ if (!(mdah = raw_read_mda_header(fid->fmt, area)))
+ goto_out;
+
+ if (!(rlocn = _find_vg_rlocn(area, mdah, vgname, &precommitted))) {
+ log_debug("VG %s not found on %s", vgname, dev_name(area->dev));
+ goto out;
+ }
+
+ if (rlocn->offset + rlocn->size > mdah->size)
+ wrap = (uint32_t) ((rlocn->offset + rlocn->size) - mdah->size);
+
+ if (wrap > rlocn->offset) {
+ log_error("VG %s metadata too large for circular buffer",
+ vgname);
+ goto out;
+ }
+
+ /* FIXME 64-bit */
+ if (!(vg = text_vg_import_fd(fid, NULL, area->dev,
+ (off_t) (area->start + rlocn->offset),
+ (uint32_t) (rlocn->size - wrap),
+ (off_t) (area->start + MDA_HEADER_SIZE),
+ wrap, calc_crc, rlocn->checksum, &when,
+ &desc)))
+ goto_out;
+ log_debug("Read %s %smetadata (%u) from %s at %" PRIu64 " size %"
+ PRIu64, vg->name, precommitted ? "pre-commit " : "",
+ vg->seqno, dev_name(area->dev),
+ area->start + rlocn->offset, rlocn->size);
+
+ if (precommitted)
+ vg->status |= PRECOMMITTED;
+
+ out:
+ return vg;
+}
+
+static struct volume_group *_vg_read_raw(struct format_instance *fid,
+ const char *vgname,
+ struct metadata_area *mda)
+{
+ struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+ struct volume_group *vg;
+
+ if (!dev_open(mdac->area.dev))
+ return_NULL;
+
+ vg = _vg_read_raw_area(fid, vgname, &mdac->area, 0);
+
+ if (!dev_close(mdac->area.dev))
+ stack;
+
+ return vg;
+}
+
+static struct volume_group *_vg_read_precommit_raw(struct format_instance *fid,
+ const char *vgname,
+ struct metadata_area *mda)
+{
+ struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+ struct volume_group *vg;
+
+ if (!dev_open(mdac->area.dev))
+ return_NULL;
+
+ vg = _vg_read_raw_area(fid, vgname, &mdac->area, 1);
+
+ if (!dev_close(mdac->area.dev))
+ stack;
+
+ return vg;
+}
+
+static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg,
+ struct metadata_area *mda)
+{
+ struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+ struct text_fid_context *fidtc = (struct text_fid_context *) fid->private;
+ struct raw_locn *rlocn;
+ struct mda_header *mdah;
+ struct pv_list *pvl;
+ int r = 0;
+ uint64_t new_wrap = 0, old_wrap = 0, new_end;
+ int found = 0;
+ int noprecommit = 0;
+
+ /* Ignore any mda on a PV outside the VG. vgsplit relies on this */
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (pvl->pv->dev == mdac->area.dev) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return 1;
+
+ if (!dev_open(mdac->area.dev))
+ return_0;
+
+ if (!(mdah = raw_read_mda_header(fid->fmt, &mdac->area)))
+ goto_out;
+
+ rlocn = _find_vg_rlocn(&mdac->area, mdah,
+ vg->old_name ? vg->old_name : vg->name, &noprecommit);
+ mdac->rlocn.offset = _next_rlocn_offset(rlocn, mdah);
+
+ if (!fidtc->raw_metadata_buf &&
+ !(fidtc->raw_metadata_buf_size =
+ text_vg_export_raw(vg, "", &fidtc->raw_metadata_buf))) {
+ log_error("VG %s metadata writing failed", vg->name);
+ goto out;
+ }
+
+ mdac->rlocn.size = fidtc->raw_metadata_buf_size;
+
+ if (mdac->rlocn.offset + mdac->rlocn.size > mdah->size)
+ new_wrap = (mdac->rlocn.offset + mdac->rlocn.size) - mdah->size;
+
+ if (rlocn && (rlocn->offset + rlocn->size > mdah->size))
+ old_wrap = (rlocn->offset + rlocn->size) - mdah->size;
+
+ new_end = new_wrap ? new_wrap + MDA_HEADER_SIZE :
+ mdac->rlocn.offset + mdac->rlocn.size;
+
+ if ((new_wrap && old_wrap) ||
+ (rlocn && (new_wrap || old_wrap) && (new_end > rlocn->offset)) ||
+ (mdac->rlocn.size >= mdah->size)) {
+ log_error("VG %s metadata too large for circular buffer",
+ vg->name);
+ goto out;
+ }
+
+ log_debug("Writing %s metadata to %s at %" PRIu64 " len %" PRIu64,
+ vg->name, dev_name(mdac->area.dev), mdac->area.start +
+ mdac->rlocn.offset, mdac->rlocn.size - new_wrap);
+
+ /* Write text out, circularly */
+ if (!dev_write(mdac->area.dev, mdac->area.start + mdac->rlocn.offset,
+ (size_t) (mdac->rlocn.size - new_wrap),
+ fidtc->raw_metadata_buf))
+ goto_out;
+
+ if (new_wrap) {
+ log_debug("Writing metadata to %s at %" PRIu64 " len %" PRIu64,
+ dev_name(mdac->area.dev), mdac->area.start +
+ MDA_HEADER_SIZE, new_wrap);
+
+ if (!dev_write(mdac->area.dev,
+ mdac->area.start + MDA_HEADER_SIZE,
+ (size_t) new_wrap,
+ fidtc->raw_metadata_buf +
+ mdac->rlocn.size - new_wrap))
+ goto_out;
+ }
+
+ mdac->rlocn.checksum = calc_crc(INITIAL_CRC, (uint8_t *)fidtc->raw_metadata_buf,
+ (uint32_t) (mdac->rlocn.size -
+ new_wrap));
+ if (new_wrap)
+ mdac->rlocn.checksum = calc_crc(mdac->rlocn.checksum,
+ (uint8_t *)fidtc->raw_metadata_buf +
+ mdac->rlocn.size -
+ new_wrap, (uint32_t) new_wrap);
+
+ r = 1;
+
+ out:
+ if (!r) {
+ if (!dev_close(mdac->area.dev))
+ stack;
+
+ if (fidtc->raw_metadata_buf) {
+ dm_free(fidtc->raw_metadata_buf);
+ fidtc->raw_metadata_buf = NULL;
+ }
+ }
+
+ return r;
+}
+
+static int _vg_commit_raw_rlocn(struct format_instance *fid,
+ struct volume_group *vg,
+ struct metadata_area *mda,
+ int precommit)
+{
+ struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+ struct text_fid_context *fidtc = (struct text_fid_context *) fid->private;
+ struct mda_header *mdah;
+ struct raw_locn *rlocn;
+ struct pv_list *pvl;
+ int r = 0;
+ int found = 0;
+ int noprecommit = 0;
+
+ /* Ignore any mda on a PV outside the VG. vgsplit relies on this */
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (pvl->pv->dev == mdac->area.dev) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return 1;
+
+ if (!(mdah = raw_read_mda_header(fid->fmt, &mdac->area)))
+ goto_out;
+
+ if (!(rlocn = _find_vg_rlocn(&mdac->area, mdah,
+ vg->old_name ? vg->old_name : vg->name,
+ &noprecommit))) {
+ mdah->raw_locns[0].offset = 0;
+ mdah->raw_locns[0].size = 0;
+ mdah->raw_locns[0].checksum = 0;
+ mdah->raw_locns[1].offset = 0;
+ mdah->raw_locns[1].size = 0;
+ mdah->raw_locns[1].checksum = 0;
+ mdah->raw_locns[2].offset = 0;
+ mdah->raw_locns[2].size = 0;
+ mdah->raw_locns[2].checksum = 0;
+ rlocn = &mdah->raw_locns[0];
+ }
+
+ if (precommit)
+ rlocn++;
+ else {
+ /* If not precommitting, wipe the precommitted rlocn */
+ mdah->raw_locns[1].offset = 0;
+ mdah->raw_locns[1].size = 0;
+ mdah->raw_locns[1].checksum = 0;
+ }
+
+ /* Is there new metadata to commit? */
+ if (mdac->rlocn.size) {
+ rlocn->offset = mdac->rlocn.offset;
+ rlocn->size = mdac->rlocn.size;
+ rlocn->checksum = mdac->rlocn.checksum;
+ log_debug("%sCommitting %s metadata (%u) to %s header at %"
+ PRIu64, precommit ? "Pre-" : "", vg->name, vg->seqno,
+ dev_name(mdac->area.dev), mdac->area.start);
+ } else
+ log_debug("Wiping pre-committed %s metadata from %s "
+ "header at %" PRIu64, vg->name,
+ dev_name(mdac->area.dev), mdac->area.start);
+
+ rlocn_set_ignored(mdah->raw_locns, mda_is_ignored(mda));
+ if (!_raw_write_mda_header(fid->fmt, mdac->area.dev, mdac->area.start,
+ mdah)) {
+ dm_pool_free(fid->fmt->cmd->mem, mdah);
+ log_error("Failed to write metadata area header");
+ goto out;
+ }
+
+ r = 1;
+
+ out:
+ if (!precommit) {
+ if (!dev_close(mdac->area.dev))
+ stack;
+ if (fidtc->raw_metadata_buf) {
+ dm_free(fidtc->raw_metadata_buf);
+ fidtc->raw_metadata_buf = NULL;
+ }
+ }
+
+ return r;
+}
+
+static int _vg_commit_raw(struct format_instance *fid, struct volume_group *vg,
+ struct metadata_area *mda)
+{
+ return _vg_commit_raw_rlocn(fid, vg, mda, 0);
+}
+
+static int _vg_precommit_raw(struct format_instance *fid,
+ struct volume_group *vg,
+ struct metadata_area *mda)
+{
+ return _vg_commit_raw_rlocn(fid, vg, mda, 1);
+}
+
+/* Close metadata area devices */
+static int _vg_revert_raw(struct format_instance *fid, struct volume_group *vg,
+ struct metadata_area *mda)
+{
+ struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+ struct pv_list *pvl;
+ int found = 0;
+
+ /* Ignore any mda on a PV outside the VG. vgsplit relies on this */
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (pvl->pv->dev == mdac->area.dev) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return 1;
+
+ /* Wipe pre-committed metadata */
+ mdac->rlocn.size = 0;
+ return _vg_commit_raw_rlocn(fid, vg, mda, 0);
+}
+
+static int _vg_remove_raw(struct format_instance *fid, struct volume_group *vg,
+ struct metadata_area *mda)
+{
+ struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
+ struct mda_header *mdah;
+ struct raw_locn *rlocn;
+ int r = 0;
+ int noprecommit = 0;
+
+ if (!dev_open(mdac->area.dev))
+ return_0;
+
+ if (!(mdah = raw_read_mda_header(fid->fmt, &mdac->area)))
+ goto_out;
+
+ if (!(rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name, &noprecommit))) {
+ rlocn = &mdah->raw_locns[0];
+ mdah->raw_locns[1].offset = 0;
+ }
+
+ rlocn->offset = 0;
+ rlocn->size = 0;
+ rlocn->checksum = 0;
+ rlocn_set_ignored(mdah->raw_locns, mda_is_ignored(mda));
+
+ if (!_raw_write_mda_header(fid->fmt, mdac->area.dev, mdac->area.start,
+ mdah)) {
+ dm_pool_free(fid->fmt->cmd->mem, mdah);
+ log_error("Failed to write metadata area header");
+ goto out;
+ }
+
+ r = 1;
+
+ out:
+ if (!dev_close(mdac->area.dev))
+ stack;
+
+ return r;
+}
+
+static struct volume_group *_vg_read_file_name(struct format_instance *fid,
+ const char *vgname,
+ const char *read_path)
+{
+ struct volume_group *vg;
+ time_t when;
+ char *desc;
+
+ if (!(vg = text_vg_import_file(fid, read_path, &when, &desc)))
+ return_NULL;
+
+ /*
+ * Currently you can only have a single volume group per
+ * text file (this restriction may remain). We need to
+ * check that it contains the correct volume group.
+ */
+ if (vgname && strcmp(vgname, vg->name)) {
+ free_vg(vg);
+ log_error("'%s' does not contain volume group '%s'.",
+ read_path, vgname);
+ return NULL;
+ } else
+ log_debug("Read volume group %s from %s", vg->name, read_path);
+
+ return vg;
+}
+
+static struct volume_group *_vg_read_file(struct format_instance *fid,
+ const char *vgname,
+ struct metadata_area *mda)
+{
+ struct text_context *tc = (struct text_context *) mda->metadata_locn;
+
+ return _vg_read_file_name(fid, vgname, tc->path_live);
+}
+
+static struct volume_group *_vg_read_precommit_file(struct format_instance *fid,
+ const char *vgname,
+ struct metadata_area *mda)
+{
+ struct text_context *tc = (struct text_context *) mda->metadata_locn;
+ struct volume_group *vg;
+
+ if ((vg = _vg_read_file_name(fid, vgname, tc->path_edit)))
+ vg->status |= PRECOMMITTED;
+ else
+ vg = _vg_read_file_name(fid, vgname, tc->path_live);
+
+ return vg;
+}
+
+static int _vg_write_file(struct format_instance *fid __attribute__((unused)),
+ struct volume_group *vg, struct metadata_area *mda)
+{
+ struct text_context *tc = (struct text_context *) mda->metadata_locn;
+
+ FILE *fp;
+ int fd;
+ char *slash;
+ char temp_file[PATH_MAX], temp_dir[PATH_MAX];
+
+ slash = strrchr(tc->path_edit, '/');
+
+ if (slash == 0)
+ strcpy(temp_dir, ".");
+ else if (slash - tc->path_edit < PATH_MAX) {
+ strncpy(temp_dir, tc->path_edit,
+ (size_t) (slash - tc->path_edit));
+ temp_dir[slash - tc->path_edit] = '\0';
+
+ } else {
+ log_error("Text format failed to determine directory.");
+ return 0;
+ }
+
+ if (!create_temp_name(temp_dir, temp_file, sizeof(temp_file), &fd,
+ &vg->cmd->rand_seed)) {
+ log_error("Couldn't create temporary text file name.");
+ return 0;
+ }
+
+ if (!(fp = fdopen(fd, "w"))) {
+ log_sys_error("fdopen", temp_file);
+ if (close(fd))
+ log_sys_error("fclose", temp_file);
+ return 0;
+ }
+
+ log_debug("Writing %s metadata to %s", vg->name, temp_file);
+
+ if (!text_vg_export_file(vg, tc->desc, fp)) {
+ log_error("Failed to write metadata to %s.", temp_file);
+ if (fclose(fp))
+ log_sys_error("fclose", temp_file);
+ return 0;
+ }
+
+ if (fsync(fd) && (errno != EROFS) && (errno != EINVAL)) {
+ log_sys_error("fsync", tc->path_edit);
+ if (fclose(fp))
+ log_sys_error("fclose", tc->path_edit);
+ return 0;
+ }
+
+ if (lvm_fclose(fp, tc->path_edit))
+ return_0;
+
+ if (rename(temp_file, tc->path_edit)) {
+ log_debug("Renaming %s to %s", temp_file, tc->path_edit);
+ log_error("%s: rename to %s failed: %s", temp_file,
+ tc->path_edit, strerror(errno));
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _vg_commit_file_backup(struct format_instance *fid __attribute__((unused)),
+ struct volume_group *vg,
+ struct metadata_area *mda)
+{
+ struct text_context *tc = (struct text_context *) mda->metadata_locn;
+
+ if (test_mode()) {
+ log_verbose("Test mode: Skipping committing %s metadata (%u)",
+ vg->name, vg->seqno);
+ if (unlink(tc->path_edit)) {
+ log_debug("Unlinking %s", tc->path_edit);
+ log_sys_error("unlink", tc->path_edit);
+ return 0;
+ }
+ } else {
+ log_debug("Committing %s metadata (%u)", vg->name, vg->seqno);
+ log_debug("Renaming %s to %s", tc->path_edit, tc->path_live);
+ if (rename(tc->path_edit, tc->path_live)) {
+ log_error("%s: rename to %s failed: %s", tc->path_edit,
+ tc->path_live, strerror(errno));
+ return 0;
+ }
+ }
+
+ sync_dir(tc->path_edit);
+
+ return 1;
+}
+
+static int _vg_commit_file(struct format_instance *fid, struct volume_group *vg,
+ struct metadata_area *mda)
+{
+ struct text_context *tc = (struct text_context *) mda->metadata_locn;
+ char *slash;
+ char new_name[PATH_MAX];
+ size_t len;
+
+ if (!_vg_commit_file_backup(fid, vg, mda))
+ return 0;
+
+ /* vgrename? */
+ if ((slash = strrchr(tc->path_live, '/')))
+ slash = slash + 1;
+ else
+ slash = tc->path_live;
+
+ if (strcmp(slash, vg->name)) {
+ len = slash - tc->path_live;
+ strncpy(new_name, tc->path_live, len);
+ strcpy(new_name + len, vg->name);
+ log_debug("Renaming %s to %s", tc->path_live, new_name);
+ if (test_mode())
+ log_verbose("Test mode: Skipping rename");
+ else {
+ if (rename(tc->path_live, new_name)) {
+ log_error("%s: rename to %s failed: %s",
+ tc->path_live, new_name,
+ strerror(errno));
+ sync_dir(new_name);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int _vg_remove_file(struct format_instance *fid __attribute__((unused)),
+ struct volume_group *vg __attribute__((unused)),
+ struct metadata_area *mda)
+{
+ struct text_context *tc = (struct text_context *) mda->metadata_locn;
+
+ if (path_exists(tc->path_edit) && unlink(tc->path_edit)) {
+ log_sys_error("unlink", tc->path_edit);
+ return 0;
+ }
+
+ if (path_exists(tc->path_live) && unlink(tc->path_live)) {
+ log_sys_error("unlink", tc->path_live);
+ return 0;
+ }
+
+ sync_dir(tc->path_live);
+
+ return 1;
+}
+
+static int _scan_file(const struct format_type *fmt, const char *vgname)
+{
+ struct dirent *dirent;
+ struct dir_list *dl;
+ struct dm_list *dir_list;
+ char *tmp;
+ DIR *d;
+ struct volume_group *vg;
+ struct format_instance *fid;
+ char path[PATH_MAX];
+ char *scanned_vgname;
+
+ dir_list = &((struct mda_lists *) fmt->private)->dirs;
+
+ dm_list_iterate_items(dl, dir_list) {
+ if (!(d = opendir(dl->dir))) {
+ log_sys_error("opendir", dl->dir);
+ continue;
+ }
+ while ((dirent = readdir(d)))
+ if (strcmp(dirent->d_name, ".") &&
+ strcmp(dirent->d_name, "..") &&
+ (!(tmp = strstr(dirent->d_name, ".tmp")) ||
+ tmp != dirent->d_name + strlen(dirent->d_name)
+ - 4)) {
+ scanned_vgname = dirent->d_name;
+
+ /* If vgname supplied, only scan that one VG */
+ if (vgname && strcmp(vgname, scanned_vgname))
+ continue;
+
+ if (dm_snprintf(path, PATH_MAX, "%s/%s",
+ dl->dir, scanned_vgname) < 0) {
+ log_error("Name too long %s/%s",
+ dl->dir, scanned_vgname);
+ break;
+ }
+
+ /* FIXME stat file to see if it's changed */
+ fid = _text_create_text_instance(fmt, NULL, NULL,
+ NULL);
+ if ((vg = _vg_read_file_name(fid, scanned_vgname,
+ path))) {
+ /* FIXME Store creation host in vg */
+ lvmcache_update_vg(vg, 0);
+ free_vg(vg);
+ }
+ }
+
+ if (closedir(d))
+ log_sys_error("closedir", dl->dir);
+ }
+
+ return 1;
+}
+
+const char *vgname_from_mda(const struct format_type *fmt,
+ struct mda_header *mdah,
+ struct device_area *dev_area, struct id *vgid,
+ uint64_t *vgstatus, char **creation_host,
+ uint64_t *mda_free_sectors)
+{
+ struct raw_locn *rlocn;
+ uint32_t wrap = 0;
+ const char *vgname = NULL;
+ unsigned int len = 0;
+ char buf[NAME_LEN + 1] __attribute__((aligned(8)));
+ char uuid[64] __attribute__((aligned(8)));
+ uint64_t buffer_size, current_usage;
+
+ if (mda_free_sectors)
+ *mda_free_sectors = ((dev_area->size - MDA_HEADER_SIZE) / 2) >> SECTOR_SHIFT;
+
+ if (!mdah) {
+ log_error(INTERNAL_ERROR "vgname_from_mda called with NULL pointer for mda_header");
+ goto_out;
+ }
+
+ /* FIXME Cope with returning a list */
+ rlocn = mdah->raw_locns;
+
+ /*
+ * If no valid offset, do not try to search for vgname
+ */
+ if (!rlocn->offset)
+ goto out;
+
+ /* Do quick check for a vgname */
+ if (!dev_read(dev_area->dev, dev_area->start + rlocn->offset,
+ NAME_LEN, buf))
+ goto_out;
+
+ while (buf[len] && !isspace(buf[len]) && buf[len] != '{' &&
+ len < (NAME_LEN - 1))
+ len++;
+
+ buf[len] = '\0';
+
+ /* Ignore this entry if the characters aren't permissible */
+ if (!validate_name(buf))
+ goto_out;
+
+ /* We found a VG - now check the metadata */
+ if (rlocn->offset + rlocn->size > mdah->size)
+ wrap = (uint32_t) ((rlocn->offset + rlocn->size) - mdah->size);
+
+ if (wrap > rlocn->offset) {
+ log_error("%s: metadata too large for circular buffer",
+ dev_name(dev_area->dev));
+ goto out;
+ }
+
+ /* FIXME 64-bit */
+ if (!(vgname = text_vgname_import(fmt, dev_area->dev,
+ (off_t) (dev_area->start +
+ rlocn->offset),
+ (uint32_t) (rlocn->size - wrap),
+ (off_t) (dev_area->start +
+ MDA_HEADER_SIZE),
+ wrap, calc_crc, rlocn->checksum,
+ vgid, vgstatus, creation_host)))
+ goto_out;
+
+ /* Ignore this entry if the characters aren't permissible */
+ if (!validate_name(vgname)) {
+ vgname = NULL;
+ goto_out;
+ }
+
+ if (!id_write_format(vgid, uuid, sizeof(uuid))) {
+ vgname = NULL;
+ goto_out;
+ }
+
+ log_debug("%s: Found metadata at %" PRIu64 " size %" PRIu64
+ " (in area at %" PRIu64 " size %" PRIu64
+ ") for %s (%s)",
+ dev_name(dev_area->dev), dev_area->start + rlocn->offset,
+ rlocn->size, dev_area->start, dev_area->size, vgname, uuid);
+
+ if (mda_free_sectors) {
+ current_usage = (rlocn->size + SECTOR_SIZE - UINT64_C(1)) -
+ (rlocn->size + SECTOR_SIZE - UINT64_C(1)) % SECTOR_SIZE;
+ buffer_size = mdah->size - MDA_HEADER_SIZE;
+
+ if (current_usage * 2 >= buffer_size)
+ *mda_free_sectors = UINT64_C(0);
+ else
+ *mda_free_sectors = ((buffer_size - 2 * current_usage) / 2) >> SECTOR_SHIFT;
+ }
+
+ out:
+ return vgname;
+}
+
+static int _scan_raw(const struct format_type *fmt, const char *vgname __attribute__((unused)))
+{
+ struct raw_list *rl;
+ struct dm_list *raw_list;
+ const char *scanned_vgname;
+ struct volume_group *vg;
+ struct format_instance fid;
+ struct id vgid;
+ uint64_t vgstatus;
+ struct mda_header *mdah;
+
+ raw_list = &((struct mda_lists *) fmt->private)->raws;
+
+ fid.fmt = fmt;
+ dm_list_init(&fid.metadata_areas_in_use);
+ dm_list_init(&fid.metadata_areas_ignored);
+
+ dm_list_iterate_items(rl, raw_list) {
+ /* FIXME We're reading mdah twice here... */
+ if (!dev_open(rl->dev_area.dev)) {
+ stack;
+ continue;
+ }
+
+ if (!(mdah = raw_read_mda_header(fmt, &rl->dev_area))) {
+ stack;
+ goto close_dev;
+ }
+
+ if ((scanned_vgname = vgname_from_mda(fmt, mdah,
+ &rl->dev_area, &vgid, &vgstatus,
+ NULL, NULL))) {
+ vg = _vg_read_raw_area(&fid, scanned_vgname, &rl->dev_area, 0);
+ if (vg)
+ lvmcache_update_vg(vg, 0);
+
+ }
+ close_dev:
+ if (!dev_close(rl->dev_area.dev))
+ stack;
+ }
+
+ return 1;
+}
+
+static int _text_scan(const struct format_type *fmt, const char *vgname)
+{
+ return (_scan_file(fmt, vgname) & _scan_raw(fmt, vgname));
+}
+
+/* For orphan, creates new mdas according to policy.
+ Always have an mda between end-of-label and pe_align() boundary */
+static int _mda_setup(const struct format_type *fmt,
+ uint64_t pe_start, uint64_t pe_end,
+ int pvmetadatacopies, uint64_t pvmetadatasize,
+ unsigned metadataignore, struct dm_list *mdas,
+ struct physical_volume *pv,
+ struct volume_group *vg __attribute__((unused)))
+{
+ uint64_t mda_adjustment, disk_size, alignment, alignment_offset;
+ uint64_t start1, mda_size1; /* First area - start of disk */
+ uint64_t start2, mda_size2; /* Second area - end of disk */
+ uint64_t wipe_size = 8 << SECTOR_SHIFT;
+ size_t pagesize = lvm_getpagesize();
+
+ if (!pvmetadatacopies)
+ return 1;
+
+ alignment = pv->pe_align << SECTOR_SHIFT;
+ alignment_offset = pv->pe_align_offset << SECTOR_SHIFT;
+ disk_size = pv->size << SECTOR_SHIFT;
+ pe_start <<= SECTOR_SHIFT;
+ pe_end <<= SECTOR_SHIFT;
+
+ if (pe_end > disk_size) {
+ log_error("Physical extents end beyond end of device %s!",
+ pv_dev_name(pv));
+ return 0;
+ }
+
+ /* Requested metadatasize */
+ mda_size1 = pvmetadatasize << SECTOR_SHIFT;
+
+ /* Place mda straight after label area at start of disk */
+ start1 = LABEL_SCAN_SIZE;
+
+ /* Unless the space available is tiny, round to PAGE_SIZE boundary */
+ if ((!pe_start && !pe_end) ||
+ ((pe_start > start1) && (pe_start - start1 >= MDA_SIZE_MIN))) {
+ mda_adjustment = start1 % pagesize;
+ if (mda_adjustment)
+ start1 += (pagesize - mda_adjustment);
+ }
+
+ /* Round up to pe_align boundary */
+ mda_adjustment = (mda_size1 + start1) % alignment;
+ if (mda_adjustment) {
+ mda_size1 += (alignment - mda_adjustment);
+ /* Revert if it's now too large */
+ if (start1 + mda_size1 > disk_size)
+ mda_size1 -= (alignment - mda_adjustment);
+ }
+
+ /* Add pe_align_offset if on pe_align boundary */
+ if (alignment_offset &&
+ (((start1 + mda_size1) % alignment) == 0)) {
+ mda_size1 += alignment_offset;
+ /* Revert if it's now too large */
+ if (start1 + mda_size1 > disk_size)
+ mda_size1 -= alignment_offset;
+ }
+
+ /* Ensure it's not going to be bigger than the disk! */
+ if (start1 + mda_size1 > disk_size) {
+ log_warn("WARNING: metadata area fills disk leaving no "
+ "space for data on %s.", pv_dev_name(pv));
+ /* Leave some free space for rounding */
+ /* Avoid empty data area as could cause tools problems */
+ mda_size1 = disk_size - start1 - alignment * 2;
+ if (start1 + mda_size1 > disk_size) {
+ log_error("Insufficient space for first mda on %s",
+ pv_dev_name(pv));
+ return 0;
+ }
+ /* Round up to pe_align boundary */
+ mda_adjustment = (mda_size1 + start1) % alignment;
+ if (mda_adjustment)
+ mda_size1 += (alignment - mda_adjustment);
+ /* Only have 1 mda in this case */
+ pvmetadatacopies = 1;
+ }
+
+ /* If we already have PEs, avoid overlap */
+ if (pe_start || pe_end) {
+ if (pe_start <= start1)
+ mda_size1 = 0;
+ else if (start1 + mda_size1 > pe_start)
+ mda_size1 = pe_start - start1;
+ }
+
+ /* FIXME If creating new mdas, wipe them! */
+ if (mda_size1) {
+ if (!add_mda(fmt, fmt->cmd->mem, mdas, pv->dev, start1,
+ mda_size1, metadataignore))
+ return 0;
+
+ if (!dev_set((struct device *) pv->dev, start1,
+ (size_t) (mda_size1 >
+ wipe_size ? : mda_size1), 0)) {
+ log_error("Failed to wipe new metadata area");
+ return 0;
+ }
+
+ if (pvmetadatacopies == 1)
+ return 1;
+ } else
+ start1 = 0;
+
+ /* A second copy at end of disk */
+ mda_size2 = pvmetadatasize << SECTOR_SHIFT;
+
+ /* Ensure it's not going to be bigger than the disk! */
+ if (mda_size2 > disk_size)
+ mda_size2 = disk_size - start1 - mda_size1;
+
+ mda_adjustment = (disk_size - mda_size2) % alignment;
+ if (mda_adjustment)
+ mda_size2 += mda_adjustment;
+
+ start2 = disk_size - mda_size2;
+
+ /* If we already have PEs, avoid overlap */
+ if (pe_start || pe_end) {
+ if (start2 < pe_end) {
+ mda_size2 -= (pe_end - start2);
+ start2 = pe_end;
+ }
+ }
+
+ /* If we already have a first mda, avoid overlap */
+ if (mda_size1) {
+ if (start2 < start1 + mda_size1) {
+ mda_size2 -= (start1 + mda_size1 - start2);
+ start2 = start1 + mda_size1;
+ }
+ /* No room for any PEs here now! */
+ }
+
+ if (mda_size2) {
+ if (!add_mda(fmt, fmt->cmd->mem, mdas, pv->dev, start2,
+ mda_size2, metadataignore)) return 0;
+ if (!dev_set(pv->dev, start2,
+ (size_t) (mda_size1 >
+ wipe_size ? : mda_size1), 0)) {
+ log_error("Failed to wipe new metadata area");
+ return 0;
+ }
+ } else
+ return 0;
+
+ return 1;
+}
+
+/* Only for orphans */
+/* Set label_sector to -1 if rewriting existing label into same sector */
+/* If mdas is supplied it overwrites existing mdas e.g. used with pvcreate */
+static int _text_pv_write(const struct format_type *fmt, struct physical_volume *pv,
+ struct dm_list *mdas, int64_t label_sector)
+{
+ struct label *label;
+ struct lvmcache_info *info;
+ struct mda_context *mdac;
+ struct metadata_area *mda;
+ char buf[MDA_HEADER_SIZE] __attribute__((aligned(8)));
+ struct mda_header *mdah = (struct mda_header *) buf;
+ uint64_t adjustment;
+ struct data_area_list *da;
+
+ /* FIXME Test mode don't update cache? */
+
+ if (!(info = lvmcache_add(fmt->labeller, (char *) &pv->id, pv->dev,
+ FMT_TEXT_ORPHAN_VG_NAME, NULL, 0)))
+ return_0;
+ label = info->label;
+
+ if (label_sector != -1)
+ label->sector = label_sector;
+
+ info->device_size = pv->size << SECTOR_SHIFT;
+ info->fmt = fmt;
+
+ /* If mdas supplied, use them regardless of existing ones, */
+ /* otherwise retain existing ones */
+ if (mdas) {
+ if (info->mdas.n)
+ del_mdas(&info->mdas);
+ else
+ dm_list_init(&info->mdas);
+ dm_list_iterate_items(mda, mdas) {
+ mdac = mda->metadata_locn;
+ log_debug("Creating metadata area on %s at sector %"
+ PRIu64 " size %" PRIu64 " sectors",
+ dev_name(mdac->area.dev),
+ mdac->area.start >> SECTOR_SHIFT,
+ mdac->area.size >> SECTOR_SHIFT);
+ add_mda(fmt, NULL, &info->mdas, mdac->area.dev,
+ mdac->area.start, mdac->area.size, mda_is_ignored(mda));
+ }
+ /* FIXME Temporary until mda creation supported by tools */
+ } else if (!info->mdas.n) {
+ dm_list_init(&info->mdas);
+ }
+
+ /*
+ * If no pe_start supplied but PV already exists,
+ * get existing value; use-cases include:
+ * - pvcreate on PV without prior pvremove
+ * - vgremove on VG with PV(s) that have pe_start=0 (hacked cfg)
+ */
+ if (info->das.n) {
+ if (!pv->pe_start)
+ dm_list_iterate_items(da, &info->das)
+ pv->pe_start = da->disk_locn.offset >> SECTOR_SHIFT;
+ del_das(&info->das);
+ } else
+ dm_list_init(&info->das);
+
+#if 0
+ /*
+ * FIXME: ideally a pre-existing pe_start seen in .pv_write
+ * would always be preserved BUT 'pvcreate on PV without prior pvremove'
+ * could easily cause the pe_start to overlap with the first mda!
+ */
+ if (pv->pe_start) {
+ log_very_verbose("%s: preserving pe_start=%lu",
+ pv_dev_name(pv), pv->pe_start);
+ goto preserve_pe_start;
+ }
+#endif
+
+ /*
+ * If pe_start is still unset, set it to first aligned
+ * sector after any metadata areas that begin before pe_start.
+ */
+ if (!pv->pe_start) {
+ pv->pe_start = pv->pe_align;
+ if (pv->pe_align_offset)
+ pv->pe_start += pv->pe_align_offset;
+ }
+ dm_list_iterate_items(mda, &info->mdas) {
+ mdac = (struct mda_context *) mda->metadata_locn;
+ if (pv->dev == mdac->area.dev &&
+ ((mdac->area.start <= (pv->pe_start << SECTOR_SHIFT)) ||
+ (mdac->area.start <= lvm_getpagesize() &&
+ pv->pe_start < (lvm_getpagesize() >> SECTOR_SHIFT))) &&
+ (mdac->area.start + mdac->area.size >
+ (pv->pe_start << SECTOR_SHIFT))) {
+ pv->pe_start = (mdac->area.start + mdac->area.size)
+ >> SECTOR_SHIFT;
+ /* Adjust pe_start to: (N * pe_align) + pe_align_offset */
+ if (pv->pe_align) {
+ adjustment =
+ (pv->pe_start - pv->pe_align_offset) % pv->pe_align;
+ if (adjustment)
+ pv->pe_start += (pv->pe_align - adjustment);
+
+ log_very_verbose("%s: setting pe_start=%" PRIu64
+ " (orig_pe_start=%" PRIu64 ", "
+ "pe_align=%lu, pe_align_offset=%lu, "
+ "adjustment=%" PRIu64 ")",
+ pv_dev_name(pv), pv->pe_start,
+ (adjustment ?
+ pv->pe_start - (pv->pe_align - adjustment) :
+ pv->pe_start),
+ pv->pe_align, pv->pe_align_offset, adjustment);
+ }
+ }
+ }
+ if (pv->pe_start >= pv->size) {
+ log_error("Data area is beyond end of device %s!",
+ pv_dev_name(pv));
+ return 0;
+ }
+
+ /* FIXME: preserve_pe_start: */
+ if (!add_da
+ (NULL, &info->das, pv->pe_start << SECTOR_SHIFT, UINT64_C(0)))
+ return_0;
+
+ if (!dev_open(pv->dev))
+ return_0;
+
+ dm_list_iterate_items(mda, &info->mdas) {
+ mdac = mda->metadata_locn;
+ memset(&buf, 0, sizeof(buf));
+ mdah->size = mdac->area.size;
+ rlocn_set_ignored(mdah->raw_locns, mda_is_ignored(mda));
+ if (!_raw_write_mda_header(fmt, mdac->area.dev,
+ mdac->area.start, mdah)) {
+ if (!dev_close(pv->dev))
+ stack;
+ return_0;
+ }
+ }
+
+ if (!label_write(pv->dev, label)) {
+ dev_close(pv->dev);
+ return_0;
+ }
+
+ if (!dev_close(pv->dev))
+ return_0;
+
+ return 1;
+}
+
+static int _add_raw(struct dm_list *raw_list, struct device_area *dev_area)
+{
+ struct raw_list *rl;
+
+ /* Already present? */
+ dm_list_iterate_items(rl, raw_list) {
+ /* FIXME Check size/overlap consistency too */
+ if (rl->dev_area.dev == dev_area->dev &&
+ rl->dev_area.start == dev_area->start)
+ return 1;
+ }
+
+ if (!(rl = dm_malloc(sizeof(struct raw_list)))) {
+ log_error("_add_raw allocation failed");
+ return 0;
+ }
+ memcpy(&rl->dev_area, dev_area, sizeof(*dev_area));
+ dm_list_add(raw_list, &rl->list);
+
+ return 1;
+}
+
+static int _get_pv_if_in_vg(struct lvmcache_info *info,
+ struct physical_volume *pv)
+{
+ if (info->vginfo && info->vginfo->vgname &&
+ !is_orphan_vg(info->vginfo->vgname) &&
+ get_pv_from_vg_by_id(info->fmt, info->vginfo->vgname,
+ info->vginfo->vgid, info->dev->pvid, pv))
+ return 1;
+
+ return 0;
+}
+
+static int _populate_pv_fields(struct lvmcache_info *info,
+ struct physical_volume *pv,
+ int scan_label_only)
+{
+ struct data_area_list *da;
+
+ /* Have we already cached vgname? */
+ if (!scan_label_only && _get_pv_if_in_vg(info, pv))
+ return 1;
+
+ /* Perform full scan (just the first time) and try again */
+ if (!scan_label_only && !memlock() && !full_scan_done()) {
+ lvmcache_label_scan(info->fmt->cmd, 2);
+
+ if (_get_pv_if_in_vg(info, pv))
+ return 1;
+ }
+
+ /* Orphan */
+ pv->dev = info->dev;
+ pv->fmt = info->fmt;
+ pv->size = info->device_size >> SECTOR_SHIFT;
+ pv->vg_name = FMT_TEXT_ORPHAN_VG_NAME;
+ memcpy(&pv->id, &info->dev->pvid, sizeof(pv->id));
+
+ /* Currently only support exactly one data area */
+ if (dm_list_size(&info->das) != 1) {
+ log_error("Must be exactly one data area (found %d) on PV %s",
+ dm_list_size(&info->das), dev_name(info->dev));
+ return 0;
+ }
+
+ dm_list_iterate_items(da, &info->das)
+ pv->pe_start = da->disk_locn.offset >> SECTOR_SHIFT;
+
+ return 1;
+}
+
+/*
+ * Copy constructor for a metadata_locn.
+ */
+static void *_metadata_locn_copy_raw(struct dm_pool *mem, void *metadata_locn)
+{
+ struct mda_context *mdac, *mdac_new;
+
+ mdac = (struct mda_context *) metadata_locn;
+ if (!(mdac_new = dm_pool_alloc(mem, sizeof(*mdac_new)))) {
+ log_error("mda_context allocation failed");
+ return NULL;
+ }
+ memcpy(mdac_new, mdac, sizeof(*mdac));
+
+ return mdac_new;
+}
+
+/*
+ * Return a string description of the metadata location.
+ */
+static const char *_metadata_locn_name_raw(void *metadata_locn)
+{
+ struct mda_context *mdac = (struct mda_context *) metadata_locn;
+
+ return dev_name(mdac->area.dev);
+}
+
+static uint64_t _metadata_locn_offset_raw(void *metadata_locn)
+{
+ struct mda_context *mdac = (struct mda_context *) metadata_locn;
+
+ return mdac->area.start;
+}
+
+static int _text_pv_read(const struct format_type *fmt, const char *pv_name,
+ struct physical_volume *pv, struct dm_list *mdas,
+ int scan_label_only)
+{
+ struct metadata_area *mda, *mda_new;
+ struct label *label;
+ struct device *dev;
+ struct lvmcache_info *info;
+
+ if (!(dev = dev_cache_get(pv_name, fmt->cmd->filter)))
+ return_0;
+
+ if (!(label_read(dev, &label, UINT64_C(0))))
+ return_0;
+ info = (struct lvmcache_info *) label->info;
+
+ if (!_populate_pv_fields(info, pv, scan_label_only))
+ return 0;
+
+ if (!mdas)
+ return 1;
+
+ /* Add copy of mdas to supplied list */
+ dm_list_iterate_items(mda, &info->mdas) {
+ mda_new = mda_copy(fmt->cmd->mem, mda);
+ if (!mda_new)
+ return 0;
+ dm_list_add(mdas, &mda_new->list);
+ }
+
+ return 1;
+}
+
+static void _text_destroy_instance(struct format_instance *fid __attribute__((unused)))
+{
+}
+
+static void _free_dirs(struct dm_list *dir_list)
+{
+ struct dm_list *dl, *tmp;
+
+ dm_list_iterate_safe(dl, tmp, dir_list) {
+ dm_list_del(dl);
+ dm_free(dl);
+ }
+}
+
+static void _free_raws(struct dm_list *raw_list)
+{
+ struct dm_list *rl, *tmp;
+
+ dm_list_iterate_safe(rl, tmp, raw_list) {
+ dm_list_del(rl);
+ dm_free(rl);
+ }
+}
+
+static void _text_destroy(struct format_type *fmt)
+{
+ if (fmt->private) {
+ _free_dirs(&((struct mda_lists *) fmt->private)->dirs);
+ _free_raws(&((struct mda_lists *) fmt->private)->raws);
+ dm_free(fmt->private);
+ }
+
+ dm_free(fmt);
+}
+
+static struct metadata_area_ops _metadata_text_file_ops = {
+ .vg_read = _vg_read_file,
+ .vg_read_precommit = _vg_read_precommit_file,
+ .vg_write = _vg_write_file,
+ .vg_remove = _vg_remove_file,
+ .vg_commit = _vg_commit_file
+};
+
+static struct metadata_area_ops _metadata_text_file_backup_ops = {
+ .vg_read = _vg_read_file,
+ .vg_write = _vg_write_file,
+ .vg_remove = _vg_remove_file,
+ .vg_commit = _vg_commit_file_backup
+};
+
+static struct metadata_area_ops _metadata_text_raw_ops = {
+ .vg_read = _vg_read_raw,
+ .vg_read_precommit = _vg_read_precommit_raw,
+ .vg_write = _vg_write_raw,
+ .vg_remove = _vg_remove_raw,
+ .vg_precommit = _vg_precommit_raw,
+ .vg_commit = _vg_commit_raw,
+ .vg_revert = _vg_revert_raw,
+ .mda_metadata_locn_copy = _metadata_locn_copy_raw,
+ .mda_metadata_locn_name = _metadata_locn_name_raw,
+ .mda_metadata_locn_offset = _metadata_locn_offset_raw,
+ .mda_free_sectors = _mda_free_sectors_raw,
+ .mda_total_sectors = _mda_total_sectors_raw,
+ .mda_in_vg = _mda_in_vg_raw,
+ .pv_analyze_mda = _pv_analyze_mda_raw,
+ .mda_locns_match = _mda_locns_match_raw
+};
+
+/* pvmetadatasize in sectors */
+/*
+ * pe_start goal: FIXME -- reality of .pv_write complexity undermines this goal
+ * - In cases where a pre-existing pe_start is provided (pvcreate --restorefile
+ * and vgconvert): pe_start must not be changed (so pv->pe_start = pe_start).
+ * - In cases where pe_start is 0: leave pv->pe_start as 0 and defer the
+ * setting of pv->pe_start to .pv_write
+ */
+static int _text_pv_setup(const struct format_type *fmt,
+ uint64_t pe_start, uint32_t extent_count,
+ uint32_t extent_size, unsigned long data_alignment,
+ unsigned long data_alignment_offset,
+ int pvmetadatacopies, uint64_t pvmetadatasize,
+ unsigned metadataignore, struct dm_list *mdas,
+ struct physical_volume *pv, struct volume_group *vg)
+{
+ struct metadata_area *mda, *mda_new, *mda2;
+ struct mda_context *mdac, *mdac2;
+ struct dm_list *pvmdas;
+ struct lvmcache_info *info;
+ int found;
+ uint64_t pe_end = 0;
+ unsigned mda_count = 0;
+ uint64_t mda_size2 = 0;
+ uint64_t pe_count;
+
+ /* FIXME Cope with pvchange */
+ /* FIXME Merge code with _text_create_text_instance */
+
+ /* If new vg, add any further mdas on this PV to the fid's mda list */
+ if (vg) {
+ /* Iterate through all mdas on this PV */
+ if ((info = info_from_pvid(pv->dev->pvid, 0))) {
+ pvmdas = &info->mdas;
+ dm_list_iterate_items(mda, pvmdas) {
+ mda_count++;
+ mdac =
+ (struct mda_context *) mda->metadata_locn;
+
+ /* FIXME Check it isn't already in use */
+
+ /* Reduce usable device size */
+ if (mda_count > 1)
+ mda_size2 = mdac->area.size >> SECTOR_SHIFT;
+
+ /* Ensure it isn't already on list */
+ found = 0;
+ dm_list_iterate_items(mda2, mdas) {
+ if (mda2->ops !=
+ &_metadata_text_raw_ops) continue;
+ mdac2 =
+ (struct mda_context *)
+ mda2->metadata_locn;
+ if (!memcmp
+ (&mdac2->area, &mdac->area,
+ sizeof(mdac->area))) {
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ continue;
+
+ mda_new = mda_copy(fmt->cmd->mem, mda);
+ if (!mda_new)
+ return_0;
+ dm_list_add(mdas, &mda_new->list);
+ /* FIXME multiple dev_areas inside area */
+ }
+ }
+
+ /* FIXME Cope with genuine pe_count 0 */
+
+ /* If missing, estimate pv->size from file-based metadata */
+ if (!pv->size && pv->pe_count)
+ pv->size = pv->pe_count * (uint64_t) vg->extent_size +
+ pv->pe_start + mda_size2;
+
+ /* Recalculate number of extents that will fit */
+ if (!pv->pe_count) {
+ pe_count = (pv->size - pv->pe_start - mda_size2) /
+ vg->extent_size;
+ if (pe_count > UINT32_MAX) {
+ log_error("PV %s too large for extent size %s.",
+ pv_dev_name(pv),
+ display_size(vg->cmd, (uint64_t) vg->extent_size));
+ return 0;
+ }
+ pv->pe_count = (uint32_t) pe_count;
+ }
+
+ /* Unlike LVM1, we don't store this outside a VG */
+ /* FIXME Default from config file? vgextend cmdline flag? */
+ pv->status |= ALLOCATABLE_PV;
+ } else {
+ if (pe_start)
+ pv->pe_start = pe_start;
+
+ if (!data_alignment)
+ data_alignment = find_config_tree_int(pv->fmt->cmd,
+ "devices/data_alignment",
+ 0) * 2;
+
+ if (set_pe_align(pv, data_alignment) != data_alignment &&
+ data_alignment) {
+ log_error("%s: invalid data alignment of "
+ "%lu sectors (requested %lu sectors)",
+ pv_dev_name(pv), pv->pe_align, data_alignment);
+ return 0;
+ }
+
+ if (set_pe_align_offset(pv, data_alignment_offset) != data_alignment_offset &&
+ data_alignment_offset) {
+ log_error("%s: invalid data alignment offset of "
+ "%lu sectors (requested %lu sectors)",
+ pv_dev_name(pv), pv->pe_align_offset, data_alignment_offset);
+ return 0;
+ }
+
+ if (pv->pe_align < pv->pe_align_offset) {
+ log_error("%s: pe_align (%lu sectors) must not be less "
+ "than pe_align_offset (%lu sectors)",
+ pv_dev_name(pv), pv->pe_align, pv->pe_align_offset);
+ return 0;
+ }
+
+ /*
+ * This initialization has a side-effect of allowing
+ * orphaned PVs to be created with the proper alignment.
+ * Setting pv->pe_start here circumvents .pv_write's
+ * "pvcreate on PV without prior pvremove" retreival of
+ * the PV's previous pe_start.
+ * - Without this you get actual != expected pe_start
+ * failures in the testsuite.
+ */
+ if (!pe_start && pv->pe_start < pv->pe_align)
+ pv->pe_start = pv->pe_align;
+
+ if (extent_count)
+ pe_end = pe_start + extent_count * extent_size - 1;
+ if (!_mda_setup(fmt, pe_start, pe_end, pvmetadatacopies,
+ pvmetadatasize, metadataignore, mdas, pv, vg))
+ return_0;
+ }
+
+ return 1;
+}
+
+/* NULL vgname means use only the supplied context e.g. an archive file */
+static struct format_instance *_text_create_text_instance(const struct format_type
+ *fmt, const char *vgname,
+ const char *vgid,
+ void *context)
+{
+ struct format_instance *fid;
+ struct text_fid_context *fidtc;
+ struct metadata_area *mda;
+ struct mda_context *mdac;
+ struct dir_list *dl;
+ struct raw_list *rl;
+ struct dm_list *dir_list, *raw_list;
+ char path[PATH_MAX];
+ struct lvmcache_vginfo *vginfo;
+ struct lvmcache_info *info;
+
+ if (!(fid = dm_pool_alloc(fmt->cmd->mem, sizeof(*fid)))) {
+ log_error("Couldn't allocate format instance object.");
+ return NULL;
+ }
+
+ if (!(fidtc = (struct text_fid_context *)
+ dm_pool_zalloc(fmt->cmd->mem,sizeof(*fidtc)))) {
+ log_error("Couldn't allocate text_fid_context.");
+ return NULL;
+ }
+
+ fidtc->raw_metadata_buf = NULL;
+ fid->private = (void *) fidtc;
+
+ fid->fmt = fmt;
+ dm_list_init(&fid->metadata_areas_in_use);
+ dm_list_init(&fid->metadata_areas_ignored);
+
+ if (!vgname) {
+ if (!(mda = dm_pool_zalloc(fmt->cmd->mem, sizeof(*mda))))
+ return_NULL;
+ mda->ops = &_metadata_text_file_backup_ops;
+ mda->metadata_locn = context;
+ mda->status = 0;
+ fid_add_mda(fid, mda);
+ } else {
+ dir_list = &((struct mda_lists *) fmt->private)->dirs;
+
+ dm_list_iterate_items(dl, dir_list) {
+ if (dm_snprintf(path, PATH_MAX, "%s/%s",
+ dl->dir, vgname) < 0) {
+ log_error("Name too long %s/%s", dl->dir,
+ vgname);
+ return NULL;
+ }
+
+ context = create_text_context(fmt->cmd, path, NULL);
+ if (!(mda = dm_pool_zalloc(fmt->cmd->mem, sizeof(*mda))))
+ return_NULL;
+ mda->ops = &_metadata_text_file_ops;
+ mda->metadata_locn = context;
+ mda->status = 0;
+ fid_add_mda(fid, mda);
+ }
+
+ raw_list = &((struct mda_lists *) fmt->private)->raws;
+
+ dm_list_iterate_items(rl, raw_list) {
+ /* FIXME Cache this; rescan below if some missing */
+ if (!_raw_holds_vgname(fid, &rl->dev_area, vgname))
+ continue;
+
+ if (!(mda = dm_pool_zalloc(fmt->cmd->mem, sizeof(*mda))))
+ return_NULL;
+
+ if (!(mdac = dm_pool_zalloc(fmt->cmd->mem, sizeof(*mdac))))
+ return_NULL;
+ mda->metadata_locn = mdac;
+ /* FIXME Allow multiple dev_areas inside area */
+ memcpy(&mdac->area, &rl->dev_area, sizeof(mdac->area));
+ mda->ops = &_metadata_text_raw_ops;
+ mda->status = 0;
+ /* FIXME MISTAKE? mda->metadata_locn = context; */
+ fid_add_mda(fid, mda);
+ }
+
+ /* Scan PVs in VG for any further MDAs */
+ lvmcache_label_scan(fmt->cmd, 0);
+ if (!(vginfo = vginfo_from_vgname(vgname, vgid)))
+ goto_out;
+ dm_list_iterate_items(info, &vginfo->infos) {
+ if (!fid_add_mdas(fid, &info->mdas))
+ return_NULL;
+ }
+ /* FIXME Check raw metadata area count - rescan if required */
+ }
+
+ out:
+ return fid;
+}
+
+void *create_text_context(struct cmd_context *cmd, const char *path,
+ const char *desc)
+{
+ struct text_context *tc;
+ char *tmp;
+
+ if ((tmp = strstr(path, ".tmp")) && (tmp == path + strlen(path) - 4)) {
+ log_error("%s: Volume group filename may not end in .tmp",
+ path);
+ return NULL;
+ }
+
+ if (!(tc = dm_pool_alloc(cmd->mem, sizeof(*tc))))
+ return_NULL;
+
+ if (!(tc->path_live = dm_pool_strdup(cmd->mem, path)))
+ goto_bad;
+
+ if (!(tc->path_edit = dm_pool_alloc(cmd->mem, strlen(path) + 5)))
+ goto_bad;
+
+ sprintf(tc->path_edit, "%s.tmp", path);
+
+ if (!desc)
+ desc = "";
+
+ if (!(tc->desc = dm_pool_strdup(cmd->mem, desc)))
+ goto_bad;
+
+ return (void *) tc;
+
+ bad:
+ dm_pool_free(cmd->mem, tc);
+
+ log_error("Couldn't allocate text format context object.");
+ return NULL;
+}
+
+static struct format_handler _text_handler = {
+ .scan = _text_scan,
+ .pv_read = _text_pv_read,
+ .pv_setup = _text_pv_setup,
+ .pv_write = _text_pv_write,
+ .vg_setup = _text_vg_setup,
+ .lv_setup = _text_lv_setup,
+ .create_instance = _text_create_text_instance,
+ .destroy_instance = _text_destroy_instance,
+ .destroy = _text_destroy
+};
+
+static int _add_dir(const char *dir, struct dm_list *dir_list)
+{
+ struct dir_list *dl;
+
+ if (dm_create_dir(dir)) {
+ if (!(dl = dm_malloc(sizeof(struct dm_list) + strlen(dir) + 1))) {
+ log_error("_add_dir allocation failed");
+ return 0;
+ }
+ log_very_verbose("Adding text format metadata dir: %s", dir);
+ strcpy(dl->dir, dir);
+ dm_list_add(dir_list, &dl->list);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int _get_config_disk_area(struct cmd_context *cmd,
+ const struct config_node *cn, struct dm_list *raw_list)
+{
+ struct device_area dev_area;
+ const char *id_str;
+ struct id id;
+
+ if (!(cn = cn->child)) {
+ log_error("Empty metadata disk_area section of config file");
+ return 0;
+ }
+
+ if (!get_config_uint64(cn, "start_sector", &dev_area.start)) {
+ log_error("Missing start_sector in metadata disk_area section "
+ "of config file");
+ return 0;
+ }
+ dev_area.start <<= SECTOR_SHIFT;
+
+ if (!get_config_uint64(cn, "size", &dev_area.size)) {
+ log_error("Missing size in metadata disk_area section "
+ "of config file");
+ return 0;
+ }
+ dev_area.size <<= SECTOR_SHIFT;
+
+ if (!get_config_str(cn, "id", &id_str)) {
+ log_error("Missing uuid in metadata disk_area section "
+ "of config file");
+ return 0;
+ }
+
+ if (!id_read_format(&id, id_str)) {
+ log_error("Invalid uuid in metadata disk_area section "
+ "of config file: %s", id_str);
+ return 0;
+ }
+
+ if (!(dev_area.dev = device_from_pvid(cmd, &id, NULL))) {
+ char buffer[64] __attribute__((aligned(8)));
+
+ if (!id_write_format(&id, buffer, sizeof(buffer)))
+ log_error("Couldn't find device.");
+ else
+ log_error("Couldn't find device with uuid '%s'.",
+ buffer);
+
+ return 0;
+ }
+
+ return _add_raw(raw_list, &dev_area);
+}
+
+struct format_type *create_text_format(struct cmd_context *cmd)
+{
+ struct format_type *fmt;
+ const struct config_node *cn;
+ const struct config_value *cv;
+ struct mda_lists *mda_lists;
+
+ if (!(fmt = dm_malloc(sizeof(*fmt))))
+ return_NULL;
+
+ fmt->cmd = cmd;
+ fmt->ops = &_text_handler;
+ fmt->name = FMT_TEXT_NAME;
+ fmt->alias = FMT_TEXT_ALIAS;
+ fmt->orphan_vg_name = ORPHAN_VG_NAME(FMT_TEXT_NAME);
+ fmt->features = FMT_SEGMENTS | FMT_MDAS | FMT_TAGS | FMT_PRECOMMIT |
+ FMT_UNLIMITED_VOLS | FMT_RESIZE_PV |
+ FMT_UNLIMITED_STRIPESIZE;
+
+ if (!(mda_lists = dm_malloc(sizeof(struct mda_lists)))) {
+ log_error("Failed to allocate dir_list");
+ dm_free(fmt);
+ return NULL;
+ }
+
+ dm_list_init(&mda_lists->dirs);
+ dm_list_init(&mda_lists->raws);
+ mda_lists->file_ops = &_metadata_text_file_ops;
+ mda_lists->raw_ops = &_metadata_text_raw_ops;
+ fmt->private = (void *) mda_lists;
+
+ if (!(fmt->labeller = text_labeller_create(fmt))) {
+ log_error("Couldn't create text label handler.");
+ dm_free(fmt);
+ return NULL;
+ }
+
+ if (!(label_register_handler(FMT_TEXT_NAME, fmt->labeller))) {
+ log_error("Couldn't register text label handler.");
+ dm_free(fmt);
+ return NULL;
+ }
+
+ if ((cn = find_config_tree_node(cmd, "metadata/dirs"))) {
+ for (cv = cn->v; cv; cv = cv->next) {
+ if (cv->type != CFG_STRING) {
+ log_error("Invalid string in config file: "
+ "metadata/dirs");
+ goto err;
+ }
+
+ if (!_add_dir(cv->v.str, &mda_lists->dirs)) {
+ log_error("Failed to add %s to text format "
+ "metadata directory list ", cv->v.str);
+ goto err;
+ }
+ cmd->independent_metadata_areas = 1;
+ }
+ }
+
+ if ((cn = find_config_tree_node(cmd, "metadata/disk_areas"))) {
+ for (cn = cn->child; cn; cn = cn->sib) {
+ if (!_get_config_disk_area(cmd, cn, &mda_lists->raws))
+ goto err;
+ cmd->independent_metadata_areas = 1;
+ }
+ }
+
+ log_very_verbose("Initialised format: %s", fmt->name);
+
+ return fmt;
+
+ err:
+ _free_dirs(&mda_lists->dirs);
+
+ dm_free(fmt);
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_FORMAT_TEXT_H
+#define _LVM_FORMAT_TEXT_H
+
+#include "lvm-types.h"
+#include "metadata.h"
+
+#define FMT_TEXT_NAME "lvm2"
+#define FMT_TEXT_ALIAS "text"
+#define FMT_TEXT_ORPHAN_VG_NAME ORPHAN_VG_NAME(FMT_TEXT_NAME)
+
+/*
+ * Archives a vg config. 'retain_days' is the minimum number of
+ * days that an archive file must be held for. 'min_archives' is
+ * the minimum number of archives required to be kept for each
+ * volume group.
+ */
+int archive_vg(struct volume_group *vg,
+ const char *dir,
+ const char *desc, uint32_t retain_days, uint32_t min_archive);
+
+/*
+ * Displays a list of vg backups in a particular archive directory.
+ */
+int archive_list(struct cmd_context *cmd, const char *dir, const char *vgname);
+int archive_list_file(struct cmd_context *cmd, const char *file);
+int backup_list(struct cmd_context *cmd, const char *dir, const char *vgname);
+
+/*
+ * The text format can read and write a volume_group to a file.
+ */
+struct format_type *create_text_format(struct cmd_context *cmd);
+void *create_text_context(struct cmd_context *cmd, const char *path,
+ const char *desc);
+
+struct labeller *text_labeller_create(const struct format_type *fmt);
+
+int pvhdr_read(struct device *dev, char *buf);
+
+int add_da(struct dm_pool *mem, struct dm_list *das,
+ uint64_t start, uint64_t size);
+void del_das(struct dm_list *das);
+
+int add_mda(const struct format_type *fmt, struct dm_pool *mem, struct dm_list *mdas,
+ struct device *dev, uint64_t start, uint64_t size, unsigned ignored);
+void del_mdas(struct dm_list *mdas);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_TEXT_IMPORT_EXPORT_H
+#define _LVM_TEXT_IMPORT_EXPORT_H
+
+#include "config.h"
+#include "lvm-types.h"
+#include "metadata.h"
+
+#include <stdio.h>
+
+/*
+ * Constants to identify files this code can parse.
+ */
+#define CONTENTS_FIELD "contents"
+#define CONTENTS_VALUE "Text Format Volume Group"
+
+#define FORMAT_VERSION_FIELD "version"
+#define FORMAT_VERSION_VALUE 1
+
+/*
+ * VGs, PVs and LVs all have status bitsets, we gather together
+ * common code for reading and writing them.
+ */
+enum {
+ COMPATIBLE_FLAG = 0x0,
+ VG_FLAGS,
+ PV_FLAGS,
+ LV_FLAGS,
+ STATUS_FLAG = 0x8,
+};
+
+struct text_vg_version_ops {
+ int (*check_version) (const struct config_tree * cf);
+ struct volume_group *(*read_vg) (struct format_instance * fid,
+ const struct config_tree *cf,
+ unsigned use_cached_pvs);
+ void (*read_desc) (struct dm_pool * mem, const struct config_tree *cf,
+ time_t *when, char **desc);
+ const char *(*read_vgname) (const struct format_type *fmt,
+ const struct config_tree *cft,
+ struct id *vgid, uint64_t *vgstatus,
+ char **creation_host);
+};
+
+struct text_vg_version_ops *text_vg_vsn1_init(void);
+
+int print_flags(uint64_t status, int type, char *buffer, size_t size);
+int read_flags(uint64_t *status, int type, const struct config_value *cv);
+
+char *alloc_printed_tags(struct dm_list *tags);
+int read_tags(struct dm_pool *mem, struct dm_list *tags, const struct config_value *cv);
+
+int text_vg_export_file(struct volume_group *vg, const char *desc, FILE *fp);
+int text_vg_export_raw(struct volume_group *vg, const char *desc, char **buf);
+struct volume_group *text_vg_import_file(struct format_instance *fid,
+ const char *file,
+ time_t *when, char **desc);
+struct volume_group *text_vg_import_fd(struct format_instance *fid,
+ const char *file,
+ struct device *dev,
+ off_t offset, uint32_t size,
+ off_t offset2, uint32_t size2,
+ checksum_fn_t checksum_fn,
+ uint32_t checksum,
+ time_t *when, char **desc);
+const char *text_vgname_import(const struct format_type *fmt,
+ struct device *dev,
+ off_t offset, uint32_t size,
+ off_t offset2, uint32_t size2,
+ checksum_fn_t checksum_fn, uint32_t checksum,
+ struct id *vgid, uint64_t *vgstatus,
+ char **creation_host);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "import-export.h"
+#include "display.h"
+#include "toolcontext.h"
+#include "lvmcache.h"
+
+/* FIXME Use tidier inclusion method */
+static struct text_vg_version_ops *(_text_vsn_list[2]);
+
+static int _text_import_initialised = 0;
+
+static void _init_text_import(void)
+{
+ if (_text_import_initialised)
+ return;
+
+ _text_vsn_list[0] = text_vg_vsn1_init();
+ _text_vsn_list[1] = NULL;
+ _text_import_initialised = 1;
+}
+
+const char *text_vgname_import(const struct format_type *fmt,
+ struct device *dev,
+ off_t offset, uint32_t size,
+ off_t offset2, uint32_t size2,
+ checksum_fn_t checksum_fn, uint32_t checksum,
+ struct id *vgid, uint64_t *vgstatus,
+ char **creation_host)
+{
+ struct config_tree *cft;
+ struct text_vg_version_ops **vsn;
+ const char *vgname = NULL;
+
+ _init_text_import();
+
+ if (!(cft = create_config_tree(NULL, 0)))
+ return_NULL;
+
+ if ((!dev && !read_config_file(cft)) ||
+ (dev && !read_config_fd(cft, dev, offset, size,
+ offset2, size2, checksum_fn, checksum)))
+ goto_out;
+
+ /*
+ * Find a set of version functions that can read this file
+ */
+ for (vsn = &_text_vsn_list[0]; *vsn; vsn++) {
+ if (!(*vsn)->check_version(cft))
+ continue;
+
+ if (!(vgname = (*vsn)->read_vgname(fmt, cft, vgid, vgstatus,
+ creation_host)))
+ goto_out;
+
+ break;
+ }
+
+ out:
+ destroy_config_tree(cft);
+ return vgname;
+}
+
+struct volume_group *text_vg_import_fd(struct format_instance *fid,
+ const char *file,
+ struct device *dev,
+ off_t offset, uint32_t size,
+ off_t offset2, uint32_t size2,
+ checksum_fn_t checksum_fn,
+ uint32_t checksum,
+ time_t *when, char **desc)
+{
+ struct volume_group *vg = NULL;
+ struct config_tree *cft;
+ struct text_vg_version_ops **vsn;
+
+ _init_text_import();
+
+ *desc = NULL;
+ *when = 0;
+
+ if (!(cft = create_config_tree(file, 0)))
+ return_NULL;
+
+ if ((!dev && !read_config_file(cft)) ||
+ (dev && !read_config_fd(cft, dev, offset, size,
+ offset2, size2, checksum_fn, checksum))) {
+ log_error("Couldn't read volume group metadata.");
+ goto out;
+ }
+
+ /*
+ * Find a set of version functions that can read this file
+ */
+ for (vsn = &_text_vsn_list[0]; *vsn; vsn++) {
+ if (!(*vsn)->check_version(cft))
+ continue;
+
+ if (!(vg = (*vsn)->read_vg(fid, cft, 0)))
+ goto_out;
+
+ (*vsn)->read_desc(vg->vgmem, cft, when, desc);
+ break;
+ }
+
+ out:
+ destroy_config_tree(cft);
+ return vg;
+}
+
+struct volume_group *text_vg_import_file(struct format_instance *fid,
+ const char *file,
+ time_t *when, char **desc)
+{
+ return text_vg_import_fd(fid, file, NULL, (off_t)0, 0, (off_t)0, 0, NULL, 0,
+ when, desc);
+}
+
+struct volume_group *import_vg_from_buffer(const char *buf,
+ struct format_instance *fid)
+{
+ struct volume_group *vg = NULL;
+ struct config_tree *cft;
+ struct text_vg_version_ops **vsn;
+
+ _init_text_import();
+
+ if (!(cft = create_config_tree_from_string(fid->fmt->cmd, buf)))
+ return_NULL;
+
+ for (vsn = &_text_vsn_list[0]; *vsn; vsn++) {
+ if (!(*vsn)->check_version(cft))
+ continue;
+ /*
+ * The only path to this point uses cached vgmetadata,
+ * so it can use cached PV state too.
+ */
+ if (!(vg = (*vsn)->read_vg(fid, cft, 1)))
+ stack;
+ break;
+ }
+
+ destroy_config_tree(cft);
+ return vg;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "import-export.h"
+#include "display.h"
+#include "toolcontext.h"
+#include "lvmcache.h"
+#include "lv_alloc.h"
+#include "pv_alloc.h"
+#include "segtype.h"
+#include "text_import.h"
+#include "defaults.h"
+
+typedef int (*section_fn) (struct format_instance * fid, struct dm_pool * mem,
+ struct volume_group * vg, const struct config_node * pvn,
+ const struct config_node * vgn,
+ struct dm_hash_table * pv_hash,
+ struct dm_hash_table * lv_hash,
+ unsigned *scan_done_once,
+ unsigned report_missing_devices);
+
+#define _read_int32(root, path, result) \
+ get_config_uint32(root, path, (uint32_t *) result)
+
+#define _read_uint32(root, path, result) \
+ get_config_uint32(root, path, result)
+
+#define _read_int64(root, path, result) \
+ get_config_uint64(root, path, result)
+
+/*
+ * Logs an attempt to read an invalid format file.
+ */
+static void _invalid_format(const char *str)
+{
+ log_error("Can't process text format file - %s.", str);
+}
+
+/*
+ * Checks that the config file contains vg metadata, and that it
+ * we recognise the version number,
+ */
+static int _check_version(const struct config_tree *cft)
+{
+ const struct config_node *cn;
+ const struct config_value *cv;
+
+ /*
+ * Check the contents field.
+ */
+ if (!(cn = find_config_node(cft->root, CONTENTS_FIELD))) {
+ _invalid_format("missing contents field");
+ return 0;
+ }
+
+ cv = cn->v;
+ if (!cv || cv->type != CFG_STRING || strcmp(cv->v.str, CONTENTS_VALUE)) {
+ _invalid_format("unrecognised contents field");
+ return 0;
+ }
+
+ /*
+ * Check the version number.
+ */
+ if (!(cn = find_config_node(cft->root, FORMAT_VERSION_FIELD))) {
+ _invalid_format("missing version number");
+ return 0;
+ }
+
+ cv = cn->v;
+ if (!cv || cv->type != CFG_INT || cv->v.i != FORMAT_VERSION_VALUE) {
+ _invalid_format("unrecognised version number");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _is_converting(struct logical_volume *lv)
+{
+ struct lv_segment *seg;
+
+ if (lv->status & MIRRORED) {
+ seg = first_seg(lv);
+ /* Can't use is_temporary_mirror() because the metadata for
+ * seg_lv may not be read in and flags may not be set yet. */
+ if (seg_type(seg, 0) == AREA_LV &&
+ strstr(seg_lv(seg, 0)->name, MIRROR_SYNC_LAYER))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int _read_id(struct id *id, const struct config_node *cn, const char *path)
+{
+ const struct config_value *cv;
+
+ if (!(cn = find_config_node(cn, path))) {
+ log_error("Couldn't find uuid.");
+ return 0;
+ }
+
+ cv = cn->v;
+ if (!cv || !cv->v.str) {
+ log_error("uuid must be a string.");
+ return 0;
+ }
+
+ if (!id_read_format(id, cv->v.str)) {
+ log_error("Invalid uuid.");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _read_flag_config(const struct config_node *n, uint64_t *status, int type)
+{
+ const struct config_node *cn;
+ *status = 0;
+
+ if (!(cn = find_config_node(n, "status"))) {
+ log_error("Could not find status flags.");
+ return 0;
+ }
+
+ if (!(read_flags(status, type | STATUS_FLAG, cn->v))) {
+ log_error("Could not read status flags.");
+ return 0;
+ }
+
+ if ((cn = find_config_node(n, "flags"))) {
+ if (!(read_flags(status, type, cn->v))) {
+ log_error("Could not read flags.");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int _read_pv(struct format_instance *fid, struct dm_pool *mem,
+ struct volume_group *vg, const struct config_node *pvn,
+ const struct config_node *vgn __attribute__((unused)),
+ struct dm_hash_table *pv_hash,
+ struct dm_hash_table *lv_hash __attribute__((unused)),
+ unsigned *scan_done_once,
+ unsigned report_missing_devices)
+{
+ struct physical_volume *pv;
+ struct pv_list *pvl;
+ const struct config_node *cn;
+ uint64_t size;
+
+ if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl))) ||
+ !(pvl->pv = dm_pool_zalloc(mem, sizeof(*pvl->pv))))
+ return_0;
+
+ pv = pvl->pv;
+
+ /*
+ * Add the pv to the pv hash for quick lookup when we read
+ * the lv segments.
+ */
+ if (!dm_hash_insert(pv_hash, pvn->key, pv))
+ return_0;
+
+ if (!(pvn = pvn->child)) {
+ log_error("Empty pv section.");
+ return 0;
+ }
+
+ if (!_read_id(&pv->id, pvn, "id")) {
+ log_error("Couldn't read uuid for physical volume.");
+ return 0;
+ }
+
+ /*
+ * Convert the uuid into a device.
+ */
+ if (!(pv->dev = device_from_pvid(fid->fmt->cmd, &pv->id, scan_done_once))) {
+ char buffer[64] __attribute__((aligned(8)));
+
+ if (!id_write_format(&pv->id, buffer, sizeof(buffer)))
+ buffer[0] = '\0';
+ if (report_missing_devices)
+ log_error_once("Couldn't find device with uuid %s.", buffer);
+ else
+ log_very_verbose("Couldn't find device with uuid %s.", buffer);
+ }
+
+ if (!(pv->vg_name = dm_pool_strdup(mem, vg->name)))
+ return_0;
+
+ memcpy(&pv->vgid, &vg->id, sizeof(vg->id));
+
+ if (!_read_flag_config(pvn, &pv->status, PV_FLAGS)) {
+ log_error("Couldn't read status flags for physical volume.");
+ return 0;
+ }
+
+ if (!pv->dev)
+ pv->status |= MISSING_PV;
+
+ /* Late addition */
+ _read_int64(pvn, "dev_size", &pv->size);
+
+ if (!_read_int64(pvn, "pe_start", &pv->pe_start)) {
+ log_error("Couldn't read extent size for physical volume.");
+ return 0;
+ }
+
+ if (!_read_int32(pvn, "pe_count", &pv->pe_count)) {
+ log_error("Couldn't find extent count (pe_count) for "
+ "physical volume.");
+ return 0;
+ }
+
+ dm_list_init(&pv->tags);
+ dm_list_init(&pv->segments);
+
+ /* Optional tags */
+ if ((cn = find_config_node(pvn, "tags")) &&
+ !(read_tags(mem, &pv->tags, cn->v))) {
+ log_error("Couldn't read tags for physical volume %s in %s.",
+ pv_dev_name(pv), vg->name);
+ return 0;
+ }
+
+ pv->pe_size = vg->extent_size;
+
+ pv->pe_alloc_count = 0;
+ pv->pe_align = 0;
+ pv->fmt = fid->fmt;
+
+ /* Fix up pv size if missing or impossibly large */
+ if ((!pv->size || pv->size > (1ULL << 62)) && pv->dev) {
+ if (!dev_get_size(pv->dev, &pv->size)) {
+ log_error("%s: Couldn't get size.", pv_dev_name(pv));
+ return 0;
+ }
+ log_verbose("Fixing up missing size (%s) "
+ "for PV %s", display_size(fid->fmt->cmd, pv->size),
+ pv_dev_name(pv));
+ if (vg) {
+ size = pv->pe_count * (uint64_t) vg->extent_size +
+ pv->pe_start;
+ if (size > pv->size)
+ log_warn("WARNING: Physical Volume %s is too "
+ "large for underlying device",
+ pv_dev_name(pv));
+ }
+ }
+
+ if (!alloc_pv_segment_whole_pv(mem, pv))
+ return_0;
+
+ vg->extent_count += pv->pe_count;
+ vg->free_count += pv->pe_count;
+ add_pvl_to_vgs(vg, pvl);
+
+ return 1;
+}
+
+static void _insert_segment(struct logical_volume *lv, struct lv_segment *seg)
+{
+ struct lv_segment *comp;
+
+ dm_list_iterate_items(comp, &lv->segments) {
+ if (comp->le > seg->le) {
+ dm_list_add(&comp->list, &seg->list);
+ return;
+ }
+ }
+
+ lv->le_count += seg->len;
+ dm_list_add(&lv->segments, &seg->list);
+}
+
+static int _read_segment(struct dm_pool *mem, struct volume_group *vg,
+ struct logical_volume *lv, const struct config_node *sn,
+ struct dm_hash_table *pv_hash)
+{
+ uint32_t area_count = 0u;
+ struct lv_segment *seg;
+ const struct config_node *cn, *sn_child = sn->child;
+ const struct config_value *cv;
+ uint32_t start_extent, extent_count;
+ struct segment_type *segtype;
+ const char *segtype_str;
+
+ if (!sn_child) {
+ log_error("Empty segment section.");
+ return 0;
+ }
+
+ if (!_read_int32(sn_child, "start_extent", &start_extent)) {
+ log_error("Couldn't read 'start_extent' for segment '%s' "
+ "of logical volume %s.", sn->key, lv->name);
+ return 0;
+ }
+
+ if (!_read_int32(sn_child, "extent_count", &extent_count)) {
+ log_error("Couldn't read 'extent_count' for segment '%s' "
+ "of logical volume %s.", sn->key, lv->name);
+ return 0;
+ }
+
+ segtype_str = "striped";
+
+ if ((cn = find_config_node(sn_child, "type"))) {
+ cv = cn->v;
+ if (!cv || !cv->v.str) {
+ log_error("Segment type must be a string.");
+ return 0;
+ }
+ segtype_str = cv->v.str;
+ }
+
+ if (!(segtype = get_segtype_from_string(vg->cmd, segtype_str)))
+ return_0;
+
+ if (segtype->ops->text_import_area_count &&
+ !segtype->ops->text_import_area_count(sn_child, &area_count))
+ return_0;
+
+ if (!(seg = alloc_lv_segment(mem, segtype, lv, start_extent,
+ extent_count, 0, 0, NULL, area_count,
+ extent_count, 0, 0, 0, NULL))) {
+ log_error("Segment allocation failed");
+ return 0;
+ }
+
+ if (seg->segtype->ops->text_import &&
+ !seg->segtype->ops->text_import(seg, sn_child, pv_hash))
+ return_0;
+
+ /* Optional tags */
+ if ((cn = find_config_node(sn_child, "tags")) &&
+ !(read_tags(mem, &seg->tags, cn->v))) {
+ log_error("Couldn't read tags for a segment of %s/%s.",
+ vg->name, lv->name);
+ return 0;
+ }
+
+ /*
+ * Insert into correct part of segment list.
+ */
+ _insert_segment(lv, seg);
+
+ if (seg_is_mirrored(seg))
+ lv->status |= MIRRORED;
+
+ if (seg_is_virtual(seg))
+ lv->status |= VIRTUAL;
+
+ if (_is_converting(lv))
+ lv->status |= CONVERTING;
+
+ return 1;
+}
+
+int text_import_areas(struct lv_segment *seg, const struct config_node *sn,
+ const struct config_node *cn, struct dm_hash_table *pv_hash,
+ uint64_t status)
+{
+ unsigned int s;
+ const struct config_value *cv;
+ struct logical_volume *lv1;
+ struct physical_volume *pv;
+ const char *seg_name = config_parent_name(sn);
+
+ if (!seg->area_count) {
+ log_error("Zero areas not allowed for segment %s", seg_name);
+ return 0;
+ }
+
+ for (cv = cn->v, s = 0; cv && s < seg->area_count; s++, cv = cv->next) {
+
+ /* first we read the pv */
+ if (cv->type != CFG_STRING) {
+ log_error("Bad volume name in areas array for segment %s.", seg_name);
+ return 0;
+ }
+
+ if (!cv->next) {
+ log_error("Missing offset in areas array for segment %s.", seg_name);
+ return 0;
+ }
+
+ if (cv->next->type != CFG_INT) {
+ log_error("Bad offset in areas array for segment %s.", seg_name);
+ return 0;
+ }
+
+ /* FIXME Cope if LV not yet read in */
+ if ((pv = dm_hash_lookup(pv_hash, cv->v.str))) {
+ if (!set_lv_segment_area_pv(seg, s, pv, (uint32_t) cv->next->v.i))
+ return_0;
+ } else if ((lv1 = find_lv(seg->lv->vg, cv->v.str))) {
+ if (!set_lv_segment_area_lv(seg, s, lv1,
+ (uint32_t) cv->next->v.i,
+ status))
+ return_0;
+ } else {
+ log_error("Couldn't find volume '%s' "
+ "for segment '%s'.",
+ cv->v.str ? : "NULL", seg_name);
+ return 0;
+ }
+
+ cv = cv->next;
+ }
+
+ /*
+ * Check we read the correct number of stripes.
+ */
+ if (cv || (s < seg->area_count)) {
+ log_error("Incorrect number of areas in area array "
+ "for segment '%s'.", seg_name);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _read_segments(struct dm_pool *mem, struct volume_group *vg,
+ struct logical_volume *lv, const struct config_node *lvn,
+ struct dm_hash_table *pv_hash)
+{
+ const struct config_node *sn;
+ int count = 0, seg_count;
+
+ for (sn = lvn; sn; sn = sn->sib) {
+
+ /*
+ * All sub-sections are assumed to be segments.
+ */
+ if (!sn->v) {
+ if (!_read_segment(mem, vg, lv, sn, pv_hash))
+ return_0;
+
+ count++;
+ }
+ /* FIXME Remove this restriction */
+ if ((lv->status & SNAPSHOT) && count > 1) {
+ log_error("Only one segment permitted for snapshot");
+ return 0;
+ }
+ }
+
+ if (!_read_int32(lvn, "segment_count", &seg_count)) {
+ log_error("Couldn't read segment count for logical volume %s.",
+ lv->name);
+ return 0;
+ }
+
+ if (seg_count != count) {
+ log_error("segment_count and actual number of segments "
+ "disagree for logical volume %s.", lv->name);
+ return 0;
+ }
+
+ /*
+ * Check there are no gaps or overlaps in the lv.
+ */
+ if (!check_lv_segments(lv, 0))
+ return_0;
+
+ /*
+ * Merge segments in case someones been editing things by hand.
+ */
+ if (!lv_merge_segments(lv))
+ return_0;
+
+ return 1;
+}
+
+static int _read_lvnames(struct format_instance *fid __attribute__((unused)),
+ struct dm_pool *mem,
+ struct volume_group *vg, const struct config_node *lvn,
+ const struct config_node *vgn __attribute__((unused)),
+ struct dm_hash_table *pv_hash __attribute__((unused)),
+ struct dm_hash_table *lv_hash,
+ unsigned *scan_done_once __attribute__((unused)),
+ unsigned report_missing_devices __attribute__((unused)))
+{
+ struct logical_volume *lv;
+ const struct config_node *cn;
+
+ if (!(lv = alloc_lv(mem)))
+ return_0;
+
+ if (!(lv->name = dm_pool_strdup(mem, lvn->key)))
+ return_0;
+
+ if (!(lvn = lvn->child)) {
+ log_error("Empty logical volume section.");
+ return 0;
+ }
+
+ if (!_read_flag_config(lvn, &lv->status, LV_FLAGS)) {
+ log_error("Couldn't read status flags for logical volume %s.",
+ lv->name);
+ return 0;
+ }
+
+ lv->alloc = ALLOC_INHERIT;
+ if ((cn = find_config_node(lvn, "allocation_policy"))) {
+ const struct config_value *cv = cn->v;
+ if (!cv || !cv->v.str) {
+ log_error("allocation_policy must be a string.");
+ return 0;
+ }
+
+ lv->alloc = get_alloc_from_string(cv->v.str);
+ if (lv->alloc == ALLOC_INVALID) {
+ log_warn("WARNING: Ignoring unrecognised allocation policy %s for LV %s", cv->v.str, lv->name);
+ lv->alloc = ALLOC_INHERIT;
+ }
+ }
+
+ if (!_read_int32(lvn, "read_ahead", &lv->read_ahead))
+ /* If not present, choice of auto or none is configurable */
+ lv->read_ahead = vg->cmd->default_settings.read_ahead;
+ else {
+ switch (lv->read_ahead) {
+ case 0:
+ lv->read_ahead = DM_READ_AHEAD_AUTO;
+ break;
+ case (uint32_t) -1:
+ lv->read_ahead = DM_READ_AHEAD_NONE;
+ break;
+ default:
+ ;
+ }
+ }
+
+ /* Optional tags */
+ if ((cn = find_config_node(lvn, "tags")) &&
+ !(read_tags(mem, &lv->tags, cn->v))) {
+ log_error("Couldn't read tags for logical volume %s/%s.",
+ vg->name, lv->name);
+ return 0;
+ }
+
+ if (!dm_hash_insert(lv_hash, lv->name, lv))
+ return_0;
+
+ return link_lv_to_vg(vg, lv);
+}
+
+static int _read_lvsegs(struct format_instance *fid __attribute__((unused)),
+ struct dm_pool *mem,
+ struct volume_group *vg, const struct config_node *lvn,
+ const struct config_node *vgn __attribute__((unused)),
+ struct dm_hash_table *pv_hash,
+ struct dm_hash_table *lv_hash,
+ unsigned *scan_done_once __attribute__((unused)),
+ unsigned report_missing_devices __attribute__((unused)))
+{
+ struct logical_volume *lv;
+
+ if (!(lv = dm_hash_lookup(lv_hash, lvn->key))) {
+ log_error("Lost logical volume reference %s", lvn->key);
+ return 0;
+ }
+
+ if (!(lvn = lvn->child)) {
+ log_error("Empty logical volume section.");
+ return 0;
+ }
+
+ /* FIXME: read full lvid */
+ if (!_read_id(&lv->lvid.id[1], lvn, "id")) {
+ log_error("Couldn't read uuid for logical volume %s.",
+ lv->name);
+ return 0;
+ }
+
+ memcpy(&lv->lvid.id[0], &lv->vg->id, sizeof(lv->lvid.id[0]));
+
+ if (!_read_segments(mem, vg, lv, lvn, pv_hash))
+ return_0;
+
+ lv->size = (uint64_t) lv->le_count * (uint64_t) vg->extent_size;
+
+ lv->minor = -1;
+ if ((lv->status & FIXED_MINOR) &&
+ !_read_int32(lvn, "minor", &lv->minor)) {
+ log_error("Couldn't read minor number for logical "
+ "volume %s.", lv->name);
+ return 0;
+ }
+
+ lv->major = -1;
+ if ((lv->status & FIXED_MINOR) &&
+ !_read_int32(lvn, "major", &lv->major)) {
+ log_error("Couldn't read major number for logical "
+ "volume %s.", lv->name);
+ }
+
+ return 1;
+}
+
+static int _read_sections(struct format_instance *fid,
+ const char *section, section_fn fn,
+ struct dm_pool *mem,
+ struct volume_group *vg, const struct config_node *vgn,
+ struct dm_hash_table *pv_hash,
+ struct dm_hash_table *lv_hash,
+ int optional,
+ unsigned *scan_done_once)
+{
+ const struct config_node *n;
+ /* Only report missing devices when doing a scan */
+ unsigned report_missing_devices = scan_done_once ? !*scan_done_once : 1;
+
+ if (!(n = find_config_node(vgn, section))) {
+ if (!optional) {
+ log_error("Couldn't find section '%s'.", section);
+ return 0;
+ }
+
+ return 1;
+ }
+
+ for (n = n->child; n; n = n->sib) {
+ if (!fn(fid, mem, vg, n, vgn, pv_hash, lv_hash,
+ scan_done_once, report_missing_devices))
+ return_0;
+ }
+
+ return 1;
+}
+
+static struct volume_group *_read_vg(struct format_instance *fid,
+ const struct config_tree *cft,
+ unsigned use_cached_pvs)
+{
+ const struct config_node *vgn, *cn;
+ struct volume_group *vg;
+ struct dm_hash_table *pv_hash = NULL, *lv_hash = NULL;
+ struct dm_pool *mem = dm_pool_create("lvm2 vg_read", VG_MEMPOOL_CHUNK);
+ unsigned scan_done_once = use_cached_pvs;
+
+ if (!mem)
+ return_NULL;
+
+ /* skip any top-level values */
+ for (vgn = cft->root; (vgn && vgn->v); vgn = vgn->sib)
+ ;
+
+ if (!vgn) {
+ log_error("Couldn't find volume group in file.");
+ goto bad;
+ }
+
+ if (!(vg = dm_pool_zalloc(mem, sizeof(*vg))))
+ goto_bad;
+
+ vg->vgmem = mem;
+ vg->cmd = fid->fmt->cmd;
+
+ /* FIXME Determine format type from file contents */
+ /* eg Set to instance of fmt1 here if reading a format1 backup? */
+ vg->fid = fid;
+
+ if (!(vg->name = dm_pool_strdup(mem, vgn->key)))
+ goto_bad;
+
+ if (!(vg->system_id = dm_pool_zalloc(mem, NAME_LEN)))
+ goto_bad;
+
+ vgn = vgn->child;
+
+ if ((cn = find_config_node(vgn, "system_id")) && cn->v) {
+ if (!cn->v->v.str) {
+ log_error("system_id must be a string");
+ goto bad;
+ }
+ strncpy(vg->system_id, cn->v->v.str, NAME_LEN);
+ }
+
+ if (!_read_id(&vg->id, vgn, "id")) {
+ log_error("Couldn't read uuid for volume group %s.", vg->name);
+ goto bad;
+ }
+
+ if (!_read_int32(vgn, "seqno", &vg->seqno)) {
+ log_error("Couldn't read 'seqno' for volume group %s.",
+ vg->name);
+ goto bad;
+ }
+
+ if (!_read_flag_config(vgn, &vg->status, VG_FLAGS)) {
+ log_error("Error reading flags of volume group %s.",
+ vg->name);
+ goto bad;
+ }
+
+ if (!_read_int32(vgn, "extent_size", &vg->extent_size)) {
+ log_error("Couldn't read extent size for volume group %s.",
+ vg->name);
+ goto bad;
+ }
+
+ /*
+ * 'extent_count' and 'free_count' get filled in
+ * implicitly when reading in the pv's and lv's.
+ */
+
+ if (!_read_int32(vgn, "max_lv", &vg->max_lv)) {
+ log_error("Couldn't read 'max_lv' for volume group %s.",
+ vg->name);
+ goto bad;
+ }
+
+ if (!_read_int32(vgn, "max_pv", &vg->max_pv)) {
+ log_error("Couldn't read 'max_pv' for volume group %s.",
+ vg->name);
+ goto bad;
+ }
+
+ vg->alloc = ALLOC_NORMAL;
+ if ((cn = find_config_node(vgn, "allocation_policy"))) {
+ const struct config_value *cv = cn->v;
+ if (!cv || !cv->v.str) {
+ log_error("allocation_policy must be a string.");
+ goto bad;
+ }
+
+ vg->alloc = get_alloc_from_string(cv->v.str);
+ if (vg->alloc == ALLOC_INVALID) {
+ log_warn("WARNING: Ignoring unrecognised allocation policy %s for VG %s", cv->v.str, vg->name);
+ vg->alloc = ALLOC_NORMAL;
+ }
+ }
+
+ if (!_read_uint32(vgn, "metadata_copies", &vg->mda_copies)) {
+ vg->mda_copies = DEFAULT_VGMETADATACOPIES;
+ }
+
+ /*
+ * The pv hash memorises the pv section names -> pv
+ * structures.
+ */
+ if (!(pv_hash = dm_hash_create(32))) {
+ log_error("Couldn't create hash table.");
+ goto bad;
+ }
+
+ dm_list_init(&vg->pvs);
+ if (!_read_sections(fid, "physical_volumes", _read_pv, mem, vg,
+ vgn, pv_hash, lv_hash, 0, &scan_done_once)) {
+ log_error("Couldn't find all physical volumes for volume "
+ "group %s.", vg->name);
+ goto bad;
+ }
+
+ dm_list_init(&vg->lvs);
+ dm_list_init(&vg->tags);
+ dm_list_init(&vg->removed_pvs);
+
+ /* Optional tags */
+ if ((cn = find_config_node(vgn, "tags")) &&
+ !(read_tags(mem, &vg->tags, cn->v))) {
+ log_error("Couldn't read tags for volume group %s.", vg->name);
+ goto bad;
+ }
+
+ /*
+ * The lv hash memorises the lv section names -> lv
+ * structures.
+ */
+ if (!(lv_hash = dm_hash_create(32))) {
+ log_error("Couldn't create hash table.");
+ goto bad;
+ }
+
+ if (!_read_sections(fid, "logical_volumes", _read_lvnames, mem, vg,
+ vgn, pv_hash, lv_hash, 1, NULL)) {
+ log_error("Couldn't read all logical volume names for volume "
+ "group %s.", vg->name);
+ goto bad;
+ }
+
+ if (!_read_sections(fid, "logical_volumes", _read_lvsegs, mem, vg,
+ vgn, pv_hash, lv_hash, 1, NULL)) {
+ log_error("Couldn't read all logical volumes for "
+ "volume group %s.", vg->name);
+ goto bad;
+ }
+
+ if (!fixup_imported_mirrors(vg)) {
+ log_error("Failed to fixup mirror pointers after import for "
+ "volume group %s.", vg->name);
+ goto bad;
+ }
+
+ dm_hash_destroy(pv_hash);
+ dm_hash_destroy(lv_hash);
+
+ /*
+ * Finished.
+ */
+ return vg;
+
+ bad:
+ if (pv_hash)
+ dm_hash_destroy(pv_hash);
+
+ if (lv_hash)
+ dm_hash_destroy(lv_hash);
+
+ dm_pool_destroy(mem);
+ return NULL;
+}
+
+static void _read_desc(struct dm_pool *mem,
+ const struct config_tree *cft, time_t *when, char **desc)
+{
+ const char *d;
+ unsigned int u = 0u;
+ int old_suppress;
+
+ old_suppress = log_suppress(1);
+ d = find_config_str(cft->root, "description", "");
+ log_suppress(old_suppress);
+ *desc = dm_pool_strdup(mem, d);
+
+ get_config_uint32(cft->root, "creation_time", &u);
+ *when = u;
+}
+
+static const char *_read_vgname(const struct format_type *fmt,
+ const struct config_tree *cft, struct id *vgid,
+ uint64_t *vgstatus, char **creation_host)
+{
+ const struct config_node *vgn;
+ struct dm_pool *mem = fmt->cmd->mem;
+ char *vgname;
+ int old_suppress;
+
+ old_suppress = log_suppress(2);
+ *creation_host = dm_pool_strdup(mem,
+ find_config_str(cft->root,
+ "creation_host", ""));
+ log_suppress(old_suppress);
+
+ /* skip any top-level values */
+ for (vgn = cft->root; (vgn && vgn->v); vgn = vgn->sib) ;
+
+ if (!vgn) {
+ log_error("Couldn't find volume group in file.");
+ return 0;
+ }
+
+ if (!(vgname = dm_pool_strdup(mem, vgn->key)))
+ return_0;
+
+ vgn = vgn->child;
+
+ if (!_read_id(vgid, vgn, "id")) {
+ log_error("Couldn't read uuid for volume group %s.", vgname);
+ return 0;
+ }
+
+ if (!_read_flag_config(vgn, vgstatus, VG_FLAGS)) {
+ log_error("Couldn't find status flags for volume group %s.",
+ vgname);
+ return 0;
+ }
+
+ return vgname;
+}
+
+static struct text_vg_version_ops _vsn1_ops = {
+ .check_version = _check_version,
+ .read_vg = _read_vg,
+ .read_desc = _read_desc,
+ .read_vgname = _read_vgname,
+};
+
+struct text_vg_version_ops *text_vg_vsn1_init(void)
+{
+ return &_vsn1_ops;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_TEXT_LAYOUT_H
+#define _LVM_TEXT_LAYOUT_H
+
+#include "config.h"
+#include "lvm-types.h"
+#include "metadata.h"
+#include "uuid.h"
+
+/* On disk */
+struct disk_locn {
+ uint64_t offset; /* Offset in bytes to start sector */
+ uint64_t size; /* Bytes */
+} __attribute__ ((packed));
+
+/* Data areas (holding PEs) */
+struct data_area_list {
+ struct dm_list list;
+ struct disk_locn disk_locn;
+};
+
+/* Fields with the suffix _xl should be xlate'd wherever they appear */
+/* On disk */
+struct pv_header {
+ int8_t pv_uuid[ID_LEN];
+
+ /* This size can be overridden if PV belongs to a VG */
+ uint64_t device_size_xl; /* Bytes */
+
+ /* NULL-terminated list of data areas followed by */
+ /* NULL-terminated list of metadata area headers */
+ struct disk_locn disk_areas_xl[0]; /* Two lists */
+} __attribute__ ((packed));
+
+/*
+ * Ignore this raw location. This allows us to
+ * ignored metadata areas easily, and thus balance
+ * metadata across VGs with many PVs.
+ */
+#define RAW_LOCN_IGNORED 0x00000001
+
+/* On disk */
+struct raw_locn {
+ uint64_t offset; /* Offset in bytes to start sector */
+ uint64_t size; /* Bytes */
+ uint32_t checksum;
+ uint32_t flags;
+} __attribute__ ((packed));
+
+int rlocn_is_ignored(const struct raw_locn *rlocn);
+void rlocn_set_ignored(struct raw_locn *rlocn, unsigned mda_ignored);
+
+/* On disk */
+/* Structure size limited to one sector */
+struct mda_header {
+ uint32_t checksum_xl; /* Checksum of rest of mda_header */
+ int8_t magic[16]; /* To aid scans for metadata */
+ uint32_t version;
+ uint64_t start; /* Absolute start byte of mda_header */
+ uint64_t size; /* Size of metadata area */
+
+ struct raw_locn raw_locns[0]; /* NULL-terminated list */
+} __attribute__ ((packed));
+
+struct mda_header *raw_read_mda_header(const struct format_type *fmt,
+ struct device_area *dev_area);
+
+struct mda_lists {
+ struct dm_list dirs;
+ struct dm_list raws;
+ struct metadata_area_ops *file_ops;
+ struct metadata_area_ops *raw_ops;
+};
+
+struct mda_context {
+ struct device_area area;
+ uint64_t free_sectors;
+ struct raw_locn rlocn; /* Store inbetween write and commit */
+};
+
+/* FIXME Convert this at runtime */
+#define FMTT_MAGIC "\040\114\126\115\062\040\170\133\065\101\045\162\060\116\052\076"
+#define FMTT_VERSION 1
+#define MDA_HEADER_SIZE 512
+#define LVM2_LABEL "LVM2 001"
+#define MDA_SIZE_MIN (8 * (unsigned) lvm_getpagesize())
+
+
+const char *vgname_from_mda(const struct format_type *fmt,
+ struct mda_header *mdah,
+ struct device_area *dev_area, struct id *vgid,
+ uint64_t *vgstatus, char **creation_host,
+ uint64_t *mda_free_sectors);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "import-export.h"
+#include "str_list.h"
+#include "lvm-string.h"
+
+char *alloc_printed_tags(struct dm_list *tags)
+{
+ struct str_list *sl;
+ int first = 1;
+ size_t size = 0;
+ char *buffer, *buf;
+
+ dm_list_iterate_items(sl, tags)
+ /* '"' + tag + '"' + ',' + ' ' */
+ size += strlen(sl->str) + 4;
+ /* '[' + ']' + '\0' */
+ size += 3;
+
+ if (!(buffer = buf = dm_malloc(size))) {
+ log_error("Could not allocate memory for tag list buffer.");
+ return NULL;
+ }
+
+ if (!emit_to_buffer(&buf, &size, "["))
+ goto_bad;
+
+ dm_list_iterate_items(sl, tags) {
+ if (!first) {
+ if (!emit_to_buffer(&buf, &size, ", "))
+ goto_bad;
+ } else
+ first = 0;
+
+ if (!emit_to_buffer(&buf, &size, "\"%s\"", sl->str))
+ goto_bad;
+ }
+
+ if (!emit_to_buffer(&buf, &size, "]"))
+ goto_bad;
+
+ return buffer;
+
+bad:
+ dm_free(buffer);
+ return_NULL;
+}
+
+int read_tags(struct dm_pool *mem, struct dm_list *tags, const struct config_value *cv)
+{
+ if (cv->type == CFG_EMPTY_ARRAY)
+ return 1;
+
+ while (cv) {
+ if (cv->type != CFG_STRING) {
+ log_error("Found a tag that is not a string");
+ return 0;
+ }
+
+ if (!str_list_add(mem, tags, dm_pool_strdup(mem, cv->v.str)))
+ return_0;
+
+ cv = cv->next;
+ }
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_TEXT_EXPORT_H
+#define _LVM_TEXT_EXPORT_H
+
+#define outsize(args...) do {if (!out_size(args)) return_0;} while (0)
+#define outhint(args...) do {if (!out_hint(args)) return_0;} while (0)
+#define outfc(args...) do {if (!out_text_with_comment(args)) return_0;} while (0)
+#define outf(args...) do {if (!out_text(args)) return_0;} while (0)
+#define outnl(f) do {if (!out_newline(f)) return_0;} while (0)
+
+struct formatter;
+struct lv_segment;
+struct config_node;
+
+int out_size(struct formatter *f, uint64_t size, const char *fmt, ...)
+ __attribute__ ((format(printf, 3, 4)));
+
+int out_hint(struct formatter *f, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+
+int out_text(struct formatter *f, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+
+int out_config_node(struct formatter *f, const struct config_node *cn);
+
+int out_areas(struct formatter *f, const struct lv_segment *seg,
+ const char *type);
+
+int out_text_with_comment(struct formatter *f, const char* comment, const char *fmt, ...)
+ __attribute__ ((format(printf, 3, 4)));
+
+void out_inc_indent(struct formatter *f);
+void out_dec_indent(struct formatter *f);
+int out_newline(struct formatter *f);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_TEXT_IMPORT_H
+#define _LVM_TEXT_IMPORT_H
+
+struct lv_segment;
+struct config_node;
+
+int text_import_areas(struct lv_segment *seg, const struct config_node *sn,
+ const struct config_node *cn, struct dm_hash_table *pv_hash,
+ uint64_t status);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "format-text.h"
+#include "layout.h"
+#include "label.h"
+#include "xlate.h"
+#include "lvmcache.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static int _text_can_handle(struct labeller *l __attribute__((unused)),
+ void *buf,
+ uint64_t sector __attribute__((unused)))
+{
+ struct label_header *lh = (struct label_header *) buf;
+
+ if (!strncmp((char *)lh->type, LVM2_LABEL, sizeof(lh->type)))
+ return 1;
+
+ return 0;
+}
+
+static int _text_write(struct label *label, void *buf)
+{
+ struct label_header *lh = (struct label_header *) buf;
+ struct pv_header *pvhdr;
+ struct lvmcache_info *info;
+ struct disk_locn *pvh_dlocn_xl;
+ struct metadata_area *mda;
+ struct mda_context *mdac;
+ struct data_area_list *da;
+ char buffer[64] __attribute__((aligned(8)));
+ int da1, mda1, mda2;
+
+ /* FIXME Move to where label is created */
+ strncpy(label->type, LVM2_LABEL, sizeof(label->type));
+
+ strncpy((char *)lh->type, label->type, sizeof(label->type));
+
+ pvhdr = (struct pv_header *) ((void *) buf + xlate32(lh->offset_xl));
+ info = (struct lvmcache_info *) label->info;
+ pvhdr->device_size_xl = xlate64(info->device_size);
+ memcpy(pvhdr->pv_uuid, &info->dev->pvid, sizeof(struct id));
+ if (!id_write_format((const struct id *)pvhdr->pv_uuid, buffer,
+ sizeof(buffer))) {
+ stack;
+ buffer[0] = '\0';
+ }
+
+ pvh_dlocn_xl = &pvhdr->disk_areas_xl[0];
+
+ /* List of data areas (holding PEs) */
+ dm_list_iterate_items(da, &info->das) {
+ pvh_dlocn_xl->offset = xlate64(da->disk_locn.offset);
+ pvh_dlocn_xl->size = xlate64(da->disk_locn.size);
+ pvh_dlocn_xl++;
+ }
+
+ /* NULL-termination */
+ pvh_dlocn_xl->offset = xlate64(UINT64_C(0));
+ pvh_dlocn_xl->size = xlate64(UINT64_C(0));
+ pvh_dlocn_xl++;
+
+ /* List of metadata area header locations */
+ dm_list_iterate_items(mda, &info->mdas) {
+ mdac = (struct mda_context *) mda->metadata_locn;
+
+ if (mdac->area.dev != info->dev)
+ continue;
+
+ pvh_dlocn_xl->offset = xlate64(mdac->area.start);
+ pvh_dlocn_xl->size = xlate64(mdac->area.size);
+ pvh_dlocn_xl++;
+ }
+
+ /* NULL-termination */
+ pvh_dlocn_xl->offset = xlate64(UINT64_C(0));
+ pvh_dlocn_xl->size = xlate64(UINT64_C(0));
+
+ /* Create debug message with da and mda locations */
+ if (xlate64(pvhdr->disk_areas_xl[0].offset) ||
+ xlate64(pvhdr->disk_areas_xl[0].size))
+ da1 = 0;
+ else
+ da1 = -1;
+
+ mda1 = da1 + 2;
+ mda2 = mda1 + 1;
+
+ if (!xlate64(pvhdr->disk_areas_xl[mda1].offset) &&
+ !xlate64(pvhdr->disk_areas_xl[mda1].size))
+ mda1 = mda2 = 0;
+ else if (!xlate64(pvhdr->disk_areas_xl[mda2].offset) &&
+ !xlate64(pvhdr->disk_areas_xl[mda2].size))
+ mda2 = 0;
+
+ log_debug("%s: Preparing PV label header %s size %" PRIu64 " with"
+ "%s%.*" PRIu64 "%s%.*" PRIu64 "%s"
+ "%s%.*" PRIu64 "%s%.*" PRIu64 "%s"
+ "%s%.*" PRIu64 "%s%.*" PRIu64 "%s",
+ dev_name(info->dev), buffer, info->device_size,
+ (da1 > -1) ? " da1 (" : "",
+ (da1 > -1) ? 1 : 0,
+ (da1 > -1) ? xlate64(pvhdr->disk_areas_xl[da1].offset) >> SECTOR_SHIFT : 0,
+ (da1 > -1) ? "s, " : "",
+ (da1 > -1) ? 1 : 0,
+ (da1 > -1) ? xlate64(pvhdr->disk_areas_xl[da1].size) >> SECTOR_SHIFT : 0,
+ (da1 > -1) ? "s)" : "",
+ mda1 ? " mda1 (" : "",
+ mda1 ? 1 : 0,
+ mda1 ? xlate64(pvhdr->disk_areas_xl[mda1].offset) >> SECTOR_SHIFT : 0,
+ mda1 ? "s, " : "",
+ mda1 ? 1 : 0,
+ mda1 ? xlate64(pvhdr->disk_areas_xl[mda1].size) >> SECTOR_SHIFT : 0,
+ mda1 ? "s)" : "",
+ mda2 ? " mda2 (" : "",
+ mda2 ? 1 : 0,
+ mda2 ? xlate64(pvhdr->disk_areas_xl[mda2].offset) >> SECTOR_SHIFT : 0,
+ mda2 ? "s, " : "",
+ mda2 ? 1 : 0,
+ mda2 ? xlate64(pvhdr->disk_areas_xl[mda2].size) >> SECTOR_SHIFT : 0,
+ mda2 ? "s)" : "");
+
+ if (da1 < 0) {
+ log_error(INTERNAL_ERROR "%s label header currently requires "
+ "a data area.", dev_name(info->dev));
+ return 0;
+ }
+
+ return 1;
+}
+
+int add_da(struct dm_pool *mem, struct dm_list *das,
+ uint64_t start, uint64_t size)
+{
+ struct data_area_list *dal;
+
+ if (!mem) {
+ if (!(dal = dm_malloc(sizeof(*dal)))) {
+ log_error("struct data_area_list allocation failed");
+ return 0;
+ }
+ } else {
+ if (!(dal = dm_pool_alloc(mem, sizeof(*dal)))) {
+ log_error("struct data_area_list allocation failed");
+ return 0;
+ }
+ }
+
+ dal->disk_locn.offset = start;
+ dal->disk_locn.size = size;
+
+ dm_list_add(das, &dal->list);
+
+ return 1;
+}
+
+void del_das(struct dm_list *das)
+{
+ struct dm_list *dah, *tmp;
+ struct data_area_list *da;
+
+ dm_list_iterate_safe(dah, tmp, das) {
+ da = dm_list_item(dah, struct data_area_list);
+ dm_list_del(&da->list);
+ dm_free(da);
+ }
+}
+
+/* FIXME: refactor this function with other mda constructor code */
+int add_mda(const struct format_type *fmt, struct dm_pool *mem, struct dm_list *mdas,
+ struct device *dev, uint64_t start, uint64_t size, unsigned ignored)
+{
+/* FIXME List size restricted by pv_header SECTOR_SIZE */
+ struct metadata_area *mdal;
+ struct mda_lists *mda_lists = (struct mda_lists *) fmt->private;
+ struct mda_context *mdac;
+
+ if (!mem) {
+ if (!(mdal = dm_malloc(sizeof(struct metadata_area)))) {
+ log_error("struct mda_list allocation failed");
+ return 0;
+ }
+
+ if (!(mdac = dm_malloc(sizeof(struct mda_context)))) {
+ log_error("struct mda_context allocation failed");
+ dm_free(mdal);
+ return 0;
+ }
+ } else {
+ if (!(mdal = dm_pool_alloc(mem, sizeof(struct metadata_area)))) {
+ log_error("struct mda_list allocation failed");
+ return 0;
+ }
+
+ if (!(mdac = dm_pool_alloc(mem, sizeof(struct mda_context)))) {
+ log_error("struct mda_context allocation failed");
+ return 0;
+ }
+ }
+
+ mdal->ops = mda_lists->raw_ops;
+ mdal->metadata_locn = mdac;
+ mdal->status = 0;
+
+ mdac->area.dev = dev;
+ mdac->area.start = start;
+ mdac->area.size = size;
+ mdac->free_sectors = UINT64_C(0);
+ memset(&mdac->rlocn, 0, sizeof(mdac->rlocn));
+ mda_set_ignored(mdal, ignored);
+
+ dm_list_add(mdas, &mdal->list);
+ return 1;
+}
+
+void del_mdas(struct dm_list *mdas)
+{
+ struct dm_list *mdah, *tmp;
+ struct metadata_area *mda;
+
+ dm_list_iterate_safe(mdah, tmp, mdas) {
+ mda = dm_list_item(mdah, struct metadata_area);
+ dm_free(mda->metadata_locn);
+ dm_list_del(&mda->list);
+ dm_free(mda);
+ }
+}
+
+static int _text_initialise_label(struct labeller *l __attribute__((unused)),
+ struct label *label)
+{
+ strncpy(label->type, LVM2_LABEL, sizeof(label->type));
+
+ return 1;
+}
+
+static int _text_read(struct labeller *l, struct device *dev, void *buf,
+ struct label **label)
+{
+ struct label_header *lh = (struct label_header *) buf;
+ struct pv_header *pvhdr;
+ struct lvmcache_info *info;
+ struct disk_locn *dlocn_xl;
+ uint64_t offset;
+ struct metadata_area *mda;
+ struct id vgid;
+ struct mda_context *mdac;
+ const char *vgname;
+ uint64_t vgstatus;
+ char *creation_host;
+ struct mda_header *mdah;
+
+ pvhdr = (struct pv_header *) ((void *) buf + xlate32(lh->offset_xl));
+
+ if (!(info = lvmcache_add(l, (char *)pvhdr->pv_uuid, dev,
+ FMT_TEXT_ORPHAN_VG_NAME,
+ FMT_TEXT_ORPHAN_VG_NAME, 0)))
+ return_0;
+ *label = info->label;
+
+ info->device_size = xlate64(pvhdr->device_size_xl);
+
+ if (info->das.n)
+ del_das(&info->das);
+ dm_list_init(&info->das);
+
+ if (info->mdas.n)
+ del_mdas(&info->mdas);
+ dm_list_init(&info->mdas);
+
+ /* Data areas holding the PEs */
+ dlocn_xl = pvhdr->disk_areas_xl;
+ while ((offset = xlate64(dlocn_xl->offset))) {
+ add_da(NULL, &info->das, offset,
+ xlate64(dlocn_xl->size));
+ dlocn_xl++;
+ }
+
+ /* Metadata area headers */
+ dlocn_xl++;
+ while ((offset = xlate64(dlocn_xl->offset))) {
+ add_mda(info->fmt, NULL, &info->mdas, dev, offset,
+ xlate64(dlocn_xl->size), 0);
+ dlocn_xl++;
+ }
+
+ dm_list_iterate_items(mda, &info->mdas) {
+ mdac = (struct mda_context *) mda->metadata_locn;
+ if (!dev_open(mdac->area.dev)) {
+ mda_set_ignored(mda, 1);
+ stack;
+ continue;
+ }
+ if (!(mdah = raw_read_mda_header(info->fmt, &mdac->area))) {
+ stack;
+ goto close_dev;
+ }
+ mda_set_ignored(mda, rlocn_is_ignored(mdah->raw_locns));
+
+ if (mda_is_ignored(mda)) {
+ log_debug("Ignoring mda on device %s at offset %"PRIu64,
+ dev_name(mdac->area.dev),
+ mdac->area.start);
+ if (!dev_close(mdac->area.dev))
+ stack;
+ continue;
+ }
+
+ if ((vgname = vgname_from_mda(info->fmt, mdah,
+ &mdac->area,
+ &vgid, &vgstatus, &creation_host,
+ &mdac->free_sectors)) &&
+ !lvmcache_update_vgname_and_id(info, vgname,
+ (char *) &vgid, vgstatus,
+ creation_host)) {
+ if (!dev_close(mdac->area.dev))
+ stack;
+ return_0;
+ }
+ close_dev:
+ if (!dev_close(mdac->area.dev))
+ stack;
+ }
+
+ info->status &= ~CACHE_INVALID;
+
+ return 1;
+}
+
+static void _text_destroy_label(struct labeller *l __attribute__((unused)),
+ struct label *label)
+{
+ struct lvmcache_info *info = (struct lvmcache_info *) label->info;
+
+ if (info->mdas.n)
+ del_mdas(&info->mdas);
+ if (info->das.n)
+ del_das(&info->das);
+}
+
+static void _fmt_text_destroy(struct labeller *l)
+{
+ dm_free(l);
+}
+
+struct label_ops _text_ops = {
+ .can_handle = _text_can_handle,
+ .write = _text_write,
+ .read = _text_read,
+ .verify = _text_can_handle,
+ .initialise_label = _text_initialise_label,
+ .destroy_label = _text_destroy_label,
+ .destroy = _fmt_text_destroy,
+};
+
+struct labeller *text_labeller_create(const struct format_type *fmt)
+{
+ struct labeller *l;
+
+ if (!(l = dm_malloc(sizeof(*l)))) {
+ log_error("Couldn't allocate labeller object.");
+ return NULL;
+ }
+
+ l->ops = &_text_ops;
+ l->private = (const void *) fmt;
+
+ return l;
+}
--- /dev/null
+/*
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "display.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "str_list.h"
+#include "targets.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "str_list.h"
+#include "metadata.h"
+
+static const char *_freeseg_name(const struct lv_segment *seg)
+{
+ return seg->segtype->name;
+}
+
+static void _freeseg_destroy(struct segment_type *segtype)
+{
+ dm_free(segtype);
+}
+
+static struct segtype_handler _freeseg_ops = {
+ .name = _freeseg_name,
+ .destroy = _freeseg_destroy,
+};
+
+struct segment_type *init_free_segtype(struct cmd_context *cmd)
+{
+ struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+ if (!segtype)
+ return_NULL;
+
+ segtype->cmd = cmd;
+ segtype->ops = &_freeseg_ops;
+ segtype->name = "free";
+ segtype->private = NULL;
+ segtype->flags = SEG_VIRTUAL | SEG_CANNOT_BE_ZEROED;
+
+ log_very_verbose("Initialised segtype: %s", segtype->name);
+
+ return segtype;
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "label.h"
+#include "crc.h"
+#include "xlate.h"
+#include "lvmcache.h"
+#include "metadata.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* FIXME Allow for larger labels? Restricted to single sector currently */
+
+/*
+ * Internal labeller struct.
+ */
+struct labeller_i {
+ struct dm_list list;
+
+ struct labeller *l;
+ char name[0];
+};
+
+static struct dm_list _labellers;
+
+static struct labeller_i *_alloc_li(const char *name, struct labeller *l)
+{
+ struct labeller_i *li;
+ size_t len;
+
+ len = sizeof(*li) + strlen(name) + 1;
+
+ if (!(li = dm_malloc(len))) {
+ log_error("Couldn't allocate memory for labeller list object.");
+ return NULL;
+ }
+
+ li->l = l;
+ strcpy(li->name, name);
+
+ return li;
+}
+
+static void _free_li(struct labeller_i *li)
+{
+ dm_free(li);
+}
+
+int label_init(void)
+{
+ dm_list_init(&_labellers);
+ return 1;
+}
+
+void label_exit(void)
+{
+ struct dm_list *c, *n;
+ struct labeller_i *li;
+
+ for (c = _labellers.n; c && c != &_labellers; c = n) {
+ n = c->n;
+ li = dm_list_item(c, struct labeller_i);
+ li->l->ops->destroy(li->l);
+ _free_li(li);
+ }
+
+ dm_list_init(&_labellers);
+}
+
+int label_register_handler(const char *name, struct labeller *handler)
+{
+ struct labeller_i *li;
+
+ if (!(li = _alloc_li(name, handler)))
+ return_0;
+
+ dm_list_add(&_labellers, &li->list);
+ return 1;
+}
+
+struct labeller *label_get_handler(const char *name)
+{
+ struct labeller_i *li;
+
+ dm_list_iterate_items(li, &_labellers)
+ if (!strcmp(li->name, name))
+ return li->l;
+
+ return NULL;
+}
+
+static struct labeller *_find_labeller(struct device *dev, char *buf,
+ uint64_t *label_sector,
+ uint64_t scan_sector)
+{
+ struct labeller_i *li;
+ struct labeller *r = NULL;
+ struct label_header *lh;
+ struct lvmcache_info *info;
+ uint64_t sector;
+ int found = 0;
+ char readbuf[LABEL_SCAN_SIZE] __attribute__((aligned(8)));
+
+ if (!dev_read(dev, scan_sector << SECTOR_SHIFT,
+ LABEL_SCAN_SIZE, readbuf)) {
+ log_debug("%s: Failed to read label area", dev_name(dev));
+ goto out;
+ }
+
+ /* Scan a few sectors for a valid label */
+ for (sector = 0; sector < LABEL_SCAN_SECTORS;
+ sector += LABEL_SIZE >> SECTOR_SHIFT) {
+ lh = (struct label_header *) (readbuf +
+ (sector << SECTOR_SHIFT));
+
+ if (!strncmp((char *)lh->id, LABEL_ID, sizeof(lh->id))) {
+ if (found) {
+ log_error("Ignoring additional label on %s at "
+ "sector %" PRIu64, dev_name(dev),
+ sector + scan_sector);
+ }
+ if (xlate64(lh->sector_xl) != sector + scan_sector) {
+ log_info("%s: Label for sector %" PRIu64
+ " found at sector %" PRIu64
+ " - ignoring", dev_name(dev),
+ (uint64_t)xlate64(lh->sector_xl),
+ sector + scan_sector);
+ continue;
+ }
+ if (calc_crc(INITIAL_CRC, (uint8_t *)&lh->offset_xl, LABEL_SIZE -
+ ((uint8_t *) &lh->offset_xl - (uint8_t *) lh)) !=
+ xlate32(lh->crc_xl)) {
+ log_info("Label checksum incorrect on %s - "
+ "ignoring", dev_name(dev));
+ continue;
+ }
+ if (found)
+ continue;
+ }
+
+ dm_list_iterate_items(li, &_labellers) {
+ if (li->l->ops->can_handle(li->l, (char *) lh,
+ sector + scan_sector)) {
+ log_very_verbose("%s: %s label detected",
+ dev_name(dev), li->name);
+ if (found) {
+ log_error("Ignoring additional label "
+ "on %s at sector %" PRIu64,
+ dev_name(dev),
+ sector + scan_sector);
+ continue;
+ }
+ r = li->l;
+ memcpy(buf, lh, LABEL_SIZE);
+ if (label_sector)
+ *label_sector = sector + scan_sector;
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ out:
+ if (!found) {
+ if ((info = info_from_pvid(dev->pvid, 0)))
+ lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name,
+ info->fmt->orphan_vg_name,
+ 0, NULL);
+ log_very_verbose("%s: No label detected", dev_name(dev));
+ }
+
+ return r;
+}
+
+/* FIXME Also wipe associated metadata area headers? */
+int label_remove(struct device *dev)
+{
+ char buf[LABEL_SIZE] __attribute__((aligned(8)));
+ char readbuf[LABEL_SCAN_SIZE] __attribute__((aligned(8)));
+ int r = 1;
+ uint64_t sector;
+ int wipe;
+ struct labeller_i *li;
+ struct label_header *lh;
+
+ memset(buf, 0, LABEL_SIZE);
+
+ log_very_verbose("Scanning for labels to wipe from %s", dev_name(dev));
+
+ if (!dev_open(dev))
+ return_0;
+
+ /*
+ * We flush the device just in case someone is stupid
+ * enough to be trying to import an open pv into lvm.
+ */
+ dev_flush(dev);
+
+ if (!dev_read(dev, UINT64_C(0), LABEL_SCAN_SIZE, readbuf)) {
+ log_debug("%s: Failed to read label area", dev_name(dev));
+ goto out;
+ }
+
+ /* Scan first few sectors for anything looking like a label */
+ for (sector = 0; sector < LABEL_SCAN_SECTORS;
+ sector += LABEL_SIZE >> SECTOR_SHIFT) {
+ lh = (struct label_header *) (readbuf +
+ (sector << SECTOR_SHIFT));
+
+ wipe = 0;
+
+ if (!strncmp((char *)lh->id, LABEL_ID, sizeof(lh->id))) {
+ if (xlate64(lh->sector_xl) == sector)
+ wipe = 1;
+ } else {
+ dm_list_iterate_items(li, &_labellers) {
+ if (li->l->ops->can_handle(li->l, (char *) lh,
+ sector)) {
+ wipe = 1;
+ break;
+ }
+ }
+ }
+
+ if (wipe) {
+ log_info("%s: Wiping label at sector %" PRIu64,
+ dev_name(dev), sector);
+ if (!dev_write(dev, sector << SECTOR_SHIFT, LABEL_SIZE,
+ buf)) {
+ log_error("Failed to remove label from %s at "
+ "sector %" PRIu64, dev_name(dev),
+ sector);
+ r = 0;
+ }
+ }
+ }
+
+ out:
+ if (!dev_close(dev))
+ stack;
+
+ return r;
+}
+
+int label_read(struct device *dev, struct label **result,
+ uint64_t scan_sector)
+{
+ char buf[LABEL_SIZE] __attribute__((aligned(8)));
+ struct labeller *l;
+ uint64_t sector;
+ struct lvmcache_info *info;
+ int r = 0;
+
+ if ((info = info_from_pvid(dev->pvid, 1))) {
+ log_debug("Using cached label for %s", dev_name(dev));
+ *result = info->label;
+ return 1;
+ }
+
+ if (!dev_open(dev)) {
+ stack;
+
+ if ((info = info_from_pvid(dev->pvid, 0)))
+ lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name,
+ info->fmt->orphan_vg_name,
+ 0, NULL);
+
+ return r;
+ }
+
+ if (!(l = _find_labeller(dev, buf, §or, scan_sector)))
+ goto out;
+
+ if ((r = (l->ops->read)(l, dev, buf, result)) && result && *result)
+ (*result)->sector = sector;
+
+ out:
+ if (!dev_close(dev))
+ stack;
+
+ return r;
+}
+
+/* Caller may need to use label_get_handler to create label struct! */
+int label_write(struct device *dev, struct label *label)
+{
+ char buf[LABEL_SIZE] __attribute__((aligned(8)));
+ struct label_header *lh = (struct label_header *) buf;
+ int r = 1;
+
+ if (!label->labeller->ops->write) {
+ log_error("Label handler does not support label writes");
+ return 0;
+ }
+
+ if ((LABEL_SIZE + (label->sector << SECTOR_SHIFT)) > LABEL_SCAN_SIZE) {
+ log_error("Label sector %" PRIu64 " beyond range (%ld)",
+ label->sector, LABEL_SCAN_SECTORS);
+ return 0;
+ }
+
+ memset(buf, 0, LABEL_SIZE);
+
+ strncpy((char *)lh->id, LABEL_ID, sizeof(lh->id));
+ lh->sector_xl = xlate64(label->sector);
+ lh->offset_xl = xlate32(sizeof(*lh));
+
+ if (!(label->labeller->ops->write)(label, buf))
+ return_0;
+
+ lh->crc_xl = xlate32(calc_crc(INITIAL_CRC, (uint8_t *)&lh->offset_xl, LABEL_SIZE -
+ ((uint8_t *) &lh->offset_xl - (uint8_t *) lh)));
+
+ if (!dev_open(dev))
+ return_0;
+
+ log_info("%s: Writing label to sector %" PRIu64 " with stored offset %"
+ PRIu32 ".", dev_name(dev), label->sector,
+ xlate32(lh->offset_xl));
+ if (!dev_write(dev, label->sector << SECTOR_SHIFT, LABEL_SIZE, buf)) {
+ log_debug("Failed to write label to %s", dev_name(dev));
+ r = 0;
+ }
+
+ if (!dev_close(dev))
+ stack;
+
+ return r;
+}
+
+/* Unused */
+int label_verify(struct device *dev)
+{
+ struct labeller *l;
+ char buf[LABEL_SIZE] __attribute__((aligned(8)));
+ uint64_t sector;
+ struct lvmcache_info *info;
+ int r = 0;
+
+ if (!dev_open(dev)) {
+ if ((info = info_from_pvid(dev->pvid, 0)))
+ lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name,
+ info->fmt->orphan_vg_name,
+ 0, NULL);
+
+ return_0;
+ }
+
+ if (!(l = _find_labeller(dev, buf, §or, UINT64_C(0))))
+ goto out;
+
+ r = l->ops->verify ? l->ops->verify(l, buf, sector) : 1;
+
+ out:
+ if (!dev_close(dev))
+ stack;
+
+ return r;
+}
+
+void label_destroy(struct label *label)
+{
+ label->labeller->ops->destroy_label(label->labeller, label);
+ dm_free(label);
+}
+
+struct label *label_create(struct labeller *labeller)
+{
+ struct label *label;
+
+ if (!(label = dm_zalloc(sizeof(*label)))) {
+ log_error("label allocaction failed");
+ return NULL;
+ }
+
+ label->labeller = labeller;
+
+ labeller->ops->initialise_label(labeller, label);
+
+ return label;
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_LABEL_H
+#define _LVM_LABEL_H
+
+#include "uuid.h"
+#include "device.h"
+
+#define LABEL_ID "LABELONE"
+#define LABEL_SIZE SECTOR_SIZE /* Think very carefully before changing this */
+#define LABEL_SCAN_SECTORS 4L
+#define LABEL_SCAN_SIZE (LABEL_SCAN_SECTORS << SECTOR_SHIFT)
+
+struct labeller;
+
+/* On disk - 32 bytes */
+struct label_header {
+ int8_t id[8]; /* LABELONE */
+ uint64_t sector_xl; /* Sector number of this label */
+ uint32_t crc_xl; /* From next field to end of sector */
+ uint32_t offset_xl; /* Offset from start of struct to contents */
+ int8_t type[8]; /* LVM2 001 */
+} __attribute__ ((packed));
+
+/* In core */
+struct label {
+ char type[8];
+ uint64_t sector;
+ struct labeller *labeller;
+ void *info;
+};
+
+struct labeller;
+
+struct label_ops {
+ /*
+ * Is the device labelled with this format ?
+ */
+ int (*can_handle) (struct labeller * l, void *buf, uint64_t sector);
+
+ /*
+ * Write a label to a volume.
+ */
+ int (*write) (struct label * label, void *buf);
+
+ /*
+ * Read a label from a volume.
+ */
+ int (*read) (struct labeller * l, struct device * dev,
+ void *buf, struct label ** label);
+
+ /*
+ * Additional consistency checks for the paranoid.
+ */
+ int (*verify) (struct labeller * l, void *buf, uint64_t sector);
+
+ /*
+ * Populate label_type etc.
+ */
+ int (*initialise_label) (struct labeller * l, struct label * label);
+
+ /*
+ * Destroy a previously read label.
+ */
+ void (*destroy_label) (struct labeller * l, struct label * label);
+
+ /*
+ * Destructor.
+ */
+ void (*destroy) (struct labeller * l);
+};
+
+struct labeller {
+ struct label_ops *ops;
+ const void *private;
+};
+
+int label_init(void);
+void label_exit(void);
+
+int label_register_handler(const char *name, struct labeller *handler);
+
+struct labeller *label_get_handler(const char *name);
+
+int label_remove(struct device *dev);
+int label_read(struct device *dev, struct label **result,
+ uint64_t scan_sector);
+int label_write(struct device *dev, struct label *label);
+int label_verify(struct device *dev);
+struct label *label_create(struct labeller *labeller);
+void label_destroy(struct label *label);
+
+#endif
--- /dev/null
+locking_init
+locking_end
+lock_resource
+query_resource
+reset_locking
--- /dev/null
+#
+# Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES = cluster_locking.c
+
+LIB_SHARED = liblvm2clusterlock.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+install install_cluster: install_lvm2_plugin
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Locking functions for LVM.
+ * The main purpose of this part of the library is to serialise LVM
+ * management operations across a cluster.
+ */
+
+#include "lib.h"
+#include "clvm.h"
+#include "lvm-string.h"
+#include "locking.h"
+#include "locking_types.h"
+#include "toolcontext.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#ifndef CLUSTER_LOCKING_INTERNAL
+int lock_resource(struct cmd_context *cmd, const char *resource, uint32_t flags);
+int query_resource(const char *resource, int *mode);
+void locking_end(void);
+int locking_init(int type, struct config_tree *cf, uint32_t *flags);
+#endif
+
+typedef struct lvm_response {
+ char node[255];
+ char *response;
+ int status;
+ int len;
+} lvm_response_t;
+
+/*
+ * This gets stuck at the start of memory we allocate so we
+ * can sanity-check it at deallocation time
+ */
+#define LVM_SIGNATURE 0x434C564D
+
+/*
+ * NOTE: the LVMD uses the socket FD as the client ID, this means
+ * that any client that calls fork() will inherit the context of
+ * it's parent.
+ */
+static int _clvmd_sock = -1;
+
+/* FIXME Install SIGPIPE handler? */
+
+/* Open connection to the Cluster Manager daemon */
+static int _open_local_sock(void)
+{
+ int local_socket;
+ struct sockaddr_un sockaddr;
+
+ /* Open local socket */
+ if ((local_socket = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ log_error("Local socket creation failed: %s", strerror(errno));
+ return -1;
+ }
+
+ memset(&sockaddr, 0, sizeof(sockaddr));
+ memcpy(sockaddr.sun_path, CLVMD_SOCKNAME, sizeof(CLVMD_SOCKNAME));
+
+ sockaddr.sun_family = AF_UNIX;
+
+ if (connect(local_socket,(struct sockaddr *) &sockaddr,
+ sizeof(sockaddr))) {
+ int saved_errno = errno;
+
+ log_error("connect() failed on local socket: %s",
+ strerror(errno));
+ if (close(local_socket))
+ stack;
+
+ errno = saved_errno;
+ return -1;
+ }
+
+ return local_socket;
+}
+
+/* Send a request and return the status */
+static int _send_request(char *inbuf, int inlen, char **retbuf)
+{
+ char outbuf[PIPE_BUF] __attribute__((aligned(8)));
+ struct clvm_header *outheader = (struct clvm_header *) outbuf;
+ int len;
+ int off;
+ int buflen;
+ int err;
+
+ /* Send it to CLVMD */
+ rewrite:
+ if ( (err = write(_clvmd_sock, inbuf, inlen)) != inlen) {
+ if (err == -1 && errno == EINTR)
+ goto rewrite;
+ log_error("Error writing data to clvmd: %s", strerror(errno));
+ return 0;
+ }
+
+ /* Get the response */
+ reread:
+ if ((len = read(_clvmd_sock, outbuf, sizeof(struct clvm_header))) < 0) {
+ if (errno == EINTR)
+ goto reread;
+ log_error("Error reading data from clvmd: %s", strerror(errno));
+ return 0;
+ }
+
+ if (len == 0) {
+ log_error("EOF reading CLVMD");
+ errno = ENOTCONN;
+ return 0;
+ }
+
+ /* Allocate buffer */
+ buflen = len + outheader->arglen;
+ *retbuf = dm_malloc(buflen);
+ if (!*retbuf) {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ /* Copy the header */
+ memcpy(*retbuf, outbuf, len);
+ outheader = (struct clvm_header *) *retbuf;
+
+ /* Read the returned values */
+ off = 1; /* we've already read the first byte */
+ while (off <= outheader->arglen && len > 0) {
+ len = read(_clvmd_sock, outheader->args + off,
+ buflen - off - offsetof(struct clvm_header, args));
+ if (len > 0)
+ off += len;
+ }
+
+ /* Was it an error ? */
+ if (outheader->status != 0) {
+ errno = outheader->status;
+
+ /* Only return an error here if there are no node-specific
+ errors present in the message that might have more detail */
+ if (!(outheader->flags & CLVMD_FLAG_NODEERRS)) {
+ log_error("cluster request failed: %s", strerror(errno));
+ return 0;
+ }
+
+ }
+
+ return 1;
+}
+
+/* Build the structure header and parse-out wildcard node names */
+/* FIXME: Cleanup implicit casts of clvmd_cmd (int, char, uint8_t, etc). */
+static void _build_header(struct clvm_header *head, int clvmd_cmd, const char *node,
+ int len)
+{
+ head->cmd = clvmd_cmd;
+ head->status = 0;
+ head->flags = 0;
+ head->clientid = 0;
+ head->arglen = len;
+
+ if (node) {
+ /*
+ * Allow a couple of special node names:
+ * "*" for all nodes,
+ * "." for the local node only
+ */
+ if (strcmp(node, "*") == 0) {
+ head->node[0] = '\0';
+ } else if (strcmp(node, ".") == 0) {
+ head->node[0] = '\0';
+ head->flags = CLVMD_FLAG_LOCAL;
+ } else
+ strcpy(head->node, node);
+ } else
+ head->node[0] = '\0';
+}
+
+/*
+ * Send a message to a(or all) node(s) in the cluster and wait for replies
+ */
+static int _cluster_request(char clvmd_cmd, const char *node, void *data, int len,
+ lvm_response_t ** response, int *num)
+{
+ char outbuf[sizeof(struct clvm_header) + len + strlen(node) + 1] __attribute__((aligned(8)));
+ char *inptr;
+ char *retbuf = NULL;
+ int status;
+ int i;
+ int num_responses = 0;
+ struct clvm_header *head = (struct clvm_header *) outbuf;
+ lvm_response_t *rarray;
+
+ *num = 0;
+
+ if (_clvmd_sock == -1)
+ _clvmd_sock = _open_local_sock();
+
+ if (_clvmd_sock == -1)
+ return 0;
+
+ _build_header(head, clvmd_cmd, node, len);
+ memcpy(head->node + strlen(head->node) + 1, data, len);
+
+ status = _send_request(outbuf, sizeof(struct clvm_header) +
+ strlen(head->node) + len, &retbuf);
+ if (!status)
+ goto out;
+
+ /* Count the number of responses we got */
+ head = (struct clvm_header *) retbuf;
+ inptr = head->args;
+ while (inptr[0]) {
+ num_responses++;
+ inptr += strlen(inptr) + 1;
+ inptr += sizeof(int);
+ inptr += strlen(inptr) + 1;
+ }
+
+ /*
+ * Allocate response array.
+ * With an extra pair of INTs on the front to sanity
+ * check the pointer when we are given it back to free
+ */
+ *response = dm_malloc(sizeof(lvm_response_t) * num_responses);
+ if (!*response) {
+ errno = ENOMEM;
+ status = 0;
+ goto out;
+ }
+
+ rarray = *response;
+
+ /* Unpack the response into an lvm_response_t array */
+ inptr = head->args;
+ i = 0;
+ while (inptr[0]) {
+ strcpy(rarray[i].node, inptr);
+ inptr += strlen(inptr) + 1;
+
+ memcpy(&rarray[i].status, inptr, sizeof(int));
+ inptr += sizeof(int);
+
+ rarray[i].response = dm_malloc(strlen(inptr) + 1);
+ if (rarray[i].response == NULL) {
+ /* Free up everything else and return error */
+ int j;
+ for (j = 0; j < i; j++)
+ dm_free(rarray[i].response);
+ free(*response);
+ errno = ENOMEM;
+ status = -1;
+ goto out;
+ }
+
+ strcpy(rarray[i].response, inptr);
+ rarray[i].len = strlen(inptr);
+ inptr += strlen(inptr) + 1;
+ i++;
+ }
+ *num = num_responses;
+ *response = rarray;
+
+ out:
+ if (retbuf)
+ dm_free(retbuf);
+
+ return status;
+}
+
+/* Free reply array */
+static int _cluster_free_request(lvm_response_t * response, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ dm_free(response[i].response);
+ }
+
+ dm_free(response);
+
+ return 1;
+}
+
+static int _lock_for_cluster(struct cmd_context *cmd, unsigned char clvmd_cmd,
+ uint32_t flags, const char *name)
+{
+ int status;
+ int i;
+ char *args;
+ const char *node = "";
+ int len;
+ int dmeventd_mode;
+ int saved_errno;
+ lvm_response_t *response = NULL;
+ int num_responses;
+
+ assert(name);
+
+ len = strlen(name) + 3;
+ args = alloca(len);
+ strcpy(args + 2, name);
+
+ /* Mask off lock flags */
+ args[0] = flags & (LCK_SCOPE_MASK | LCK_TYPE_MASK | LCK_NONBLOCK | LCK_HOLD);
+ args[1] = flags & (LCK_LOCAL | LCK_CLUSTER_VG);
+
+ if (flags & LCK_ORIGIN_ONLY)
+ args[1] |= LCK_ORIGIN_ONLY_MODE;
+
+ if (mirror_in_sync())
+ args[1] |= LCK_MIRROR_NOSYNC_MODE;
+
+ /*
+ * Must handle tri-state return from dmeventd_monitor_mode.
+ * But DMEVENTD_MONITOR_IGNORE is not propagated across the cluster.
+ */
+ dmeventd_mode = dmeventd_monitor_mode();
+ if (dmeventd_mode != DMEVENTD_MONITOR_IGNORE && dmeventd_mode)
+ args[1] |= LCK_DMEVENTD_MONITOR_MODE;
+
+ if (cmd->partial_activation)
+ args[1] |= LCK_PARTIAL_MODE;
+
+ /*
+ * VG locks are just that: locks, and have no side effects
+ * so we only need to do them on the local node because all
+ * locks are cluster-wide.
+ * Also, if the lock is exclusive it makes no sense to try to
+ * acquire it on all nodes, so just do that on the local node too.
+ * One exception, is that P_ locks /do/ get distributed across
+ * the cluster because they might have side-effects.
+ */
+ if (strncmp(name, "P_", 2) &&
+ (clvmd_cmd == CLVMD_CMD_LOCK_VG ||
+ (flags & LCK_TYPE_MASK) == LCK_EXCL ||
+ (flags & LCK_LOCAL) ||
+ !(flags & LCK_CLUSTER_VG)))
+ node = ".";
+
+ status = _cluster_request(clvmd_cmd, node, args, len,
+ &response, &num_responses);
+
+ /* If any nodes were down then display them and return an error */
+ for (i = 0; i < num_responses; i++) {
+ if (response[i].status == EHOSTDOWN) {
+ log_error("clvmd not running on node %s",
+ response[i].node);
+ status = 0;
+ errno = response[i].status;
+ } else if (response[i].status) {
+ log_error("Error locking on node %s: %s",
+ response[i].node,
+ response[i].response[0] ?
+ response[i].response :
+ strerror(response[i].status));
+ status = 0;
+ errno = response[i].status;
+ }
+ }
+
+ saved_errno = errno;
+ _cluster_free_request(response, num_responses);
+ errno = saved_errno;
+
+ return status;
+}
+
+/* API entry point for LVM */
+#ifdef CLUSTER_LOCKING_INTERNAL
+static int _lock_resource(struct cmd_context *cmd, const char *resource,
+ uint32_t flags)
+#else
+int lock_resource(struct cmd_context *cmd, const char *resource, uint32_t flags)
+#endif
+{
+ char lockname[PATH_MAX];
+ int clvmd_cmd = 0;
+ const char *lock_scope;
+ const char *lock_type = "";
+
+ assert(strlen(resource) < sizeof(lockname));
+ assert(resource);
+
+ switch (flags & LCK_SCOPE_MASK) {
+ case LCK_VG:
+ if (flags == LCK_VG_BACKUP) {
+ log_very_verbose("Requesting backup of VG metadata for %s",
+ resource);
+ return _lock_for_cluster(cmd, CLVMD_CMD_VG_BACKUP,
+ LCK_CLUSTER_VG, resource);
+ }
+
+ /* If the VG name is empty then lock the unused PVs */
+ if (is_orphan_vg(resource) || is_global_vg(resource) || (flags & LCK_CACHE))
+ dm_snprintf(lockname, sizeof(lockname), "P_%s",
+ resource);
+ else
+ dm_snprintf(lockname, sizeof(lockname), "V_%s",
+ resource);
+
+ lock_scope = "VG";
+ clvmd_cmd = CLVMD_CMD_LOCK_VG;
+ /*
+ * Old clvmd does not expect LCK_HOLD which was already processed
+ * in lock_vol, mask it for compatibility reasons.
+ */
+ if (flags != LCK_VG_COMMIT && flags != LCK_VG_REVERT)
+ flags &= ~LCK_HOLD;
+
+ break;
+
+ case LCK_LV:
+ clvmd_cmd = CLVMD_CMD_LOCK_LV;
+ strcpy(lockname, resource);
+ lock_scope = "LV";
+ flags &= ~LCK_HOLD; /* Mask off HOLD flag */
+ break;
+
+ default:
+ log_error("Unrecognised lock scope: %d",
+ flags & LCK_SCOPE_MASK);
+ return 0;
+ }
+
+ switch(flags & LCK_TYPE_MASK) {
+ case LCK_UNLOCK:
+ lock_type = "UN";
+ break;
+ case LCK_NULL:
+ lock_type = "NL";
+ break;
+ case LCK_READ:
+ lock_type = "CR";
+ break;
+ case LCK_PREAD:
+ lock_type = "PR";
+ break;
+ case LCK_WRITE:
+ lock_type = "PW";
+ break;
+ case LCK_EXCL:
+ lock_type = "EX";
+ break;
+ default:
+ log_error("Unrecognised lock type: %u",
+ flags & LCK_TYPE_MASK);
+ return 0;
+ }
+
+ log_very_verbose("Locking %s %s %s (%s%s%s%s%s%s%s) (0x%x)", lock_scope, lockname,
+ lock_type, lock_scope,
+ flags & LCK_NONBLOCK ? "|NONBLOCK" : "",
+ flags & LCK_HOLD ? "|HOLD" : "",
+ flags & LCK_LOCAL ? "|LOCAL" : "",
+ flags & LCK_CLUSTER_VG ? "|CLUSTER" : "",
+ flags & LCK_CACHE ? "|CACHE" : "",
+ flags & LCK_ORIGIN_ONLY ? "|ORIGIN_ONLY" : "",
+ flags);
+
+ /* Send a message to the cluster manager */
+ return _lock_for_cluster(cmd, clvmd_cmd, flags, lockname);
+}
+
+static int decode_lock_type(const char *response)
+{
+ if (!response)
+ return LCK_NULL;
+ else if (strcmp(response, "EX"))
+ return LCK_EXCL;
+ else if (strcmp(response, "CR"))
+ return LCK_READ;
+ else if (strcmp(response, "PR"))
+ return LCK_PREAD;
+
+ stack;
+ return 0;
+}
+
+#ifdef CLUSTER_LOCKING_INTERNAL
+static int _query_resource(const char *resource, int *mode)
+#else
+int query_resource(const char *resource, int *mode)
+#endif
+{
+ int i, status, len, num_responses, saved_errno;
+ const char *node = "";
+ char *args;
+ lvm_response_t *response = NULL;
+
+ saved_errno = errno;
+ len = strlen(resource) + 3;
+ args = alloca(len);
+ strcpy(args + 2, resource);
+
+ args[0] = 0;
+ args[1] = LCK_CLUSTER_VG;
+
+ status = _cluster_request(CLVMD_CMD_LOCK_QUERY, node, args, len,
+ &response, &num_responses);
+ *mode = LCK_NULL;
+ for (i = 0; i < num_responses; i++) {
+ if (response[i].status == EHOSTDOWN)
+ continue;
+
+ if (!response[i].response[0])
+ continue;
+
+ /*
+ * All nodes should use CR, or exactly one node
+ * should held EX. (PR is obsolete)
+ * If two nodes node reports different locks,
+ * something is broken - just return more important mode.
+ */
+ if (decode_lock_type(response[i].response) > *mode)
+ *mode = decode_lock_type(response[i].response);
+
+ log_debug("Lock held for %s, node %s : %s", resource,
+ response[i].node, response[i].response);
+ }
+
+ _cluster_free_request(response, num_responses);
+ errno = saved_errno;
+
+ return status;
+}
+
+#ifdef CLUSTER_LOCKING_INTERNAL
+static void _locking_end(void)
+#else
+void locking_end(void)
+#endif
+{
+ if (_clvmd_sock != -1 && close(_clvmd_sock))
+ stack;
+
+ _clvmd_sock = -1;
+}
+
+#ifdef CLUSTER_LOCKING_INTERNAL
+static void _reset_locking(void)
+#else
+void reset_locking(void)
+#endif
+{
+ if (close(_clvmd_sock))
+ stack;
+
+ _clvmd_sock = _open_local_sock();
+ if (_clvmd_sock == -1)
+ stack;
+}
+
+#ifdef CLUSTER_LOCKING_INTERNAL
+int init_cluster_locking(struct locking_type *locking, struct cmd_context *cmd)
+{
+ locking->lock_resource = _lock_resource;
+ locking->query_resource = _query_resource;
+ locking->fin_locking = _locking_end;
+ locking->reset_locking = _reset_locking;
+ locking->flags = LCK_PRE_MEMLOCK | LCK_CLUSTERED;
+
+ _clvmd_sock = _open_local_sock();
+ if (_clvmd_sock == -1)
+ return 0;
+
+ return 1;
+}
+#else
+int locking_init(int type, struct config_tree *cf, uint32_t *flags)
+{
+ _clvmd_sock = _open_local_sock();
+ if (_clvmd_sock == -1)
+ return 0;
+
+ /* Ask LVM to lock memory before calling us */
+ *flags |= LCK_PRE_MEMLOCK;
+ *flags |= LCK_CLUSTERED;
+
+ return 1;
+}
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "locking_types.h"
+#include "defaults.h"
+#include "sharedlib.h"
+#include "toolcontext.h"
+
+static void *_locking_lib = NULL;
+static void (*_reset_fn) (void) = NULL;
+static void (*_end_fn) (void) = NULL;
+static int (*_lock_fn) (struct cmd_context * cmd, const char *resource,
+ uint32_t flags) = NULL;
+static int (*_init_fn) (int type, struct config_tree * cft,
+ uint32_t *flags) = NULL;
+static int (*_lock_query_fn) (const char *resource, int *mode) = NULL;
+
+static int _lock_resource(struct cmd_context *cmd, const char *resource,
+ uint32_t flags)
+{
+ if (_lock_fn)
+ return _lock_fn(cmd, resource, flags);
+ else
+ return 0;
+}
+
+static void _fin_external_locking(void)
+{
+ if (_end_fn)
+ _end_fn();
+
+ dlclose(_locking_lib);
+
+ _locking_lib = NULL;
+ _init_fn = NULL;
+ _end_fn = NULL;
+ _lock_fn = NULL;
+ _reset_fn = NULL;
+}
+
+static void _reset_external_locking(void)
+{
+ if (_reset_fn)
+ _reset_fn();
+}
+
+int init_external_locking(struct locking_type *locking, struct cmd_context *cmd)
+{
+ const char *libname;
+
+ if (_locking_lib) {
+ log_error("External locking already initialised");
+ return 1;
+ }
+
+ locking->lock_resource = _lock_resource;
+ locking->fin_locking = _fin_external_locking;
+ locking->reset_locking = _reset_external_locking;
+ locking->flags = 0;
+
+ libname = find_config_tree_str(cmd, "global/locking_library",
+ DEFAULT_LOCKING_LIB);
+
+ if (!(_locking_lib = load_shared_library(cmd, libname, "locking", 1)))
+ return_0;
+
+ /* Get the functions we need */
+ if (!(_init_fn = dlsym(_locking_lib, "locking_init")) ||
+ !(_lock_fn = dlsym(_locking_lib, "lock_resource")) ||
+ !(_reset_fn = dlsym(_locking_lib, "reset_locking")) ||
+ !(_end_fn = dlsym(_locking_lib, "locking_end"))) {
+ log_error("Shared library %s does not contain locking "
+ "functions", libname);
+ dlclose(_locking_lib);
+ _locking_lib = NULL;
+ return 0;
+ }
+
+ if (!(_lock_query_fn = dlsym(_locking_lib, "query_resource")))
+ log_warn("WARNING: %s: _query_resource() missing: "
+ "Using inferior activation method.", libname);
+
+ log_verbose("Loaded external locking library %s", libname);
+ return _init_fn(2, cmd->cft, &locking->flags);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "locking.h"
+#include "locking_types.h"
+#include "activate.h"
+#include "config.h"
+#include "defaults.h"
+#include "lvm-file.h"
+#include "lvm-string.h"
+#include "lvmcache.h"
+
+#include <limits.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <signal.h>
+
+struct lock_list {
+ struct dm_list list;
+ int lf;
+ char *res;
+};
+
+static struct dm_list _lock_list;
+static char _lock_dir[NAME_LEN];
+static int _prioritise_write_locks;
+
+static sig_t _oldhandler;
+static sigset_t _fullsigset, _intsigset;
+static volatile sig_atomic_t _handler_installed;
+
+static void _undo_flock(const char *file, int fd)
+{
+ struct stat buf1, buf2;
+
+ log_debug("_undo_flock %s", file);
+ if (!flock(fd, LOCK_NB | LOCK_EX) &&
+ !stat(file, &buf1) &&
+ !fstat(fd, &buf2) &&
+ is_same_inode(buf1, buf2))
+ if (unlink(file))
+ log_sys_error("unlink", file);
+
+ if (close(fd) < 0)
+ log_sys_error("close", file);
+}
+
+static int _release_lock(const char *file, int unlock)
+{
+ struct lock_list *ll;
+ struct dm_list *llh, *llt;
+
+ dm_list_iterate_safe(llh, llt, &_lock_list) {
+ ll = dm_list_item(llh, struct lock_list);
+
+ if (!file || !strcmp(ll->res, file)) {
+ dm_list_del(llh);
+ if (unlock) {
+ log_very_verbose("Unlocking %s", ll->res);
+ if (flock(ll->lf, LOCK_NB | LOCK_UN))
+ log_sys_error("flock", ll->res);
+ }
+
+ _undo_flock(ll->res, ll->lf);
+
+ dm_free(ll->res);
+ dm_free(llh);
+
+ if (file)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void _fin_file_locking(void)
+{
+ _release_lock(NULL, 1);
+}
+
+static void _reset_file_locking(void)
+{
+ _release_lock(NULL, 0);
+}
+
+static void _remove_ctrl_c_handler(void)
+{
+ siginterrupt(SIGINT, 0);
+ if (!_handler_installed)
+ return;
+
+ _handler_installed = 0;
+
+ sigprocmask(SIG_SETMASK, &_fullsigset, NULL);
+ if (signal(SIGINT, _oldhandler) == SIG_ERR)
+ log_sys_error("signal", "_remove_ctrl_c_handler");
+}
+
+static void _trap_ctrl_c(int sig __attribute__((unused)))
+{
+ _remove_ctrl_c_handler();
+ log_error("CTRL-c detected: giving up waiting for lock");
+}
+
+static void _install_ctrl_c_handler(void)
+{
+ _handler_installed = 1;
+
+ if ((_oldhandler = signal(SIGINT, _trap_ctrl_c)) == SIG_ERR) {
+ _handler_installed = 0;
+ return;
+ }
+
+ sigprocmask(SIG_SETMASK, &_intsigset, NULL);
+ siginterrupt(SIGINT, 1);
+}
+
+static int _do_flock(const char *file, int *fd, int operation, uint32_t nonblock)
+{
+ int r = 1;
+ int old_errno;
+ struct stat buf1, buf2;
+
+ log_debug("_do_flock %s %c%c",
+ file, operation == LOCK_EX ? 'W' : 'R', nonblock ? ' ' : 'B');
+ do {
+ if ((*fd > -1) && close(*fd))
+ log_sys_error("close", file);
+
+ if ((*fd = open(file, O_CREAT | O_APPEND | O_RDWR, 0777)) < 0) {
+ log_sys_error("open", file);
+ return 0;
+ }
+
+ if (nonblock)
+ operation |= LOCK_NB;
+ else
+ _install_ctrl_c_handler();
+
+ r = flock(*fd, operation);
+ old_errno = errno;
+ if (!nonblock)
+ _remove_ctrl_c_handler();
+
+ if (r) {
+ errno = old_errno;
+ log_sys_error("flock", file);
+ close(*fd);
+ return 0;
+ }
+
+ if (!stat(file, &buf1) && !fstat(*fd, &buf2) &&
+ is_same_inode(buf1, buf2))
+ return 1;
+ } while (!nonblock);
+
+ return_0;
+}
+
+#define AUX_LOCK_SUFFIX ":aux"
+
+static int _do_write_priority_flock(const char *file, int *fd, int operation, uint32_t nonblock)
+{
+ int r, fd_aux = -1;
+ char *file_aux = alloca(strlen(file) + sizeof(AUX_LOCK_SUFFIX));
+
+ strcpy(file_aux, file);
+ strcat(file_aux, AUX_LOCK_SUFFIX);
+
+ if ((r = _do_flock(file_aux, &fd_aux, LOCK_EX, 0))) {
+ if (operation == LOCK_EX) {
+ r = _do_flock(file, fd, operation, nonblock);
+ _undo_flock(file_aux, fd_aux);
+ } else {
+ _undo_flock(file_aux, fd_aux);
+ r = _do_flock(file, fd, operation, nonblock);
+ }
+ }
+
+ return r;
+}
+
+static int _lock_file(const char *file, uint32_t flags)
+{
+ int operation;
+ uint32_t nonblock = flags & LCK_NONBLOCK;
+ int r;
+
+ struct lock_list *ll;
+ char state;
+
+ switch (flags & LCK_TYPE_MASK) {
+ case LCK_READ:
+ operation = LOCK_SH;
+ state = 'R';
+ break;
+ case LCK_WRITE:
+ operation = LOCK_EX;
+ state = 'W';
+ break;
+ case LCK_UNLOCK:
+ return _release_lock(file, 1);
+ default:
+ log_error("Unrecognised lock type: %d", flags & LCK_TYPE_MASK);
+ return 0;
+ }
+
+ if (!(ll = dm_malloc(sizeof(struct lock_list))))
+ return_0;
+
+ if (!(ll->res = dm_strdup(file))) {
+ dm_free(ll);
+ return_0;
+ }
+
+ ll->lf = -1;
+
+ log_very_verbose("Locking %s %c%c", ll->res, state,
+ nonblock ? ' ' : 'B');
+
+ (void) dm_prepare_selinux_context(file, S_IFREG);
+ if (_prioritise_write_locks)
+ r = _do_write_priority_flock(file, &ll->lf, operation, nonblock);
+ else
+ r = _do_flock(file, &ll->lf, operation, nonblock);
+ (void) dm_prepare_selinux_context(NULL, 0);
+
+ if (r)
+ dm_list_add(&_lock_list, &ll->list);
+ else {
+ dm_free(ll->res);
+ dm_free(ll);
+ stack;
+ }
+
+ return r;
+}
+
+static int _file_lock_resource(struct cmd_context *cmd, const char *resource,
+ uint32_t flags)
+{
+ char lockfile[PATH_MAX];
+ unsigned origin_only = (flags & LCK_ORIGIN_ONLY) ? 1 : 0;
+
+ switch (flags & LCK_SCOPE_MASK) {
+ case LCK_VG:
+ /* Skip cache refresh for VG_GLOBAL - the caller handles it */
+ if (strcmp(resource, VG_GLOBAL))
+ lvmcache_drop_metadata(resource, 0);
+
+ /* LCK_CACHE does not require a real lock */
+ if (flags & LCK_CACHE)
+ break;
+
+ if (is_orphan_vg(resource) || is_global_vg(resource))
+ dm_snprintf(lockfile, sizeof(lockfile),
+ "%s/P_%s", _lock_dir, resource + 1);
+ else
+ dm_snprintf(lockfile, sizeof(lockfile),
+ "%s/V_%s", _lock_dir, resource);
+
+ if (!_lock_file(lockfile, flags))
+ return_0;
+ break;
+ case LCK_LV:
+ switch (flags & LCK_TYPE_MASK) {
+ case LCK_UNLOCK:
+ log_very_verbose("Unlocking LV %s%s", resource, origin_only ? " without snapshots" : "");
+ if (!lv_resume_if_active(cmd, resource, origin_only))
+ return 0;
+ break;
+ case LCK_NULL:
+ log_very_verbose("Locking LV %s (NL)", resource);
+ if (!lv_deactivate(cmd, resource))
+ return 0;
+ break;
+ case LCK_READ:
+ log_very_verbose("Locking LV %s (R)", resource);
+ if (!lv_activate_with_filter(cmd, resource, 0))
+ return 0;
+ break;
+ case LCK_PREAD:
+ log_very_verbose("Locking LV %s (PR) - ignored", resource);
+ break;
+ case LCK_WRITE:
+ log_very_verbose("Locking LV %s (W)%s", resource, origin_only ? " without snapshots" : "");
+ if (!lv_suspend_if_active(cmd, resource, origin_only))
+ return 0;
+ break;
+ case LCK_EXCL:
+ log_very_verbose("Locking LV %s (EX)", resource);
+ if (!lv_activate_with_filter(cmd, resource, 1))
+ return 0;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ log_error("Unrecognised lock scope: %d",
+ flags & LCK_SCOPE_MASK);
+ return 0;
+ }
+
+ return 1;
+}
+
+int init_file_locking(struct locking_type *locking, struct cmd_context *cmd)
+{
+ locking->lock_resource = _file_lock_resource;
+ locking->reset_locking = _reset_file_locking;
+ locking->fin_locking = _fin_file_locking;
+ locking->flags = 0;
+ int r;
+
+ /* Get lockfile directory from config file */
+ strncpy(_lock_dir, find_config_tree_str(cmd, "global/locking_dir",
+ DEFAULT_LOCK_DIR),
+ sizeof(_lock_dir));
+
+ _prioritise_write_locks =
+ find_config_tree_bool(cmd, "global/prioritise_write_locks",
+ DEFAULT_PRIORITISE_WRITE_LOCKS);
+
+ (void) dm_prepare_selinux_context(_lock_dir, S_IFDIR);
+ r = dm_create_dir(_lock_dir);
+ (void) dm_prepare_selinux_context(NULL, 0);
+
+ if (!r)
+ return 0;
+
+ /* Trap a read-only file system */
+ if ((access(_lock_dir, R_OK | W_OK | X_OK) == -1) && (errno == EROFS))
+ return 0;
+
+ dm_list_init(&_lock_list);
+
+ if (sigfillset(&_intsigset) || sigfillset(&_fullsigset)) {
+ log_sys_error("sigfillset", "init_file_locking");
+ return 0;
+ }
+
+ if (sigdelset(&_intsigset, SIGINT)) {
+ log_sys_error("sigdelset", "init_file_locking");
+ return 0;
+ }
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "locking.h"
+#include "locking_types.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "toolcontext.h"
+#include "memlock.h"
+#include "defaults.h"
+#include "lvmcache.h"
+
+#include <assert.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <unistd.h>
+
+static struct locking_type _locking;
+static sigset_t _oldset;
+
+static int _vg_lock_count = 0; /* Number of locks held */
+static int _vg_write_lock_held = 0; /* VG write lock held? */
+static int _signals_blocked = 0;
+static int _blocking_supported = 0;
+
+static volatile sig_atomic_t _sigint_caught = 0;
+static volatile sig_atomic_t _handler_installed;
+static struct sigaction _oldhandler;
+static int _oldmasked;
+
+typedef enum {
+ LV_NOOP,
+ LV_SUSPEND,
+ LV_RESUME
+} lv_operation_t;
+
+static void _catch_sigint(int unused __attribute__((unused)))
+{
+ _sigint_caught = 1;
+}
+
+int sigint_caught(void) {
+ return _sigint_caught;
+}
+
+void sigint_clear(void)
+{
+ _sigint_caught = 0;
+}
+
+/*
+ * Temporarily allow keyboard interrupts to be intercepted and noted;
+ * saves interrupt handler state for sigint_restore(). Users should
+ * use the sigint_caught() predicate to check whether interrupt was
+ * requested and act appropriately. Interrupt flags are never
+ * cleared automatically by this code, but the tools clear the flag
+ * before running each command in lvm_run_command(). All other places
+ * where the flag needs to be cleared need to call sigint_clear().
+ */
+
+void sigint_allow(void)
+{
+ struct sigaction handler;
+ sigset_t sigs;
+
+ /*
+ * Do not overwrite the backed-up handler data -
+ * just increase nesting count.
+ */
+ if (_handler_installed) {
+ _handler_installed++;
+ return;
+ }
+
+ /* Grab old sigaction for SIGINT: shall not fail. */
+ sigaction(SIGINT, NULL, &handler);
+ handler.sa_flags &= ~SA_RESTART; /* Clear restart flag */
+ handler.sa_handler = _catch_sigint;
+
+ _handler_installed = 1;
+
+ /* Override the signal handler: shall not fail. */
+ sigaction(SIGINT, &handler, &_oldhandler);
+
+ /* Unmask SIGINT. Remember to mask it again on restore. */
+ sigprocmask(0, NULL, &sigs);
+ if ((_oldmasked = sigismember(&sigs, SIGINT))) {
+ sigdelset(&sigs, SIGINT);
+ sigprocmask(SIG_SETMASK, &sigs, NULL);
+ }
+}
+
+void sigint_restore(void)
+{
+ if (!_handler_installed)
+ return;
+
+ if (_handler_installed > 1) {
+ _handler_installed--;
+ return;
+ }
+
+ /* Nesting count went down to 0. */
+ _handler_installed = 0;
+
+ if (_oldmasked) {
+ sigset_t sigs;
+ sigprocmask(0, NULL, &sigs);
+ sigaddset(&sigs, SIGINT);
+ sigprocmask(SIG_SETMASK, &sigs, NULL);
+ }
+
+ sigaction(SIGINT, &_oldhandler, NULL);
+}
+
+static void _block_signals(uint32_t flags __attribute__((unused)))
+{
+ sigset_t set;
+
+ if (_signals_blocked)
+ return;
+
+ if (sigfillset(&set)) {
+ log_sys_error("sigfillset", "_block_signals");
+ return;
+ }
+
+ if (sigprocmask(SIG_SETMASK, &set, &_oldset)) {
+ log_sys_error("sigprocmask", "_block_signals");
+ return;
+ }
+
+ _signals_blocked = 1;
+}
+
+static void _unblock_signals(void)
+{
+ /* Don't unblock signals while any locks are held */
+ if (!_signals_blocked || _vg_lock_count)
+ return;
+
+ if (sigprocmask(SIG_SETMASK, &_oldset, NULL)) {
+ log_sys_error("sigprocmask", "_block_signals");
+ return;
+ }
+
+ _signals_blocked = 0;
+}
+
+static void _lock_memory(struct cmd_context *cmd, lv_operation_t lv_op)
+{
+ if (!(_locking.flags & LCK_PRE_MEMLOCK))
+ return;
+
+ if (lv_op == LV_SUSPEND)
+ memlock_inc(cmd);
+}
+
+static void _unlock_memory(struct cmd_context *cmd, lv_operation_t lv_op)
+{
+ if (!(_locking.flags & LCK_PRE_MEMLOCK))
+ return;
+
+ if (lv_op == LV_RESUME)
+ memlock_dec(cmd);
+}
+
+void reset_locking(void)
+{
+ int was_locked = _vg_lock_count;
+
+ _vg_lock_count = 0;
+ _vg_write_lock_held = 0;
+
+ if (_locking.reset_locking)
+ _locking.reset_locking();
+
+ if (was_locked)
+ _unblock_signals();
+}
+
+static void _update_vg_lock_count(const char *resource, uint32_t flags)
+{
+ /* Ignore locks not associated with updating VG metadata */
+ if ((flags & LCK_SCOPE_MASK) != LCK_VG ||
+ (flags & LCK_CACHE) ||
+ !strcmp(resource, VG_GLOBAL))
+ return;
+
+ if ((flags & LCK_TYPE_MASK) == LCK_UNLOCK)
+ _vg_lock_count--;
+ else
+ _vg_lock_count++;
+
+ /* We don't bother to reset this until all VG locks are dropped */
+ if ((flags & LCK_TYPE_MASK) == LCK_WRITE)
+ _vg_write_lock_held = 1;
+ else if (!_vg_lock_count)
+ _vg_write_lock_held = 0;
+}
+
+/*
+ * Select a locking type
+ * type: locking type; if < 0, then read config tree value
+ */
+int init_locking(int type, struct cmd_context *cmd, int suppress_messages)
+{
+ if (ignorelockingfailure() && getenv("LVM_SUPPRESS_LOCKING_FAILURE_MESSAGES"))
+ suppress_messages = 1;
+
+ if (type < 0)
+ type = find_config_tree_int(cmd, "global/locking_type", 1);
+
+ _blocking_supported = find_config_tree_int(cmd,
+ "global/wait_for_locks", DEFAULT_WAIT_FOR_LOCKS);
+
+ switch (type) {
+ case 0:
+ init_no_locking(&_locking, cmd);
+ log_warn("WARNING: Locking disabled. Be careful! "
+ "This could corrupt your metadata.");
+ return 1;
+
+ case 1:
+ log_very_verbose("%sFile-based locking selected.",
+ _blocking_supported ? "" : "Non-blocking ");
+
+ if (!init_file_locking(&_locking, cmd)) {
+ log_error_suppress(suppress_messages,
+ "File-based locking initialisation failed.");
+ break;
+ }
+ return 1;
+
+#ifdef HAVE_LIBDL
+ case 2:
+ if (!is_static()) {
+ log_very_verbose("External locking selected.");
+ if (init_external_locking(&_locking, cmd))
+ return 1;
+ }
+ if (!find_config_tree_int(cmd, "locking/fallback_to_clustered_locking",
+ find_config_tree_int(cmd, "global/fallback_to_clustered_locking",
+ DEFAULT_FALLBACK_TO_CLUSTERED_LOCKING))) {
+ log_error("External locking initialisation failed.");
+ break;
+ }
+#endif
+
+#ifdef CLUSTER_LOCKING_INTERNAL
+ log_very_verbose("Falling back to internal clustered locking.");
+ /* Fall through */
+
+ case 3:
+ log_very_verbose("Cluster locking selected.");
+ if (!init_cluster_locking(&_locking, cmd)) {
+ log_error_suppress(suppress_messages,
+ "Internal cluster locking initialisation failed.");
+ break;
+ }
+ return 1;
+#endif
+
+ case 4:
+ log_verbose("Read-only locking selected. "
+ "Only read operations permitted.");
+ if (!init_readonly_locking(&_locking, cmd))
+ break;
+ return 1;
+
+ default:
+ log_error("Unknown locking type requested.");
+ return 0;
+ }
+
+ if ((type == 2 || type == 3) &&
+ find_config_tree_int(cmd, "locking/fallback_to_local_locking",
+ find_config_tree_int(cmd, "global/fallback_to_local_locking",
+ DEFAULT_FALLBACK_TO_LOCAL_LOCKING))) {
+ log_warn_suppress(suppress_messages, "WARNING: Falling back to local file-based locking.");
+ log_warn_suppress(suppress_messages,
+ "Volume Groups with the clustered attribute will "
+ "be inaccessible.");
+ if (init_file_locking(&_locking, cmd))
+ return 1;
+ else
+ log_error_suppress(suppress_messages,
+ "File-based locking initialisation failed.");
+ }
+
+ if (!ignorelockingfailure())
+ return 0;
+
+ log_verbose("Locking disabled - only read operations permitted.");
+ init_readonly_locking(&_locking, cmd);
+
+ return 1;
+}
+
+void fin_locking(void)
+{
+ _locking.fin_locking();
+}
+
+/*
+ * Does the LVM1 driver know of this VG name?
+ */
+int check_lvm1_vg_inactive(struct cmd_context *cmd, const char *vgname)
+{
+ struct stat info;
+ char path[PATH_MAX];
+
+ /* We'll allow operations on orphans */
+ if (is_orphan_vg(vgname) || is_global_vg(vgname))
+ return 1;
+
+ /* LVM1 is only present in 2.4 kernels. */
+ if (strncmp(cmd->kernel_vsn, "2.4.", 4))
+ return 1;
+
+ if (dm_snprintf(path, sizeof(path), "%s/lvm/VGs/%s", cmd->proc_dir,
+ vgname) < 0) {
+ log_error("LVM1 proc VG pathname too long for %s", vgname);
+ return 0;
+ }
+
+ if (stat(path, &info) == 0) {
+ log_error("%s exists: Is the original LVM driver using "
+ "this volume group?", path);
+ return 0;
+ } else if (errno != ENOENT && errno != ENOTDIR) {
+ log_sys_error("stat", path);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * VG locking is by VG name.
+ * FIXME This should become VG uuid.
+ */
+static int _lock_vol(struct cmd_context *cmd, const char *resource,
+ uint32_t flags, lv_operation_t lv_op)
+{
+ int ret = 0;
+
+ _block_signals(flags);
+ _lock_memory(cmd, lv_op);
+
+ assert(resource);
+
+ if (!*resource) {
+ log_error(INTERNAL_ERROR "Use of P_orphans is deprecated.");
+ return 0;
+ }
+
+ if ((is_orphan_vg(resource) || is_global_vg(resource)) && (flags & LCK_CACHE)) {
+ log_error(INTERNAL_ERROR "P_%s referenced", resource);
+ return 0;
+ }
+
+ if (cmd->metadata_read_only &&
+ ((flags & LCK_TYPE_MASK) == LCK_WRITE) &&
+ strcmp(resource, VG_GLOBAL)) {
+ log_error("Operation prohibited while global/metadata_read_only is set.");
+ return 0;
+ }
+
+ if ((ret = _locking.lock_resource(cmd, resource, flags))) {
+ if ((flags & LCK_SCOPE_MASK) == LCK_VG &&
+ !(flags & LCK_CACHE)) {
+ if ((flags & LCK_TYPE_MASK) == LCK_UNLOCK)
+ lvmcache_unlock_vgname(resource);
+ else
+ lvmcache_lock_vgname(resource, (flags & LCK_TYPE_MASK)
+ == LCK_READ);
+ dev_reset_error_count(cmd);
+ }
+
+ _update_vg_lock_count(resource, flags);
+ } else
+ stack;
+
+ _unlock_memory(cmd, lv_op);
+ _unblock_signals();
+
+ return ret;
+}
+
+int lock_vol(struct cmd_context *cmd, const char *vol, uint32_t flags)
+{
+ char resource[258] __attribute__((aligned(8)));
+ lv_operation_t lv_op;
+
+ switch (flags & (LCK_SCOPE_MASK | LCK_TYPE_MASK)) {
+ case LCK_LV_SUSPEND:
+ lv_op = LV_SUSPEND;
+ break;
+ case LCK_LV_RESUME:
+ lv_op = LV_RESUME;
+ break;
+ default: lv_op = LV_NOOP;
+ }
+
+
+ if (flags == LCK_NONE) {
+ log_debug(INTERNAL_ERROR "%s: LCK_NONE lock requested", vol);
+ return 1;
+ }
+
+ switch (flags & LCK_SCOPE_MASK) {
+ case LCK_VG:
+ if (!_blocking_supported)
+ flags |= LCK_NONBLOCK;
+
+ /* Global VG_ORPHANS lock covers all orphan formats. */
+ if (is_orphan_vg(vol))
+ vol = VG_ORPHANS;
+ /* VG locks alphabetical, ORPHAN lock last */
+ if (((flags & LCK_TYPE_MASK) != LCK_UNLOCK) &&
+ !(flags & LCK_CACHE) &&
+ !lvmcache_verify_lock_order(vol))
+ return 0;
+
+ /* Lock VG to change on-disk metadata. */
+ /* If LVM1 driver knows about the VG, it can't be accessed. */
+ if (!check_lvm1_vg_inactive(cmd, vol))
+ return 0;
+ break;
+ case LCK_LV:
+ /* All LV locks are non-blocking. */
+ flags |= LCK_NONBLOCK;
+ break;
+ default:
+ log_error("Unrecognised lock scope: %d",
+ flags & LCK_SCOPE_MASK);
+ return 0;
+ }
+
+ strncpy(resource, vol, sizeof(resource));
+
+ if (!_lock_vol(cmd, resource, flags, lv_op))
+ return 0;
+
+ /*
+ * If a real lock was acquired (i.e. not LCK_CACHE),
+ * perform an immediate unlock unless LCK_HOLD was requested.
+ */
+ if (!(flags & LCK_CACHE) && !(flags & LCK_HOLD) &&
+ ((flags & LCK_TYPE_MASK) != LCK_UNLOCK)) {
+ if (!_lock_vol(cmd, resource,
+ (flags & ~LCK_TYPE_MASK) | LCK_UNLOCK, lv_op))
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Unlock list of LVs */
+int resume_lvs(struct cmd_context *cmd, struct dm_list *lvs)
+{
+ struct lv_list *lvl;
+ int r = 1;
+
+ dm_list_iterate_items(lvl, lvs)
+ if (!resume_lv(cmd, lvl->lv)) {
+ r = 0;
+ stack;
+ }
+
+ return r;
+}
+
+/* Lock a list of LVs */
+int suspend_lvs(struct cmd_context *cmd, struct dm_list *lvs)
+{
+ struct dm_list *lvh;
+ struct lv_list *lvl;
+
+ dm_list_iterate_items(lvl, lvs) {
+ if (!suspend_lv(cmd, lvl->lv)) {
+ log_error("Failed to suspend %s", lvl->lv->name);
+ dm_list_uniterate(lvh, lvs, &lvl->list) {
+ lvl = dm_list_item(lvh, struct lv_list);
+ if (!resume_lv(cmd, lvl->lv))
+ stack;
+ }
+
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Lock a list of LVs */
+int activate_lvs(struct cmd_context *cmd, struct dm_list *lvs, unsigned exclusive)
+{
+ struct dm_list *lvh;
+ struct lv_list *lvl;
+
+ dm_list_iterate_items(lvl, lvs) {
+ if (!exclusive) {
+ if (!activate_lv(cmd, lvl->lv)) {
+ log_error("Failed to activate %s", lvl->lv->name);
+ return 0;
+ }
+ } else if (!activate_lv_excl(cmd, lvl->lv)) {
+ log_error("Failed to activate %s", lvl->lv->name);
+ dm_list_uniterate(lvh, lvs, &lvl->list) {
+ lvl = dm_list_item(lvh, struct lv_list);
+ if (!activate_lv(cmd, lvl->lv))
+ stack;
+ }
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int vg_write_lock_held(void)
+{
+ return _vg_write_lock_held;
+}
+
+int locking_is_clustered(void)
+{
+ return (_locking.flags & LCK_CLUSTERED) ? 1 : 0;
+}
+
+int remote_lock_held(const char *vol)
+{
+ int mode = LCK_NULL;
+
+ if (!locking_is_clustered())
+ return 0;
+
+ if (!_locking.query_resource)
+ return -1;
+
+ /*
+ * If an error occured, expect that volume is active
+ */
+ if (!_locking.query_resource(vol, &mode)) {
+ stack;
+ return 1;
+ }
+
+ return mode == LCK_NULL ? 0 : 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_LOCKING_H
+#define _LVM_LOCKING_H
+
+#include "uuid.h"
+#include "config.h"
+
+int init_locking(int type, struct cmd_context *cmd, int suppress_messages);
+void fin_locking(void);
+void reset_locking(void);
+int vg_write_lock_held(void);
+int locking_is_clustered(void);
+
+int remote_lock_held(const char *vol);
+
+/*
+ * LCK_VG:
+ * Lock/unlock on-disk volume group data.
+ * Use VG_ORPHANS to lock all orphan PVs.
+ * Use VG_GLOBAL as a global lock and to wipe the internal cache.
+ * char *vol holds volume group name.
+ * Set LCK_CACHE flag when manipulating 'vol' metadata in the internal cache.
+ * (Like commit, revert or invalidate metadata.)
+ * If more than one lock needs to be held simultaneously, they must be
+ * acquired in alphabetical order of 'vol' (to avoid deadlocks), with
+ * VG_ORPHANS last.
+ *
+ * LCK_LV:
+ * Lock/unlock an individual logical volume
+ * char *vol holds lvid
+ */
+int lock_vol(struct cmd_context *cmd, const char *vol, uint32_t flags);
+
+/*
+ * Internal locking representation.
+ * LCK_VG: Uses prefix V_ unless the vol begins with # (i.e. #global or #orphans)
+ * or the LCK_CACHE flag is set when it uses the prefix P_.
+ * If LCK_CACHE is set, we do not take out a real lock.
+ * NB In clustered situations, LCK_CACHE is not propagated directly to remote nodes.
+ * (It can be deduced from lock name.)
+ */
+
+/*
+ * Does the LVM1 driver have this VG active?
+ */
+int check_lvm1_vg_inactive(struct cmd_context *cmd, const char *vgname);
+
+/*
+ * Lock type - these numbers are the same as VMS and the IBM DLM
+ */
+#define LCK_TYPE_MASK 0x00000007U
+
+#define LCK_NULL 0x00000000U /* LCK$_NLMODE */
+#define LCK_READ 0x00000001U /* LCK$_CRMODE */
+ /* LCK$_CWMODE */
+#define LCK_PREAD 0x00000003U /* LCK$_PRMODE */
+#define LCK_WRITE 0x00000004U /* LCK$_PWMODE */
+#define LCK_EXCL 0x00000005U /* LCK$_EXMODE */
+#define LCK_UNLOCK 0x00000006U /* This is ours */
+
+/*
+ * Lock flags - these numbers are the same as DLM
+ */
+#define LCKF_NOQUEUE 0x00000001U /* LKF$_NOQUEUE */
+#define LCKF_CONVERT 0x00000004U /* LKF$_CONVERT */
+
+/*
+ * Lock scope
+ */
+#define LCK_SCOPE_MASK 0x00000008U
+#define LCK_VG 0x00000000U
+#define LCK_LV 0x00000008U
+
+/*
+ * Lock bits
+ */
+#define LCK_NONBLOCK 0x00000010U /* Don't block waiting for lock? */
+#define LCK_HOLD 0x00000020U /* Hold lock when lock_vol returns? */
+#define LCK_LOCAL 0x00000040U /* Don't propagate to other nodes */
+#define LCK_CLUSTER_VG 0x00000080U /* VG is clustered */
+#define LCK_CACHE 0x00000100U /* Operation on cache only using P_ lock */
+#define LCK_ORIGIN_ONLY 0x00000200U /* Operation should bypass any snapshots */
+
+/*
+ * Additional lock bits for cluster communication via args[1]
+ */
+#define LCK_PARTIAL_MODE 0x01 /* Partial activation? */
+#define LCK_MIRROR_NOSYNC_MODE 0x02 /* Mirrors don't require sync */
+#define LCK_DMEVENTD_MONITOR_MODE 0x04 /* Register with dmeventd */
+#define LCK_CONVERT 0x08 /* Convert existing lock */
+#define LCK_ORIGIN_ONLY_MODE 0x20 /* Same as above */
+
+/*
+ * Special cases of VG locks.
+ */
+#define VG_ORPHANS "#orphans"
+#define VG_GLOBAL "#global"
+
+/*
+ * Common combinations
+ */
+#define LCK_NONE (LCK_VG | LCK_NULL)
+
+#define LCK_VG_READ (LCK_VG | LCK_READ | LCK_HOLD)
+#define LCK_VG_WRITE (LCK_VG | LCK_WRITE | LCK_HOLD)
+#define LCK_VG_UNLOCK (LCK_VG | LCK_UNLOCK)
+#define LCK_VG_DROP_CACHE (LCK_VG | LCK_WRITE | LCK_CACHE)
+
+/* FIXME: LCK_HOLD abused here */
+#define LCK_VG_COMMIT (LCK_VG | LCK_WRITE | LCK_CACHE | LCK_HOLD)
+#define LCK_VG_REVERT (LCK_VG | LCK_READ | LCK_CACHE | LCK_HOLD)
+
+#define LCK_VG_BACKUP (LCK_VG | LCK_CACHE)
+
+#define LCK_LV_EXCLUSIVE (LCK_LV | LCK_EXCL)
+#define LCK_LV_SUSPEND (LCK_LV | LCK_WRITE)
+#define LCK_LV_RESUME (LCK_LV | LCK_UNLOCK)
+#define LCK_LV_ACTIVATE (LCK_LV | LCK_READ)
+#define LCK_LV_DEACTIVATE (LCK_LV | LCK_NULL)
+
+#define LCK_MASK (LCK_TYPE_MASK | LCK_SCOPE_MASK)
+
+#define LCK_LV_CLUSTERED(lv) \
+ (vg_is_clustered((lv)->vg) ? LCK_CLUSTER_VG : 0)
+
+#define lock_lv_vol(cmd, lv, flags) \
+ (find_replicator_vgs((lv)) ? \
+ lock_vol(cmd, (lv)->lvid.s, flags | LCK_LV_CLUSTERED(lv)) : \
+ 0)
+
+#define unlock_vg(cmd, vol) lock_vol(cmd, vol, LCK_VG_UNLOCK)
+#define unlock_and_free_vg(cmd, vg, vol) \
+ do { \
+ unlock_vg(cmd, vol); \
+ free_vg(vg); \
+ } while (0)
+
+#define resume_lv(cmd, lv) lock_lv_vol(cmd, lv, LCK_LV_RESUME)
+#define resume_lv_origin(cmd, lv) lock_lv_vol(cmd, lv, LCK_LV_RESUME | LCK_ORIGIN_ONLY)
+#define suspend_lv(cmd, lv) lock_lv_vol(cmd, lv, LCK_LV_SUSPEND | LCK_HOLD)
+#define suspend_lv_origin(cmd, lv) lock_lv_vol(cmd, lv, LCK_LV_SUSPEND | LCK_HOLD | LCK_ORIGIN_ONLY)
+#define deactivate_lv(cmd, lv) lock_lv_vol(cmd, lv, LCK_LV_DEACTIVATE)
+#define activate_lv(cmd, lv) lock_lv_vol(cmd, lv, LCK_LV_ACTIVATE | LCK_HOLD)
+#define activate_lv_excl(cmd, lv) \
+ lock_lv_vol(cmd, lv, LCK_LV_EXCLUSIVE | LCK_HOLD)
+#define activate_lv_local(cmd, lv) \
+ lock_lv_vol(cmd, lv, LCK_LV_ACTIVATE | LCK_HOLD | LCK_LOCAL)
+#define deactivate_lv_local(cmd, lv) \
+ lock_lv_vol(cmd, lv, LCK_LV_DEACTIVATE | LCK_LOCAL)
+#define drop_cached_metadata(vg) \
+ lock_vol((vg)->cmd, (vg)->name, LCK_VG_DROP_CACHE)
+#define remote_commit_cached_metadata(vg) \
+ lock_vol((vg)->cmd, (vg)->name, LCK_VG_COMMIT)
+#define remote_revert_cached_metadata(vg) \
+ lock_vol((vg)->cmd, (vg)->name, LCK_VG_REVERT)
+#define remote_backup_metadata(vg) \
+ lock_vol((vg)->cmd, (vg)->name, LCK_VG_BACKUP)
+
+/* Process list of LVs */
+int suspend_lvs(struct cmd_context *cmd, struct dm_list *lvs);
+int resume_lvs(struct cmd_context *cmd, struct dm_list *lvs);
+int activate_lvs(struct cmd_context *cmd, struct dm_list *lvs, unsigned exclusive);
+
+/* Interrupt handling */
+void sigint_clear(void);
+void sigint_allow(void);
+void sigint_restore(void);
+int sigint_caught(void);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "metadata.h"
+#include "config.h"
+
+typedef int (*lock_resource_fn) (struct cmd_context * cmd, const char *resource,
+ uint32_t flags);
+typedef int (*query_resource_fn) (const char *resource, int *mode);
+
+typedef void (*fin_lock_fn) (void);
+typedef void (*reset_lock_fn) (void);
+
+#define LCK_PRE_MEMLOCK 0x00000001 /* Is memlock() needed before calls? */
+#define LCK_CLUSTERED 0x00000002
+
+struct locking_type {
+ uint32_t flags;
+ lock_resource_fn lock_resource;
+ query_resource_fn query_resource;
+
+ reset_lock_fn reset_locking;
+ fin_lock_fn fin_locking;
+};
+
+/*
+ * Locking types
+ */
+int init_no_locking(struct locking_type *locking, struct cmd_context *cmd);
+
+int init_readonly_locking(struct locking_type *locking, struct cmd_context *cmd);
+
+int init_file_locking(struct locking_type *locking, struct cmd_context *cmd);
+
+int init_external_locking(struct locking_type *locking, struct cmd_context *cmd);
+
+int init_cluster_locking(struct locking_type *locking, struct cmd_context *cmd);
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "locking.h"
+#include "locking_types.h"
+#include "lvm-string.h"
+#include "activate.h"
+
+#include <signal.h>
+
+/*
+ * No locking
+ */
+
+static void _no_fin_locking(void)
+{
+}
+
+static void _no_reset_locking(void)
+{
+}
+
+static int _no_lock_resource(struct cmd_context *cmd, const char *resource,
+ uint32_t flags)
+{
+ switch (flags & LCK_SCOPE_MASK) {
+ case LCK_VG:
+ break;
+ case LCK_LV:
+ switch (flags & LCK_TYPE_MASK) {
+ case LCK_NULL:
+ return lv_deactivate(cmd, resource);
+ case LCK_UNLOCK:
+ return lv_resume_if_active(cmd, resource, (flags & LCK_ORIGIN_ONLY) ? 1: 0);
+ case LCK_READ:
+ return lv_activate_with_filter(cmd, resource, 0);
+ case LCK_WRITE:
+ return lv_suspend_if_active(cmd, resource, (flags & LCK_ORIGIN_ONLY) ? 1 : 0);
+ case LCK_EXCL:
+ return lv_activate_with_filter(cmd, resource, 1);
+ default:
+ break;
+ }
+ break;
+ default:
+ log_error("Unrecognised lock scope: %d",
+ flags & LCK_SCOPE_MASK);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _readonly_lock_resource(struct cmd_context *cmd,
+ const char *resource,
+ uint32_t flags)
+{
+ if ((flags & LCK_TYPE_MASK) == LCK_WRITE &&
+ (flags & LCK_SCOPE_MASK) == LCK_VG &&
+ !(flags & LCK_CACHE) &&
+ strcmp(resource, VG_GLOBAL)) {
+ log_error("Write locks are prohibited with read-only locking.");
+ return 0;
+ }
+
+ return _no_lock_resource(cmd, resource, flags);
+}
+
+int init_no_locking(struct locking_type *locking, struct cmd_context *cmd __attribute__((unused)))
+{
+ locking->lock_resource = _no_lock_resource;
+ locking->reset_locking = _no_reset_locking;
+ locking->fin_locking = _no_fin_locking;
+ locking->flags = LCK_CLUSTERED;
+
+ return 1;
+}
+
+int init_readonly_locking(struct locking_type *locking, struct cmd_context *cmd __attribute__((unused)))
+{
+ locking->lock_resource = _readonly_lock_resource;
+ locking->reset_locking = _no_reset_locking;
+ locking->fin_locking = _no_fin_locking;
+ locking->flags = 0;
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "device.h"
+#include "memlock.h"
+#include "lvm-string.h"
+#include "lvm-file.h"
+#include "defaults.h"
+#include "config.h"
+
+#include <stdarg.h>
+#include <syslog.h>
+
+static FILE *_log_file;
+static struct device _log_dev;
+static struct str_list _log_dev_alias;
+
+static int _syslog = 0;
+static int _log_to_file = 0;
+static int _log_direct = 0;
+static int _log_while_suspended = 0;
+static int _indent = 1;
+static int _log_suppress = 0;
+static char _msg_prefix[30] = " ";
+static int _already_logging = 0;
+static int _abort_on_internal_errors = 0;
+
+static lvm2_log_fn_t _lvm2_log_fn = NULL;
+
+static int _lvm_errno = 0;
+static int _store_errmsg = 0;
+static char *_lvm_errmsg = NULL;
+
+void init_log_fn(lvm2_log_fn_t log_fn)
+{
+ if (log_fn)
+ _lvm2_log_fn = log_fn;
+ else
+ _lvm2_log_fn = NULL;
+}
+
+void init_log_file(const char *log_file, int append)
+{
+ const char *open_mode = append ? "a" : "w";
+
+ if (!(_log_file = fopen(log_file, open_mode))) {
+ log_sys_error("fopen", log_file);
+ return;
+ }
+
+ _log_to_file = 1;
+}
+
+void init_log_direct(const char *log_file, int append)
+{
+ int open_flags = append ? 0 : O_TRUNC;
+
+ dev_create_file(log_file, &_log_dev, &_log_dev_alias, 1);
+ if (!dev_open_flags(&_log_dev, O_RDWR | O_CREAT | open_flags, 1, 0))
+ return;
+
+ _log_direct = 1;
+}
+
+void init_log_while_suspended(int log_while_suspended)
+{
+ _log_while_suspended = log_while_suspended;
+}
+
+void init_syslog(int facility)
+{
+ openlog("lvm", LOG_PID, facility);
+ _syslog = 1;
+}
+
+int log_suppress(int suppress)
+{
+ int old_suppress = _log_suppress;
+
+ _log_suppress = suppress;
+
+ return old_suppress;
+}
+
+void release_log_memory(void)
+{
+ if (!_log_direct)
+ return;
+
+ dm_free((char *) _log_dev_alias.str);
+ _log_dev_alias.str = "activate_log file";
+}
+
+void fin_log(void)
+{
+ if (_log_direct) {
+ dev_close(&_log_dev);
+ _log_direct = 0;
+ }
+
+ if (_log_to_file) {
+ if (dm_fclose(_log_file)) {
+ if (errno)
+ fprintf(stderr, "failed to write log file: %s\n",
+ strerror(errno));
+ else
+ fprintf(stderr, "failed to write log file\n");
+
+ }
+ _log_to_file = 0;
+ }
+}
+
+void fin_syslog()
+{
+ if (_syslog)
+ closelog();
+ _syslog = 0;
+}
+
+void init_msg_prefix(const char *prefix)
+{
+ strncpy(_msg_prefix, prefix, sizeof(_msg_prefix));
+ _msg_prefix[sizeof(_msg_prefix) - 1] = '\0';
+}
+
+void init_indent(int indent)
+{
+ _indent = indent;
+}
+
+void init_abort_on_internal_errors(int fatal)
+{
+ _abort_on_internal_errors = fatal;
+}
+
+void reset_lvm_errno(int store_errmsg)
+{
+ _lvm_errno = 0;
+
+ if (_lvm_errmsg) {
+ dm_free(_lvm_errmsg);
+ _lvm_errmsg = NULL;
+ }
+
+ _store_errmsg = store_errmsg;
+}
+
+int stored_errno(void)
+{
+ return _lvm_errno;
+}
+
+const char *stored_errmsg(void)
+{
+ return _lvm_errmsg ? : "";
+}
+
+static struct dm_hash_table *_duplicated = NULL;
+
+void reset_log_duplicated(void) {
+ if (_duplicated)
+ dm_hash_destroy(_duplicated);
+ _duplicated = NULL;
+}
+
+void print_log(int level, const char *file, int line, int dm_errno,
+ const char *format, ...)
+{
+ va_list ap;
+ char buf[1024], buf2[4096], locn[4096];
+ int bufused, n;
+ const char *message;
+ const char *trformat; /* Translated format string */
+ char *newbuf;
+ int use_stderr = level & _LOG_STDERR;
+ int log_once = level & _LOG_ONCE;
+ int fatal_internal_error = 0;
+
+ level &= ~(_LOG_STDERR|_LOG_ONCE);
+
+ if (_abort_on_internal_errors &&
+ !strncmp(format, INTERNAL_ERROR,
+ strlen(INTERNAL_ERROR))) {
+ fatal_internal_error = 1;
+ /* Internal errors triggering abort cannot be suppressed. */
+ _log_suppress = 0;
+ level = _LOG_FATAL;
+ }
+
+ if (_log_suppress == 2)
+ return;
+
+ if (level <= _LOG_ERR)
+ init_error_message_produced(1);
+
+ trformat = _(format);
+
+ if (dm_errno && !_lvm_errno)
+ _lvm_errno = dm_errno;
+
+ if (_lvm2_log_fn ||
+ (_store_errmsg && (level <= _LOG_ERR)) ||
+ log_once) {
+ va_start(ap, format);
+ n = vsnprintf(buf2, sizeof(buf2) - 1, trformat, ap);
+ va_end(ap);
+
+ if (n < 0) {
+ fprintf(stderr, _("vsnprintf failed: skipping external "
+ "logging function"));
+ goto log_it;
+ }
+
+ buf2[sizeof(buf2) - 1] = '\0';
+ message = &buf2[0];
+ }
+
+ if (_store_errmsg && (level <= _LOG_ERR)) {
+ if (!_lvm_errmsg)
+ _lvm_errmsg = dm_strdup(message);
+ else if ((newbuf = dm_realloc(_lvm_errmsg,
+ strlen(_lvm_errmsg) +
+ strlen(message) + 2))) {
+ _lvm_errmsg = strcat(newbuf, "\n");
+ _lvm_errmsg = strcat(newbuf, message);
+ }
+ }
+
+ if (log_once) {
+ if (!_duplicated)
+ _duplicated = dm_hash_create(128);
+ if (_duplicated) {
+ if (dm_hash_lookup(_duplicated, message))
+ level = _LOG_NOTICE;
+ dm_hash_insert(_duplicated, message, (void*)1);
+ }
+ }
+
+ if (_lvm2_log_fn) {
+ _lvm2_log_fn(level, file, line, 0, message);
+ if (fatal_internal_error)
+ abort();
+ return;
+ }
+
+ log_it:
+ if (!_log_suppress) {
+ if (verbose_level() > _LOG_DEBUG)
+ dm_snprintf(locn, sizeof(locn), "#%s:%d ",
+ file, line);
+ else
+ locn[0] = '\0';
+
+ va_start(ap, format);
+ switch (level) {
+ case _LOG_DEBUG:
+ if (!strcmp("<backtrace>", format) &&
+ verbose_level() <= _LOG_DEBUG)
+ break;
+ if (verbose_level() >= _LOG_DEBUG) {
+ fprintf(stderr, "%s%s%s", locn, log_command_name(),
+ _msg_prefix);
+ if (_indent)
+ fprintf(stderr, " ");
+ vfprintf(stderr, trformat, ap);
+ fputc('\n', stderr);
+ }
+ break;
+
+ case _LOG_INFO:
+ if (verbose_level() >= _LOG_INFO) {
+ fprintf(stderr, "%s%s%s", locn, log_command_name(),
+ _msg_prefix);
+ if (_indent)
+ fprintf(stderr, " ");
+ vfprintf(stderr, trformat, ap);
+ fputc('\n', stderr);
+ }
+ break;
+ case _LOG_NOTICE:
+ if (verbose_level() >= _LOG_NOTICE) {
+ fprintf(stderr, "%s%s%s", locn, log_command_name(),
+ _msg_prefix);
+ if (_indent)
+ fprintf(stderr, " ");
+ vfprintf(stderr, trformat, ap);
+ fputc('\n', stderr);
+ }
+ break;
+ case _LOG_WARN:
+ if (verbose_level() >= _LOG_WARN) {
+ fprintf(use_stderr ? stderr : stdout, "%s%s",
+ log_command_name(), _msg_prefix);
+ vfprintf(use_stderr ? stderr : stdout, trformat, ap);
+ fputc('\n', use_stderr ? stderr : stdout);
+ }
+ break;
+ case _LOG_ERR:
+ if (verbose_level() >= _LOG_ERR) {
+ fprintf(stderr, "%s%s%s", locn, log_command_name(),
+ _msg_prefix);
+ vfprintf(stderr, trformat, ap);
+ fputc('\n', stderr);
+ }
+ break;
+ case _LOG_FATAL:
+ default:
+ if (verbose_level() >= _LOG_FATAL) {
+ fprintf(stderr, "%s%s%s", locn, log_command_name(),
+ _msg_prefix);
+ vfprintf(stderr, trformat, ap);
+ fputc('\n', stderr);
+ }
+ break;
+ }
+ va_end(ap);
+ }
+
+ if (fatal_internal_error)
+ abort();
+
+ if (level > debug_level())
+ return;
+
+ if (_log_to_file && (_log_while_suspended || !memlock())) {
+ fprintf(_log_file, "%s:%d %s%s", file, line, log_command_name(),
+ _msg_prefix);
+
+ va_start(ap, format);
+ vfprintf(_log_file, trformat, ap);
+ va_end(ap);
+
+ fprintf(_log_file, "\n");
+ fflush(_log_file);
+ }
+
+ if (_syslog && (_log_while_suspended || !memlock())) {
+ va_start(ap, format);
+ vsyslog(level, trformat, ap);
+ va_end(ap);
+ }
+
+ /* FIXME This code is unfinished - pre-extend & condense. */
+ if (!_already_logging && _log_direct && memlock()) {
+ _already_logging = 1;
+ memset(&buf, ' ', sizeof(buf));
+ bufused = 0;
+ if ((n = dm_snprintf(buf, sizeof(buf) - 1,
+ "%s:%d %s%s", file, line, log_command_name(),
+ _msg_prefix)) == -1)
+ goto done;
+
+ bufused += n;
+
+ va_start(ap, format);
+ n = vsnprintf(buf + bufused - 1, sizeof(buf) - bufused - 1,
+ trformat, ap);
+ va_end(ap);
+ bufused += n;
+
+ done:
+ buf[bufused - 1] = '\n';
+ buf[bufused] = '\n';
+ buf[sizeof(buf) - 1] = '\n';
+ /* FIXME real size bufused */
+ dev_append(&_log_dev, sizeof(buf), buf);
+ _already_logging = 0;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_LOG_H
+#define _LVM_LOG_H
+
+/*
+ * printf()-style macros to use for messages:
+ *
+ * log_error - always print to stderr.
+ * log_print - always print to stdout. Use this instead of printf.
+ * log_verbose - print to stdout if verbose is set (-v)
+ * log_very_verbose - print to stdout if verbose is set twice (-vv)
+ * log_debug - print to stdout if verbose is set three times (-vvv)
+ *
+ * In addition, messages will be logged to file or syslog if they
+ * are more serious than the log level specified with the log/debug_level
+ * parameter in the configuration file. These messages get the file
+ * and line number prepended. 'stack' (without arguments) can be used
+ * to log this information at debug level.
+ *
+ * log_sys_error and log_sys_very_verbose are for errors from system calls
+ * e.g. log_sys_error("stat", filename);
+ * /dev/fd/7: stat failed: No such file or directory
+ *
+ */
+
+#include <stdio.h> /* FILE */
+#include <string.h> /* strerror() */
+#include <errno.h>
+
+#define EUNCLASSIFIED -1 /* Generic error code */
+
+#define _LOG_STDERR 128 /* force things to go to stderr, even if loglevel
+ would make them go to stdout */
+#define _LOG_ONCE 256 /* downgrade to NOTICE if this has been already logged */
+#define _LOG_DEBUG 7
+#define _LOG_INFO 6
+#define _LOG_NOTICE 5
+#define _LOG_WARN 4
+#define _LOG_ERR 3
+#define _LOG_FATAL 2
+#define INTERNAL_ERROR "Internal error: "
+
+#define log_debug(x...) LOG_LINE(_LOG_DEBUG, x)
+#define log_info(x...) LOG_LINE(_LOG_INFO, x)
+#define log_notice(x...) LOG_LINE(_LOG_NOTICE, x)
+#define log_warn(x...) LOG_LINE(_LOG_WARN | _LOG_STDERR, x)
+#define log_warn_suppress(s, x...) LOG_LINE(s ? _LOG_NOTICE : _LOG_WARN | _LOG_STDERR, x)
+#define log_err(x...) LOG_LINE_WITH_ERRNO(_LOG_ERR, EUNCLASSIFIED, x)
+#define log_err_suppress(s, x...) LOG_LINE_WITH_ERRNO(s ? _LOG_NOTICE : _LOG_ERR, EUNCLASSIFIED, x)
+#define log_err_once(x...) LOG_LINE_WITH_ERRNO(_LOG_ERR | _LOG_ONCE, EUNCLASSIFIED, x)
+#define log_fatal(x...) LOG_LINE_WITH_ERRNO(_LOG_FATAL, EUNCLASSIFIED, x)
+
+#define stack log_debug("<backtrace>") /* Backtrace on error */
+#define log_very_verbose(args...) log_info(args)
+#define log_verbose(args...) log_notice(args)
+#define log_print(args...) LOG_LINE(_LOG_WARN, args)
+#define log_error(args...) log_err(args)
+#define log_error_suppress(s, args...) log_err_suppress(s, args)
+#define log_error_once(args...) log_err_once(args)
+#define log_errno(args...) LOG_LINE_WITH_ERRNO(_LOG_ERR, args)
+
+/* System call equivalents */
+#define log_sys_error(x, y) \
+ log_err("%s: %s failed: %s", y, x, strerror(errno))
+#define log_sys_very_verbose(x, y) \
+ log_info("%s: %s failed: %s", y, x, strerror(errno))
+#define log_sys_debug(x, y) \
+ log_debug("%s: %s failed: %s", y, x, strerror(errno))
+
+#define return_0 do { stack; return 0; } while (0)
+#define return_NULL do { stack; return NULL; } while (0)
+#define goto_out do { stack; goto out; } while (0)
+#define goto_bad do { stack; goto bad; } while (0)
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_LOGGING_H
+#define _LVM_LOGGING_H
+
+void print_log(int level, const char *file, int line, int dm_errno,
+ const char *format, ...)
+ __attribute__ ((format(printf, 5, 6)));
+
+#define LOG_LINE(l, x...) \
+ print_log(l, __FILE__, __LINE__ , 0, ## x)
+
+#define LOG_LINE_WITH_ERRNO(l, e, x...) \
+ print_log(l, __FILE__, __LINE__ , e, ## x)
+
+#include "log.h"
+
+typedef void (*lvm2_log_fn_t) (int level, const char *file, int line,
+ int dm_errno, const char *message);
+
+void init_log_fn(lvm2_log_fn_t log_fn);
+
+void init_indent(int indent);
+void init_msg_prefix(const char *prefix);
+
+void init_log_file(const char *log_file, int append);
+void init_log_direct(const char *log_file, int append);
+void init_log_while_suspended(int log_while_suspended);
+void init_abort_on_internal_errors(int fatal);
+
+void fin_log(void);
+void release_log_memory(void);
+void reset_log_duplicated(void);
+
+void init_syslog(int facility);
+void fin_syslog(void);
+
+int error_message_produced(void);
+void reset_lvm_errno(int store_errmsg);
+int stored_errno(void);
+const char *stored_errmsg(void);
+
+/* Suppress messages to stdout/stderr (1) or everywhere (2) */
+/* Returns previous setting */
+int log_suppress(int suppress);
+
+/* Suppress messages to syslog */
+void syslog_suppress(int suppress);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "display.h"
+#include "activate.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "str_list.h"
+
+char *lvseg_tags_dup(const struct lv_segment *seg)
+{
+ return tags_format_and_copy(seg->lv->vg->vgmem, &seg->tags);
+}
+
+char *lvseg_segtype_dup(const struct lv_segment *seg)
+{
+ if (seg->area_count == 1) {
+ return (char *)"linear";
+ }
+
+ return dm_pool_strdup(seg->lv->vg->vgmem, seg->segtype->ops->name(seg));
+}
+
+uint64_t lvseg_chunksize(const struct lv_segment *seg)
+{
+ uint64_t size;
+
+ if (lv_is_cow(seg->lv))
+ size = (uint64_t) find_cow(seg->lv)->chunk_size;
+ else
+ size = UINT64_C(0);
+ return size;
+}
+
+uint64_t lvseg_start(const struct lv_segment *seg)
+{
+ return (uint64_t) seg->le * seg->lv->vg->extent_size;
+}
+
+uint64_t lvseg_size(const struct lv_segment *seg)
+{
+ return (uint64_t) seg->len * seg->lv->vg->extent_size;
+}
+
+uint32_t lv_kernel_read_ahead(const struct logical_volume *lv)
+{
+ struct lvinfo info;
+
+ if (!lv_info(lv->vg->cmd, lv, 0, &info, 0, 1) || !info.exists)
+ return UINT32_MAX;
+ return info.read_ahead;
+}
+
+char *lv_origin_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+ if (lv_is_cow(lv))
+ return lv_name_dup(mem, origin_from_cow(lv));
+ return NULL;
+}
+
+char *lv_name_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+ return dm_pool_strdup(mem, lv->name);
+}
+
+char *lv_modules_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+ struct dm_list *modules;
+
+ if (!(modules = str_list_create(mem))) {
+ log_error("modules str_list allocation failed");
+ return NULL;
+ }
+
+ if (!list_lv_modules(mem, lv, modules))
+ return_NULL;
+ return tags_format_and_copy(mem, modules);
+}
+
+char *lv_mirror_log_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+ struct lv_segment *seg;
+
+ dm_list_iterate_items(seg, &lv->segments) {
+ if (!seg_is_mirrored(seg) || !seg->log_lv)
+ continue;
+ return dm_pool_strdup(mem, seg->log_lv->name);
+ }
+ return NULL;
+}
+
+int lv_kernel_minor(const struct logical_volume *lv)
+{
+ struct lvinfo info;
+
+ if (lv_info(lv->vg->cmd, lv, 0, &info, 0, 0) && info.exists)
+ return info.minor;
+ return -1;
+}
+
+int lv_kernel_major(const struct logical_volume *lv)
+{
+ struct lvinfo info;
+ if (lv_info(lv->vg->cmd, lv, 0, &info, 0, 0) && info.exists)
+ return info.major;
+ return -1;
+}
+
+char *lv_convert_lv_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+ struct lv_segment *seg;
+
+ if (lv->status & (CONVERTING|MIRRORED)) {
+ seg = first_seg(lv);
+
+ /* Temporary mirror is always area_num == 0 */
+ if (seg_type(seg, 0) == AREA_LV &&
+ is_temporary_mirror_layer(seg_lv(seg, 0)))
+ return dm_pool_strdup(mem, seg_lv(seg, 0)->name);
+ }
+ return NULL;
+}
+
+char *lv_move_pv_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+ struct lv_segment *seg;
+
+ dm_list_iterate_items(seg, &lv->segments) {
+ if (seg->status & PVMOVE)
+ return dm_pool_strdup(mem, dev_name(seg_dev(seg, 0)));
+ }
+ return NULL;
+}
+
+uint64_t lv_origin_size(const struct logical_volume *lv)
+{
+ if (lv_is_cow(lv))
+ return (uint64_t) find_cow(lv)->len * lv->vg->extent_size;
+ if (lv_is_origin(lv))
+ return lv->size;
+ return 0;
+}
+
+char *lv_path_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+ char *repstr;
+ size_t len;
+
+ len = strlen(lv->vg->cmd->dev_dir) + strlen(lv->vg->name) +
+ strlen(lv->name) + 2;
+
+ if (!(repstr = dm_pool_zalloc(mem, len))) {
+ log_error("dm_pool_alloc failed");
+ return 0;
+ }
+
+ if (dm_snprintf(repstr, len, "%s%s/%s",
+ lv->vg->cmd->dev_dir, lv->vg->name, lv->name) < 0) {
+ log_error("lvpath snprintf failed");
+ return 0;
+ }
+ return repstr;
+}
+
+char *lv_uuid_dup(const struct logical_volume *lv)
+{
+ return id_format_and_copy(lv->vg->vgmem, &lv->lvid.id[1]);
+}
+
+char *lv_tags_dup(const struct logical_volume *lv)
+{
+ return tags_format_and_copy(lv->vg->vgmem, &lv->tags);
+}
+
+uint64_t lv_size(const struct logical_volume *lv)
+{
+ return lv->size;
+}
+
+static int _lv_mimage_in_sync(const struct logical_volume *lv)
+{
+ percent_t percent;
+ struct lv_segment *mirror_seg = find_mirror_seg(first_seg(lv));
+
+ if (!(lv->status & MIRROR_IMAGE) || !mirror_seg)
+ return_0;
+
+ if (!lv_mirror_percent(lv->vg->cmd, mirror_seg->lv, 0, &percent,
+ NULL))
+ return_0;
+
+ return (percent == PERCENT_100) ? 1 : 0;
+}
+
+char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+ percent_t snap_percent;
+ struct lvinfo info;
+ char *repstr;
+
+ if (!(repstr = dm_pool_zalloc(mem, 7))) {
+ log_error("dm_pool_alloc failed");
+ return 0;
+ }
+
+ /* Blank if this is a "free space" LV. */
+ if (!*lv->name)
+ goto out;
+
+ if (lv->status & PVMOVE)
+ repstr[0] = 'p';
+ else if (lv->status & CONVERTING)
+ repstr[0] = 'c';
+ else if (lv->status & VIRTUAL)
+ repstr[0] = 'v';
+ /* Origin takes precedence over Mirror */
+ else if (lv_is_origin(lv)) {
+ repstr[0] = (lv_is_merging_origin(lv)) ? 'O' : 'o';
+ }
+ else if (lv->status & MIRRORED) {
+ repstr[0] = (lv->status & MIRROR_NOTSYNCED) ? 'M' : 'm';
+ }else if (lv->status & MIRROR_IMAGE)
+ repstr[0] = (_lv_mimage_in_sync(lv)) ? 'i' : 'I';
+ else if (lv->status & MIRROR_LOG)
+ repstr[0] = 'l';
+ else if (lv_is_cow(lv)) {
+ repstr[0] = (lv_is_merging_cow(lv)) ? 'S' : 's';
+ } else
+ repstr[0] = '-';
+
+ if (lv->status & PVMOVE)
+ repstr[1] = '-';
+ else if (lv->status & LVM_WRITE)
+ repstr[1] = 'w';
+ else if (lv->status & LVM_READ)
+ repstr[1] = 'r';
+ else
+ repstr[1] = '-';
+
+ repstr[2] = alloc_policy_char(lv->alloc);
+
+ if (lv->status & LOCKED)
+ repstr[2] = toupper(repstr[2]);
+
+ repstr[3] = (lv->status & FIXED_MINOR) ? 'm' : '-';
+
+ if (lv_info(lv->vg->cmd, lv, 0, &info, 1, 0) && info.exists) {
+ if (info.suspended)
+ repstr[4] = 's'; /* Suspended */
+ else if (info.live_table)
+ repstr[4] = 'a'; /* Active */
+ else if (info.inactive_table)
+ repstr[4] = 'i'; /* Inactive with table */
+ else
+ repstr[4] = 'd'; /* Inactive without table */
+
+ /* Snapshot dropped? */
+ if (info.live_table && lv_is_cow(lv) &&
+ (!lv_snapshot_percent(lv, &snap_percent) ||
+ snap_percent == PERCENT_INVALID)) {
+ repstr[0] = toupper(repstr[0]);
+ if (info.suspended)
+ repstr[4] = 'S'; /* Susp Inv snapshot */
+ else
+ repstr[4] = 'I'; /* Invalid snapshot */
+ }
+
+ repstr[5] = (info.open_count) ? 'o' : '-';
+ } else {
+ repstr[4] = '-';
+ repstr[5] = '-';
+ }
+out:
+ return repstr;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _LVM_LV_H
+#define _LVM_LV_H
+
+union lvid;
+struct volume_group;
+struct dm_list;
+struct lv_segment;
+struct replicator_device;
+
+struct logical_volume {
+ union lvid lvid;
+ char *name;
+
+ struct volume_group *vg;
+
+ uint64_t status;
+ alloc_policy_t alloc;
+ uint32_t read_ahead;
+ int32_t major;
+ int32_t minor;
+
+ uint64_t size; /* Sectors */
+ uint32_t le_count;
+
+ uint32_t origin_count;
+ struct dm_list snapshot_segs;
+ struct lv_segment *snapshot;
+
+ struct replicator_device *rdevice;/* For replicator-devs, rimages, slogs - reference to rdevice */
+ struct dm_list rsites; /* For replicators - all sites */
+
+ struct dm_list segments;
+ struct dm_list tags;
+ struct dm_list segs_using_this_lv;
+};
+
+uint64_t lv_size(const struct logical_volume *lv);
+char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv);
+char *lv_uuid_dup(const struct logical_volume *lv);
+char *lv_tags_dup(const struct logical_volume *lv);
+char *lv_path_dup(struct dm_pool *mem, const struct logical_volume *lv);
+uint64_t lv_origin_size(const struct logical_volume *lv);
+char *lv_move_pv_dup(struct dm_pool *mem, const struct logical_volume *lv);
+char *lv_convert_lv_dup(struct dm_pool *mem, const struct logical_volume *lv);
+int lv_kernel_major(const struct logical_volume *lv);
+int lv_kernel_minor(const struct logical_volume *lv);
+char *lv_mirror_log_dup(struct dm_pool *mem, const struct logical_volume *lv);
+char *lv_modules_dup(struct dm_pool *mem, const struct logical_volume *lv);
+char *lv_name_dup(struct dm_pool *mem, const struct logical_volume *lv);
+char *lv_origin_dup(struct dm_pool *mem, const struct logical_volume *lv);
+uint32_t lv_kernel_read_ahead(const struct logical_volume *lv);
+uint64_t lvseg_start(const struct lv_segment *seg);
+uint64_t lvseg_size(const struct lv_segment *seg);
+uint64_t lvseg_chunksize(const struct lv_segment *seg);
+char *lvseg_segtype_dup(const struct lv_segment *seg);
+char *lvseg_tags_dup(const struct lv_segment *seg);
+
+#endif /* _LVM_LV_H */
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_LV_ALLOC_H
+
+struct lv_segment *alloc_lv_segment(struct dm_pool *mem,
+ const struct segment_type *segtype,
+ struct logical_volume *lv,
+ uint32_t le, uint32_t len,
+ uint64_t status,
+ uint32_t stripe_size,
+ struct logical_volume *log_lv,
+ uint32_t area_count,
+ uint32_t area_len,
+ uint32_t chunk_size,
+ uint32_t region_size,
+ uint32_t extents_copied,
+ struct lv_segment *pvmove_source_seg);
+
+struct lv_segment *alloc_snapshot_seg(struct logical_volume *lv,
+ uint64_t status, uint32_t old_le_count);
+
+int set_lv_segment_area_pv(struct lv_segment *seg, uint32_t area_num,
+ struct physical_volume *pv, uint32_t pe);
+int set_lv_segment_area_lv(struct lv_segment *seg, uint32_t area_num,
+ struct logical_volume *lv, uint32_t le,
+ uint64_t status);
+int move_lv_segment_area(struct lv_segment *seg_to, uint32_t area_to,
+ struct lv_segment *seg_from, uint32_t area_from);
+void release_lv_segment_area(struct lv_segment *seg, uint32_t s,
+ uint32_t area_reduction);
+
+struct alloc_handle;
+struct alloc_handle *allocate_extents(struct volume_group *vg,
+ struct logical_volume *lv,
+ const struct segment_type *segtype,
+ uint32_t stripes,
+ uint32_t mirrors, uint32_t log_count,
+ uint32_t log_region_size, uint32_t extents,
+ struct dm_list *allocatable_pvs,
+ alloc_policy_t alloc,
+ struct dm_list *parallel_areas);
+
+int lv_add_segment(struct alloc_handle *ah,
+ uint32_t first_area, uint32_t num_areas,
+ struct logical_volume *lv,
+ const struct segment_type *segtype,
+ uint32_t stripe_size,
+ uint64_t status,
+ uint32_t region_size);
+
+int lv_add_mirror_areas(struct alloc_handle *ah,
+ struct logical_volume *lv, uint32_t le,
+ uint32_t region_size);
+int lv_add_mirror_lvs(struct logical_volume *lv,
+ struct logical_volume **sub_lvs,
+ uint32_t num_extra_areas,
+ uint64_t status, uint32_t region_size);
+
+int lv_add_log_segment(struct alloc_handle *ah, uint32_t first_area,
+ struct logical_volume *log_lv, uint64_t status);
+int lv_add_virtual_segment(struct logical_volume *lv, uint64_t status,
+ uint32_t extents, const struct segment_type *segtype);
+
+void alloc_destroy(struct alloc_handle *ah);
+
+struct dm_list *build_parallel_areas_from_lv(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ unsigned use_pvmove_parent_lv);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "locking.h"
+#include "pv_map.h"
+#include "lvm-string.h"
+#include "toolcontext.h"
+#include "lv_alloc.h"
+#include "pv_alloc.h"
+#include "display.h"
+#include "segtype.h"
+#include "archiver.h"
+#include "activate.h"
+#include "str_list.h"
+
+struct lv_names {
+ const char *old;
+ const char *new;
+};
+
+int add_seg_to_segs_using_this_lv(struct logical_volume *lv,
+ struct lv_segment *seg)
+{
+ struct seg_list *sl;
+
+ dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
+ if (sl->seg == seg) {
+ sl->count++;
+ return 1;
+ }
+ }
+
+ log_very_verbose("Adding %s:%" PRIu32 " as an user of %s",
+ seg->lv->name, seg->le, lv->name);
+
+ if (!(sl = dm_pool_zalloc(lv->vg->vgmem, sizeof(*sl)))) {
+ log_error("Failed to allocate segment list");
+ return 0;
+ }
+
+ sl->count = 1;
+ sl->seg = seg;
+ dm_list_add(&lv->segs_using_this_lv, &sl->list);
+
+ return 1;
+}
+
+int remove_seg_from_segs_using_this_lv(struct logical_volume *lv,
+ struct lv_segment *seg)
+{
+ struct seg_list *sl;
+
+ dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
+ if (sl->seg != seg)
+ continue;
+ if (sl->count > 1)
+ sl->count--;
+ else {
+ log_very_verbose("%s:%" PRIu32 " is no longer a user "
+ "of %s", seg->lv->name, seg->le,
+ lv->name);
+ dm_list_del(&sl->list);
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * This is a function specialized for the common case where there is
+ * only one segment which uses the LV.
+ * e.g. the LV is a layer inserted by insert_layer_for_lv().
+ *
+ * In general, walk through lv->segs_using_this_lv.
+ */
+struct lv_segment *get_only_segment_using_this_lv(struct logical_volume *lv)
+{
+ struct seg_list *sl;
+
+ if (dm_list_size(&lv->segs_using_this_lv) != 1) {
+ log_error("%s is expected to have only one segment using it, "
+ "while it has %d", lv->name,
+ dm_list_size(&lv->segs_using_this_lv));
+ return NULL;
+ }
+
+ sl = dm_list_item(dm_list_first(&lv->segs_using_this_lv), struct seg_list);
+
+ if (sl->count != 1) {
+ log_error("%s is expected to have only one segment using it, "
+ "while %s:%" PRIu32 " uses it %d times",
+ lv->name, sl->seg->lv->name, sl->seg->le, sl->count);
+ return NULL;
+ }
+
+ return sl->seg;
+}
+
+/*
+ * PVs used by a segment of an LV
+ */
+struct seg_pvs {
+ struct dm_list list;
+
+ struct dm_list pvs; /* struct pv_list */
+
+ uint32_t le;
+ uint32_t len;
+};
+
+static struct seg_pvs *_find_seg_pvs_by_le(struct dm_list *list, uint32_t le)
+{
+ struct seg_pvs *spvs;
+
+ dm_list_iterate_items(spvs, list)
+ if (le >= spvs->le && le < spvs->le + spvs->len)
+ return spvs;
+
+ return NULL;
+}
+
+/*
+ * Find first unused LV number.
+ */
+uint32_t find_free_lvnum(struct logical_volume *lv)
+{
+ int lvnum_used[MAX_RESTRICTED_LVS + 1];
+ uint32_t i = 0;
+ struct lv_list *lvl;
+ int lvnum;
+
+ memset(&lvnum_used, 0, sizeof(lvnum_used));
+
+ dm_list_iterate_items(lvl, &lv->vg->lvs) {
+ lvnum = lvnum_from_lvid(&lvl->lv->lvid);
+ if (lvnum <= MAX_RESTRICTED_LVS)
+ lvnum_used[lvnum] = 1;
+ }
+
+ while (lvnum_used[i])
+ i++;
+
+ /* FIXME What if none are free? */
+
+ return i;
+}
+
+/*
+ * All lv_segments get created here.
+ */
+struct lv_segment *alloc_lv_segment(struct dm_pool *mem,
+ const struct segment_type *segtype,
+ struct logical_volume *lv,
+ uint32_t le, uint32_t len,
+ uint64_t status,
+ uint32_t stripe_size,
+ struct logical_volume *log_lv,
+ uint32_t area_count,
+ uint32_t area_len,
+ uint32_t chunk_size,
+ uint32_t region_size,
+ uint32_t extents_copied,
+ struct lv_segment *pvmove_source_seg)
+{
+ struct lv_segment *seg;
+ uint32_t areas_sz = area_count * sizeof(*seg->areas);
+
+ if (!(seg = dm_pool_zalloc(mem, sizeof(*seg))))
+ return_NULL;
+
+ if (!(seg->areas = dm_pool_zalloc(mem, areas_sz))) {
+ dm_pool_free(mem, seg);
+ return_NULL;
+ }
+
+ if (!segtype) {
+ log_error("alloc_lv_segment: Missing segtype.");
+ return NULL;
+ }
+
+ seg->segtype = segtype;
+ seg->lv = lv;
+ seg->le = le;
+ seg->len = len;
+ seg->status = status;
+ seg->stripe_size = stripe_size;
+ seg->area_count = area_count;
+ seg->area_len = area_len;
+ seg->chunk_size = chunk_size;
+ seg->region_size = region_size;
+ seg->extents_copied = extents_copied;
+ seg->log_lv = log_lv;
+ seg->pvmove_source_seg = pvmove_source_seg;
+ dm_list_init(&seg->tags);
+
+ if (log_lv && !attach_mirror_log(seg, log_lv))
+ return_NULL;
+
+ return seg;
+}
+
+struct lv_segment *alloc_snapshot_seg(struct logical_volume *lv,
+ uint64_t status, uint32_t old_le_count)
+{
+ struct lv_segment *seg;
+ const struct segment_type *segtype;
+
+ segtype = get_segtype_from_string(lv->vg->cmd, "snapshot");
+ if (!segtype) {
+ log_error("Failed to find snapshot segtype");
+ return NULL;
+ }
+
+ if (!(seg = alloc_lv_segment(lv->vg->cmd->mem, segtype, lv, old_le_count,
+ lv->le_count - old_le_count, status, 0,
+ NULL, 0, lv->le_count - old_le_count,
+ 0, 0, 0, NULL))) {
+ log_error("Couldn't allocate new snapshot segment.");
+ return NULL;
+ }
+
+ dm_list_add(&lv->segments, &seg->list);
+ lv->status |= VIRTUAL;
+
+ return seg;
+}
+
+void release_lv_segment_area(struct lv_segment *seg, uint32_t s,
+ uint32_t area_reduction)
+{
+ if (seg_type(seg, s) == AREA_UNASSIGNED)
+ return;
+
+ if (seg_type(seg, s) == AREA_PV) {
+ if (release_pv_segment(seg_pvseg(seg, s), area_reduction) &&
+ seg->area_len == area_reduction)
+ seg_type(seg, s) = AREA_UNASSIGNED;
+ return;
+ }
+
+ if (seg_lv(seg, s)->status & MIRROR_IMAGE) {
+ lv_reduce(seg_lv(seg, s), area_reduction);
+ return;
+ }
+
+ if (area_reduction == seg->area_len) {
+ log_very_verbose("Remove %s:%" PRIu32 "[%" PRIu32 "] from "
+ "the top of LV %s:%" PRIu32,
+ seg->lv->name, seg->le, s,
+ seg_lv(seg, s)->name, seg_le(seg, s));
+
+ remove_seg_from_segs_using_this_lv(seg_lv(seg, s), seg);
+ seg_lv(seg, s) = NULL;
+ seg_le(seg, s) = 0;
+ seg_type(seg, s) = AREA_UNASSIGNED;
+ }
+}
+
+/*
+ * Move a segment area from one segment to another
+ */
+int move_lv_segment_area(struct lv_segment *seg_to, uint32_t area_to,
+ struct lv_segment *seg_from, uint32_t area_from)
+{
+ struct physical_volume *pv;
+ struct logical_volume *lv;
+ uint32_t pe, le;
+
+ switch (seg_type(seg_from, area_from)) {
+ case AREA_PV:
+ pv = seg_pv(seg_from, area_from);
+ pe = seg_pe(seg_from, area_from);
+
+ release_lv_segment_area(seg_from, area_from,
+ seg_from->area_len);
+ release_lv_segment_area(seg_to, area_to, seg_to->area_len);
+
+ if (!set_lv_segment_area_pv(seg_to, area_to, pv, pe))
+ return_0;
+
+ break;
+
+ case AREA_LV:
+ lv = seg_lv(seg_from, area_from);
+ le = seg_le(seg_from, area_from);
+
+ release_lv_segment_area(seg_from, area_from,
+ seg_from->area_len);
+ release_lv_segment_area(seg_to, area_to, seg_to->area_len);
+
+ if (!set_lv_segment_area_lv(seg_to, area_to, lv, le, 0))
+ return_0;
+
+ break;
+
+ case AREA_UNASSIGNED:
+ release_lv_segment_area(seg_to, area_to, seg_to->area_len);
+ }
+
+ return 1;
+}
+
+/*
+ * Link part of a PV to an LV segment.
+ */
+int set_lv_segment_area_pv(struct lv_segment *seg, uint32_t area_num,
+ struct physical_volume *pv, uint32_t pe)
+{
+ seg->areas[area_num].type = AREA_PV;
+
+ if (!(seg_pvseg(seg, area_num) =
+ assign_peg_to_lvseg(pv, pe, seg->area_len, seg, area_num)))
+ return_0;
+
+ return 1;
+}
+
+/*
+ * Link one LV segment to another. Assumes sizes already match.
+ */
+int set_lv_segment_area_lv(struct lv_segment *seg, uint32_t area_num,
+ struct logical_volume *lv, uint32_t le,
+ uint64_t status)
+{
+ log_very_verbose("Stack %s:%" PRIu32 "[%" PRIu32 "] on LV %s:%" PRIu32,
+ seg->lv->name, seg->le, area_num, lv->name, le);
+
+ seg->areas[area_num].type = AREA_LV;
+ seg_lv(seg, area_num) = lv;
+ seg_le(seg, area_num) = le;
+ lv->status |= status;
+
+ if (!add_seg_to_segs_using_this_lv(lv, seg))
+ return_0;
+
+ return 1;
+}
+
+/*
+ * Prepare for adding parallel areas to an existing segment.
+ */
+static int _lv_segment_add_areas(struct logical_volume *lv,
+ struct lv_segment *seg,
+ uint32_t new_area_count)
+{
+ struct lv_segment_area *newareas;
+ uint32_t areas_sz = new_area_count * sizeof(*newareas);
+
+ if (!(newareas = dm_pool_zalloc(lv->vg->cmd->mem, areas_sz)))
+ return_0;
+
+ memcpy(newareas, seg->areas, seg->area_count * sizeof(*seg->areas));
+
+ seg->areas = newareas;
+ seg->area_count = new_area_count;
+
+ return 1;
+}
+
+/*
+ * Reduce the size of an lv_segment. New size can be zero.
+ */
+static int _lv_segment_reduce(struct lv_segment *seg, uint32_t reduction)
+{
+ uint32_t area_reduction, s;
+
+ /* Caller must ensure exact divisibility */
+ if (seg_is_striped(seg)) {
+ if (reduction % seg->area_count) {
+ log_error("Segment extent reduction %" PRIu32
+ "not divisible by #stripes %" PRIu32,
+ reduction, seg->area_count);
+ return 0;
+ }
+ area_reduction = (reduction / seg->area_count);
+ } else
+ area_reduction = reduction;
+
+ for (s = 0; s < seg->area_count; s++)
+ release_lv_segment_area(seg, s, area_reduction);
+
+ seg->len -= reduction;
+ seg->area_len -= area_reduction;
+
+ return 1;
+}
+
+/*
+ * Entry point for all LV reductions in size.
+ */
+static int _lv_reduce(struct logical_volume *lv, uint32_t extents, int delete)
+{
+ struct lv_segment *seg;
+ uint32_t count = extents;
+ uint32_t reduction;
+
+ dm_list_iterate_back_items(seg, &lv->segments) {
+ if (!count)
+ break;
+
+ if (seg->len <= count) {
+ /* remove this segment completely */
+ /* FIXME Check this is safe */
+ if (seg->log_lv && !lv_remove(seg->log_lv))
+ return_0;
+ dm_list_del(&seg->list);
+ reduction = seg->len;
+ } else
+ reduction = count;
+
+ if (!_lv_segment_reduce(seg, reduction))
+ return_0;
+ count -= reduction;
+ }
+
+ lv->le_count -= extents;
+ lv->size = (uint64_t) lv->le_count * lv->vg->extent_size;
+
+ if (!delete)
+ return 1;
+
+ /* Remove the LV if it is now empty */
+ if (!lv->le_count && !unlink_lv_from_vg(lv))
+ return_0;
+ else if (lv->vg->fid->fmt->ops->lv_setup &&
+ !lv->vg->fid->fmt->ops->lv_setup(lv->vg->fid, lv))
+ return_0;
+
+ return 1;
+}
+
+/*
+ * Empty an LV.
+ */
+int lv_empty(struct logical_volume *lv)
+{
+ return _lv_reduce(lv, lv->le_count, 0);
+}
+
+/*
+ * Empty an LV and add error segment.
+ */
+int replace_lv_with_error_segment(struct logical_volume *lv)
+{
+ uint32_t len = lv->le_count;
+
+ if (!lv_empty(lv))
+ return_0;
+
+ /*
+ * Since we are replacing the whatever-was-there with
+ * an error segment, we should also clear any flags
+ * that suggest it is anything other than "error".
+ */
+ lv->status &= ~MIRRORED;
+
+ /* FIXME: Should we bug if we find a log_lv attached? */
+
+ if (!lv_add_virtual_segment(lv, 0, len,
+ get_segtype_from_string(lv->vg->cmd,
+ "error")))
+ return_0;
+
+ return 1;
+}
+
+/*
+ * Remove given number of extents from LV.
+ */
+int lv_reduce(struct logical_volume *lv, uint32_t extents)
+{
+ return _lv_reduce(lv, extents, 1);
+}
+
+/*
+ * Completely remove an LV.
+ */
+int lv_remove(struct logical_volume *lv)
+{
+
+ if (!lv_reduce(lv, lv->le_count))
+ return_0;
+
+ return 1;
+}
+
+/*
+ * A set of contiguous physical extents allocated
+ */
+struct alloced_area {
+ struct dm_list list;
+
+ struct physical_volume *pv;
+ uint32_t pe;
+ uint32_t len;
+};
+
+/*
+ * Details of an allocation attempt
+ */
+struct alloc_handle {
+ struct cmd_context *cmd;
+ struct dm_pool *mem;
+
+ alloc_policy_t alloc; /* Overall policy */
+ uint32_t new_extents; /* Number of new extents required */
+ uint32_t area_count; /* Number of parallel areas */
+ uint32_t area_multiple; /* seg->len = area_len * area_multiple */
+ uint32_t log_area_count; /* Number of parallel logs */
+ uint32_t log_len; /* Length of log */
+ uint32_t region_size; /* Mirror region size */
+ uint32_t total_area_len; /* Total number of parallel extents */
+
+ const struct config_node *cling_tag_list_cn;
+
+ struct dm_list *parallel_areas; /* PVs to avoid */
+
+ /*
+ * Contains area_count lists of areas allocated to data stripes
+ * followed by log_area_count lists of areas allocated to log stripes.
+ */
+ struct dm_list alloced_areas[0];
+};
+
+static uint32_t _calc_area_multiple(const struct segment_type *segtype,
+ const uint32_t area_count, const uint32_t stripes)
+{
+ if (!area_count)
+ return 1;
+
+ /* Striped */
+ if (segtype_is_striped(segtype))
+ return area_count;
+
+ /* Mirrored stripes */
+ if (stripes)
+ return stripes;
+
+ /* Mirrored */
+ return 1;
+}
+
+/*
+ * Returns log device size in extents, algorithm from kernel code
+ */
+#define BYTE_SHIFT 3
+static uint32_t mirror_log_extents(uint32_t region_size, uint32_t pe_size, uint32_t area_len)
+{
+ size_t area_size, bitset_size, log_size, region_count;
+
+ area_size = area_len * pe_size;
+ region_count = dm_div_up(area_size, region_size);
+
+ /* Work out how many "unsigned long"s we need to hold the bitset. */
+ bitset_size = dm_round_up(region_count, sizeof(uint32_t) << BYTE_SHIFT);
+ bitset_size >>= BYTE_SHIFT;
+
+ /* Log device holds both header and bitset. */
+ log_size = dm_round_up((MIRROR_LOG_OFFSET << SECTOR_SHIFT) + bitset_size, 1 << SECTOR_SHIFT);
+ log_size >>= SECTOR_SHIFT;
+
+ return dm_div_up(log_size, pe_size);
+}
+
+/*
+ * Preparation for a specific allocation attempt
+ * stripes and mirrors refer to the parallel areas used for data.
+ * If log_area_count > 1 it is always mirrored (not striped).
+ */
+static struct alloc_handle *_alloc_init(struct cmd_context *cmd,
+ struct dm_pool *mem,
+ const struct segment_type *segtype,
+ alloc_policy_t alloc,
+ uint32_t new_extents,
+ uint32_t mirrors,
+ uint32_t stripes,
+ uint32_t log_area_count,
+ uint32_t extent_size,
+ uint32_t region_size,
+ struct dm_list *parallel_areas)
+{
+ struct alloc_handle *ah;
+ uint32_t s, area_count;
+
+ /* FIXME Caller should ensure this */
+ if (mirrors && !stripes)
+ stripes = 1;
+
+ if (segtype_is_virtual(segtype))
+ area_count = 0;
+ else if (mirrors > 1)
+ area_count = mirrors * stripes;
+ else
+ area_count = stripes;
+
+ if (!(ah = dm_pool_zalloc(mem, sizeof(*ah) + sizeof(ah->alloced_areas[0]) * (area_count + log_area_count)))) {
+ log_error("allocation handle allocation failed");
+ return NULL;
+ }
+
+ ah->cmd = cmd;
+
+ if (segtype_is_virtual(segtype))
+ return ah;
+
+ if (!(area_count + log_area_count)) {
+ log_error(INTERNAL_ERROR "_alloc_init called for non-virtual segment with no disk space.");
+ return NULL;
+ }
+
+ if (!(ah->mem = dm_pool_create("allocation", 1024))) {
+ log_error("allocation pool creation failed");
+ return NULL;
+ }
+
+ ah->new_extents = new_extents;
+ ah->area_count = area_count;
+ ah->log_area_count = log_area_count;
+ ah->region_size = region_size;
+ ah->alloc = alloc;
+ ah->area_multiple = _calc_area_multiple(segtype, area_count, stripes);
+
+ ah->log_len = log_area_count ? mirror_log_extents(ah->region_size, extent_size, ah->new_extents / ah->area_multiple) : 0;
+
+ for (s = 0; s < ah->area_count + ah->log_area_count; s++)
+ dm_list_init(&ah->alloced_areas[s]);
+
+ ah->parallel_areas = parallel_areas;
+
+ ah->cling_tag_list_cn = find_config_tree_node(cmd, "allocation/cling_tag_list");
+
+ return ah;
+}
+
+void alloc_destroy(struct alloc_handle *ah)
+{
+ if (ah->mem)
+ dm_pool_destroy(ah->mem);
+}
+
+static int _log_parallel_areas(struct dm_pool *mem, struct dm_list *parallel_areas)
+{
+ struct seg_pvs *spvs;
+ struct pv_list *pvl;
+ char *pvnames;
+
+ if (!parallel_areas)
+ return 1;
+
+ dm_list_iterate_items(spvs, parallel_areas) {
+ if (!dm_pool_begin_object(mem, 256)) {
+ log_error("dm_pool_begin_object failed");
+ return 0;
+ }
+
+ dm_list_iterate_items(pvl, &spvs->pvs) {
+ if (!dm_pool_grow_object(mem, pv_dev_name(pvl->pv), strlen(pv_dev_name(pvl->pv)))) {
+ log_error("dm_pool_grow_object failed");
+ dm_pool_abandon_object(mem);
+ return 0;
+ }
+ if (!dm_pool_grow_object(mem, " ", 1)) {
+ log_error("dm_pool_grow_object failed");
+ dm_pool_abandon_object(mem);
+ return 0;
+ }
+ }
+
+ if (!dm_pool_grow_object(mem, "\0", 1)) {
+ log_error("dm_pool_grow_object failed");
+ dm_pool_abandon_object(mem);
+ return 0;
+ }
+
+ pvnames = dm_pool_end_object(mem);
+ log_debug("Parallel PVs at LE %" PRIu32 " length %" PRIu32 ": %s",
+ spvs->le, spvs->len, pvnames);
+ dm_pool_free(mem, pvnames);
+ }
+
+ return 1;
+}
+
+static int _setup_alloced_segment(struct logical_volume *lv, uint64_t status,
+ uint32_t area_count,
+ uint32_t stripe_size,
+ const struct segment_type *segtype,
+ struct alloced_area *aa,
+ uint32_t region_size)
+{
+ uint32_t s, extents, area_multiple;
+ struct lv_segment *seg;
+
+ area_multiple = _calc_area_multiple(segtype, area_count, 0);
+
+ if (!(seg = alloc_lv_segment(lv->vg->cmd->mem, segtype, lv,
+ lv->le_count,
+ aa[0].len * area_multiple,
+ status, stripe_size, NULL,
+ area_count,
+ aa[0].len, 0u, region_size, 0u, NULL))) {
+ log_error("Couldn't allocate new LV segment.");
+ return 0;
+ }
+
+ for (s = 0; s < area_count; s++)
+ if (!set_lv_segment_area_pv(seg, s, aa[s].pv, aa[s].pe))
+ return_0;
+
+ dm_list_add(&lv->segments, &seg->list);
+
+ extents = aa[0].len * area_multiple;
+ lv->le_count += extents;
+ lv->size += (uint64_t) extents *lv->vg->extent_size;
+
+ if (segtype_is_mirrored(segtype))
+ lv->status |= MIRRORED;
+
+ return 1;
+}
+
+static int _setup_alloced_segments(struct logical_volume *lv,
+ struct dm_list *alloced_areas,
+ uint32_t area_count,
+ uint64_t status,
+ uint32_t stripe_size,
+ const struct segment_type *segtype,
+ uint32_t region_size)
+{
+ struct alloced_area *aa;
+
+ dm_list_iterate_items(aa, &alloced_areas[0]) {
+ if (!_setup_alloced_segment(lv, status, area_count,
+ stripe_size, segtype, aa,
+ region_size))
+ return_0;
+ }
+
+ return 1;
+}
+
+/*
+ * This function takes a list of pv_areas and adds them to allocated_areas.
+ * If the complete area is not needed then it gets split.
+ * The part used is removed from the pv_map so it can't be allocated twice.
+ */
+static int _alloc_parallel_area(struct alloc_handle *ah, uint32_t needed,
+ struct pv_area_used *areas, uint32_t *allocated,
+ unsigned log_needs_allocating, uint32_t ix_log_offset)
+{
+ uint32_t area_len, len, remaining;
+ uint32_t s;
+ uint32_t ix_log_skip = 0; /* How many areas to skip in middle of array to reach log areas */
+ uint32_t total_area_count = ah->area_count + (log_needs_allocating ? ah->log_area_count : 0);
+ struct alloced_area *aa;
+
+ if (!total_area_count) {
+ log_error(INTERNAL_ERROR "_alloc_parallel_area called without any allocation to do.");
+ return 1;
+ }
+
+ remaining = needed - *allocated;
+ area_len = remaining / ah->area_multiple;
+
+ /* Reduce area_len to the smallest of the areas */
+ for (s = 0; s < ah->area_count; s++)
+ if (area_len > areas[s].used)
+ area_len = areas[s].used;
+
+ if (!(aa = dm_pool_alloc(ah->mem, sizeof(*aa) * total_area_count))) {
+ log_error("alloced_area allocation failed");
+ return 0;
+ }
+
+ /*
+ * Areas consists of area_count areas for data stripes, then
+ * ix_log_skip areas to skip, then log_area_count areas to use for the
+ * log, then some areas too small for the log.
+ */
+ len = area_len;
+ for (s = 0; s < total_area_count; s++) {
+ if (s == ah->area_count) {
+ ix_log_skip = ix_log_offset - ah->area_count;
+ len = ah->log_len;
+ }
+
+ aa[s].pv = areas[s + ix_log_skip].pva->map->pv;
+ aa[s].pe = areas[s + ix_log_skip].pva->start;
+ aa[s].len = len;
+
+ log_debug("Allocating parallel area %" PRIu32
+ " on %s start PE %" PRIu32 " length %" PRIu32 ".",
+ s, dev_name(aa[s].pv->dev), aa[s].pe, len);
+
+ consume_pv_area(areas[s + ix_log_skip].pva, len);
+
+ dm_list_add(&ah->alloced_areas[s], &aa[s].list);
+ }
+
+ ah->total_area_len += area_len;
+
+ *allocated += area_len * ah->area_multiple;
+
+ return 1;
+}
+
+/* For striped mirrors, all the areas are counted, through the mirror layer */
+static uint32_t _stripes_per_mimage(struct lv_segment *seg)
+{
+ struct lv_segment *last_lvseg;
+
+ if (seg_is_mirrored(seg) && seg->area_count && seg_type(seg, 0) == AREA_LV) {
+ last_lvseg = dm_list_item(dm_list_last(&seg_lv(seg, 0)->segments), struct lv_segment);
+ if (seg_is_striped(last_lvseg))
+ return last_lvseg->area_count;
+ }
+
+ return 1;
+}
+
+/*
+ * Call fn for each AREA_PV used by the LV segment at lv:le of length *max_seg_len.
+ * If any constituent area contains more than one segment, max_seg_len is
+ * reduced to cover only the first.
+ * fn should return 0 on error, 1 to continue scanning or >1 to terminate without error.
+ * In the last case, this function passes on the return code.
+ */
+static int _for_each_pv(struct cmd_context *cmd, struct logical_volume *lv,
+ uint32_t le, uint32_t len, struct lv_segment *seg,
+ uint32_t *max_seg_len,
+ uint32_t first_area, uint32_t max_areas,
+ int top_level_area_index,
+ int only_single_area_segments,
+ int (*fn)(struct cmd_context *cmd,
+ struct pv_segment *peg, uint32_t s,
+ void *data),
+ void *data)
+{
+ uint32_t s;
+ uint32_t remaining_seg_len, area_len, area_multiple;
+ uint32_t stripes_per_mimage = 1;
+ int r = 1;
+
+ if (!seg && !(seg = find_seg_by_le(lv, le))) {
+ log_error("Failed to find segment for %s extent %" PRIu32,
+ lv->name, le);
+ return 0;
+ }
+
+ /* Remaining logical length of segment */
+ remaining_seg_len = seg->len - (le - seg->le);
+
+ if (remaining_seg_len > len)
+ remaining_seg_len = len;
+
+ if (max_seg_len && *max_seg_len > remaining_seg_len)
+ *max_seg_len = remaining_seg_len;
+
+ area_multiple = _calc_area_multiple(seg->segtype, seg->area_count, 0);
+ area_len = remaining_seg_len / area_multiple ? : 1;
+
+ /* For striped mirrors, all the areas are counted, through the mirror layer */
+ if (top_level_area_index == -1)
+ stripes_per_mimage = _stripes_per_mimage(seg);
+
+ for (s = first_area;
+ s < seg->area_count && (!max_areas || s <= max_areas);
+ s++) {
+ if (seg_type(seg, s) == AREA_LV) {
+ if (!(r = _for_each_pv(cmd, seg_lv(seg, s),
+ seg_le(seg, s) +
+ (le - seg->le) / area_multiple,
+ area_len, NULL, max_seg_len, 0,
+ (stripes_per_mimage == 1) && only_single_area_segments ? 1U : 0U,
+ top_level_area_index != -1 ? top_level_area_index : (int) s * stripes_per_mimage,
+ only_single_area_segments, fn,
+ data)))
+ stack;
+ } else if (seg_type(seg, s) == AREA_PV)
+ if (!(r = fn(cmd, seg_pvseg(seg, s), top_level_area_index != -1 ? (uint32_t) top_level_area_index + s : s, data)))
+ stack;
+ if (r != 1)
+ return r;
+ }
+
+ /* FIXME only_single_area_segments used as workaround to skip log LV - needs new param? */
+ if (!only_single_area_segments && seg_is_mirrored(seg) && seg->log_lv) {
+ if (!(r = _for_each_pv(cmd, seg->log_lv, 0, seg->log_lv->le_count, NULL,
+ NULL, 0, 0, 0, only_single_area_segments,
+ fn, data)))
+ stack;
+ if (r != 1)
+ return r;
+ }
+
+ /* FIXME Add snapshot cow LVs etc. */
+
+ return 1;
+}
+
+static int _comp_area(const void *l, const void *r)
+{
+ const struct pv_area_used *lhs = (const struct pv_area_used *) l;
+ const struct pv_area_used *rhs = (const struct pv_area_used *) r;
+
+ if (lhs->used < rhs->used)
+ return 1;
+
+ else if (lhs->used > rhs->used)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Search for pvseg that matches condition
+ */
+struct pv_match {
+ int (*condition)(struct pv_match *pvmatch, struct pv_segment *pvseg, struct pv_area *pva);
+
+ struct pv_area_used *areas;
+ struct pv_area *pva;
+ uint32_t areas_size;
+ const struct config_node *cling_tag_list_cn;
+ int s; /* Area index of match */
+};
+
+/*
+ * Is PV area on the same PV?
+ */
+static int _is_same_pv(struct pv_match *pvmatch __attribute((unused)), struct pv_segment *pvseg, struct pv_area *pva)
+{
+ if (pvseg->pv != pva->map->pv)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Does PV area have a tag listed in allocation/cling_tag_list that
+ * matches a tag of the PV of the existing segment?
+ */
+static int _has_matching_pv_tag(struct pv_match *pvmatch, struct pv_segment *pvseg, struct pv_area *pva)
+{
+ const struct config_value *cv;
+ const char *str;
+ const char *tag_matched;
+
+ for (cv = pvmatch->cling_tag_list_cn->v; cv; cv = cv->next) {
+ if (cv->type != CFG_STRING) {
+ log_error("Ignoring invalid string in config file entry "
+ "allocation/cling_tag_list");
+ continue;
+ }
+ str = cv->v.str;
+ if (!*str) {
+ log_error("Ignoring empty string in config file entry "
+ "allocation/cling_tag_list");
+ continue;
+ }
+
+ if (*str != '@') {
+ log_error("Ignoring string not starting with @ in config file entry "
+ "allocation/cling_tag_list: %s", str);
+ continue;
+ }
+
+ str++;
+
+ if (!*str) {
+ log_error("Ignoring empty tag in config file entry "
+ "allocation/cling_tag_list");
+ continue;
+ }
+
+ /* Wildcard matches any tag against any tag. */
+ if (!strcmp(str, "*")) {
+ if (!str_list_match_list(&pvseg->pv->tags, &pva->map->pv->tags, &tag_matched))
+ continue;
+ else {
+ log_debug("Matched allocation PV tag %s on existing %s with free space on %s.",
+ tag_matched, pv_dev_name(pvseg->pv), pv_dev_name(pva->map->pv));
+ return 1;
+ }
+ }
+
+ if (!str_list_match_item(&pvseg->pv->tags, str) ||
+ !str_list_match_item(&pva->map->pv->tags, str))
+ continue;
+ else {
+ log_debug("Matched allocation PV tag %s on existing %s with free space on %s.",
+ str, pv_dev_name(pvseg->pv), pv_dev_name(pva->map->pv));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Is PV area contiguous to PV segment?
+ */
+static int _is_contiguous(struct pv_match *pvmatch __attribute((unused)), struct pv_segment *pvseg, struct pv_area *pva)
+{
+ if (pvseg->pv != pva->map->pv)
+ return 0;
+
+ if (pvseg->pe + pvseg->len != pva->start)
+ return 0;
+
+ return 1;
+}
+
+static int _is_condition(struct cmd_context *cmd __attribute__((unused)),
+ struct pv_segment *pvseg, uint32_t s,
+ void *data)
+{
+ struct pv_match *pvmatch = data;
+
+ if (!pvmatch->condition(pvmatch, pvseg, pvmatch->pva))
+ return 1; /* Continue */
+
+ if (s >= pvmatch->areas_size)
+ return 1;
+
+ /*
+ * Only used for cling and contiguous policies so it's safe to say all
+ * the available space is used.
+ */
+ pvmatch->areas[s].pva = pvmatch->pva;
+ pvmatch->areas[s].used = pvmatch->pva->count;
+
+ log_debug("Trying allocation area %" PRIu32 " on %s start PE %" PRIu32
+ " length %" PRIu32 ".",
+ s, dev_name(pvmatch->pva->map->pv->dev), pvmatch->pva->start,
+ pvmatch->pva->count);
+
+ return 2; /* Finished */
+}
+
+/*
+ * Is pva on same PV as any existing areas?
+ */
+static int _check_cling(struct cmd_context *cmd,
+ const struct config_node *cling_tag_list_cn,
+ struct lv_segment *prev_lvseg, struct pv_area *pva,
+ struct pv_area_used *areas, uint32_t areas_size)
+{
+ struct pv_match pvmatch;
+ int r;
+
+ pvmatch.condition = cling_tag_list_cn ? _has_matching_pv_tag : _is_same_pv;
+ pvmatch.areas = areas;
+ pvmatch.areas_size = areas_size;
+ pvmatch.pva = pva;
+ pvmatch.cling_tag_list_cn = cling_tag_list_cn;
+
+ /* FIXME Cope with stacks by flattening */
+ if (!(r = _for_each_pv(cmd, prev_lvseg->lv,
+ prev_lvseg->le + prev_lvseg->len - 1, 1, NULL, NULL,
+ 0, 0, -1, 1,
+ _is_condition, &pvmatch)))
+ stack;
+
+ if (r != 2)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Is pva contiguous to any existing areas or on the same PV?
+ */
+static int _check_contiguous(struct cmd_context *cmd,
+ struct lv_segment *prev_lvseg, struct pv_area *pva,
+ struct pv_area_used *areas, uint32_t areas_size)
+{
+ struct pv_match pvmatch;
+ int r;
+
+ pvmatch.condition = _is_contiguous;
+ pvmatch.areas = areas;
+ pvmatch.areas_size = areas_size;
+ pvmatch.pva = pva;
+ pvmatch.cling_tag_list_cn = NULL;
+
+ /* FIXME Cope with stacks by flattening */
+ if (!(r = _for_each_pv(cmd, prev_lvseg->lv,
+ prev_lvseg->le + prev_lvseg->len - 1, 1, NULL, NULL,
+ 0, 0, -1, 1,
+ _is_condition, &pvmatch)))
+ stack;
+
+ if (r != 2)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Choose sets of parallel areas to use, respecting any constraints.
+ */
+static int _find_parallel_space(struct alloc_handle *ah, alloc_policy_t alloc,
+ struct dm_list *pvms, struct pv_area_used **areas_ptr,
+ uint32_t *areas_size_ptr, unsigned can_split,
+ struct lv_segment *prev_lvseg,
+ uint32_t *allocated, uint32_t *log_needs_allocating, uint32_t needed)
+{
+ struct pv_map *pvm;
+ struct pv_area *pva;
+ struct pv_list *pvl;
+ unsigned already_found_one = 0;
+ unsigned contiguous = 0, cling = 0, use_cling_tags = 0, preferred_count = 0;
+ unsigned ix, last_ix;
+ unsigned ix_offset = 0; /* Offset for non-preferred allocations */
+ unsigned ix_log_offset; /* Offset to start of areas to use for log */
+ unsigned too_small_for_log_count; /* How many too small for log? */
+ uint32_t max_parallel; /* Maximum extents to allocate */
+ uint32_t next_le;
+ uint32_t required; /* Extents we're trying to obtain from a given area */
+ struct seg_pvs *spvs;
+ struct dm_list *parallel_pvs;
+ uint32_t free_pes;
+ struct alloced_area *aa;
+ uint32_t s;
+ uint32_t total_extents_needed = (needed - *allocated) * ah->area_count / ah->area_multiple;
+
+ /* Is there enough total space? */
+ free_pes = pv_maps_size(pvms);
+ if (total_extents_needed > free_pes) {
+ log_error("Insufficient free space: %" PRIu32 " extents needed,"
+ " but only %" PRIu32 " available",
+ total_extents_needed, free_pes);
+ return 0;
+ }
+
+ /* FIXME Select log PV appropriately if there isn't one yet */
+
+ /* Are there any preceding segments we must follow on from? */
+ if (prev_lvseg) {
+ ix_offset = _stripes_per_mimage(prev_lvseg) * prev_lvseg->area_count;
+ if ((alloc == ALLOC_CONTIGUOUS))
+ contiguous = 1;
+ else if ((alloc == ALLOC_CLING))
+ cling = 1;
+ else if ((alloc == ALLOC_CLING_BY_TAGS)) {
+ cling = 1;
+ use_cling_tags = 1;
+ } else
+ ix_offset = 0;
+ }
+
+ /* FIXME This algorithm needs a lot of cleaning up! */
+ /* FIXME anywhere doesn't find all space yet */
+ /* ix_offset holds the number of allocations that must be contiguous */
+ /* ix holds the number of areas found on other PVs */
+ do {
+ ix = 0;
+ preferred_count = 0;
+
+ parallel_pvs = NULL;
+ max_parallel = needed;
+
+ /*
+ * If there are existing parallel PVs, avoid them and reduce
+ * the maximum we can allocate in one go accordingly.
+ */
+ if (ah->parallel_areas) {
+ next_le = (prev_lvseg ? prev_lvseg->le + prev_lvseg->len : 0) + *allocated / ah->area_multiple;
+ dm_list_iterate_items(spvs, ah->parallel_areas) {
+ if (next_le >= spvs->le + spvs->len)
+ continue;
+
+ if (max_parallel > (spvs->le + spvs->len) * ah->area_multiple)
+ max_parallel = (spvs->le + spvs->len) * ah->area_multiple;
+ parallel_pvs = &spvs->pvs;
+ break;
+ }
+ }
+
+ do {
+ /*
+ * Provide for escape from the loop if no progress is made.
+ * This should not happen: ALLOC_ANYWHERE should be able to use
+ * all available space. (If there aren't enough extents, the code
+ * should not reach this point.)
+ */
+ last_ix = ix;
+
+ /*
+ * Put the smallest area of each PV that is at least the
+ * size we need into areas array. If there isn't one
+ * that fits completely and we're allowed more than one
+ * LV segment, then take the largest remaining instead.
+ */
+ dm_list_iterate_items(pvm, pvms) {
+ if (dm_list_empty(&pvm->areas))
+ continue; /* Next PV */
+
+ if (alloc != ALLOC_ANYWHERE) {
+ /* Don't allocate onto the log pv */
+ if (ah->log_area_count)
+ dm_list_iterate_items(aa, &ah->alloced_areas[ah->area_count])
+ for (s = 0; s < ah->log_area_count; s++)
+ if (!aa[s].pv)
+ goto next_pv;
+
+ /* Avoid PVs used by existing parallel areas */
+ if (parallel_pvs)
+ dm_list_iterate_items(pvl, parallel_pvs)
+ if (pvm->pv == pvl->pv)
+ goto next_pv;
+ }
+
+ already_found_one = 0;
+ /* First area in each list is the largest */
+ dm_list_iterate_items(pva, &pvm->areas) {
+ /* Skip fully-reserved areas (which are not currently removed from the list). */
+ if (!pva->unreserved)
+ continue;
+ if (contiguous) {
+ if (prev_lvseg &&
+ _check_contiguous(ah->cmd,
+ prev_lvseg,
+ pva, *areas_ptr,
+ *areas_size_ptr)) {
+ preferred_count++;
+ goto next_pv;
+ }
+ continue;
+ }
+
+ if (cling) {
+ if (prev_lvseg &&
+ _check_cling(ah->cmd,
+ use_cling_tags ? ah->cling_tag_list_cn : NULL,
+ prev_lvseg,
+ pva, *areas_ptr,
+ *areas_size_ptr)) {
+ preferred_count++;
+ }
+ goto next_pv;
+ }
+
+ /* Is it big enough on its own? */
+ if (pva->unreserved * ah->area_multiple <
+ max_parallel - *allocated &&
+ ((!can_split && !ah->log_area_count) ||
+ (already_found_one &&
+ !(alloc == ALLOC_ANYWHERE))))
+ goto next_pv;
+
+ /*
+ * Except with ALLOC_ANYWHERE, replace first area with this
+ * one which is smaller but still big enough.
+ */
+ if (!already_found_one ||
+ alloc == ALLOC_ANYWHERE) {
+ ix++;
+ already_found_one = 1;
+ }
+
+ required = (max_parallel - *allocated) / ah->area_multiple;
+
+ if (alloc == ALLOC_ANYWHERE) {
+ /*
+ * Update amount unreserved - effectively splitting an area
+ * into two or more parts. If the whole stripe doesn't fit,
+ * reduce amount we're looking for.
+ */
+ if (ix + ix_offset - 1 >= ah->area_count)
+ required = ah->log_len;
+ if (required >= pva->unreserved) {
+ required = pva->unreserved;
+ pva->unreserved = 0;
+ } else {
+ pva->unreserved -= required;
+ reinsert_reduced_pv_area(pva);
+ }
+ } else {
+ if (required < ah->log_len)
+ required = ah->log_len;
+ if (required > pva->count)
+ required = pva->count;
+ }
+
+ /* Expand areas array if needed after an area was split. */
+ if (ix + ix_offset > *areas_size_ptr) {
+ *areas_size_ptr *= 2;
+ if (!(*areas_ptr = dm_realloc(*areas_ptr,
+ sizeof(**areas_ptr) *
+ (*areas_size_ptr)))) {
+ log_error("Memory reallocation for parallel areas failed.");
+ return 0;
+ }
+ }
+ (*areas_ptr)[ix + ix_offset - 1].pva = pva;
+ (*areas_ptr)[ix + ix_offset - 1].used = required;
+ log_debug("Trying allocation area %" PRIu32 " on %s start PE %" PRIu32
+ " length %" PRIu32 " leaving %" PRIu32 ".",
+ ix + ix_offset - 1, dev_name(pva->map->pv->dev), pva->start, required,
+ (alloc == ALLOC_ANYWHERE) ? pva->unreserved : pva->count - required);
+ }
+ next_pv:
+ /* With ALLOC_ANYWHERE we ignore further PVs once we have at least enough areas */
+ /* With cling and contiguous we stop if we found a match for *all* the areas */
+ /* FIXME Rename these variables! */
+ if ((alloc == ALLOC_ANYWHERE &&
+ ix + ix_offset >= ah->area_count + (*log_needs_allocating ? ah->log_area_count : 0)) ||
+ (preferred_count == ix_offset &&
+ (ix_offset == ah->area_count + (*log_needs_allocating ? ah->log_area_count : 0))))
+ break;
+ }
+ } while (alloc == ALLOC_ANYWHERE && last_ix != ix && ix < ah->area_count + (*log_needs_allocating ? ah->log_area_count : 0));
+
+ if (preferred_count < ix_offset)
+ break;
+
+ if (ix + ix_offset < ah->area_count +
+ (*log_needs_allocating ? ah->log_area_count : 0))
+ break;
+
+ /* Sort the areas so we allocate from the biggest */
+ if (ix > 1)
+ qsort((*areas_ptr) + ix_offset, ix, sizeof(**areas_ptr),
+ _comp_area);
+
+ /*
+ * First time around, if there's a log, allocate it on the
+ * smallest device that has space for it.
+ */
+ too_small_for_log_count = 0;
+ ix_log_offset = 0;
+
+ /* FIXME This logic is due to its heritage and can be simplified! */
+ if (*log_needs_allocating) {
+ /* How many areas are too small for the log? */
+ while (too_small_for_log_count < ix_offset + ix &&
+ (*((*areas_ptr) + ix_offset + ix - 1 -
+ too_small_for_log_count)).used < ah->log_len)
+ too_small_for_log_count++;
+ ix_log_offset = ix_offset + ix - too_small_for_log_count - ah->log_area_count;
+ }
+
+ if (ix + ix_offset < ah->area_count +
+ (*log_needs_allocating ? ah->log_area_count +
+ too_small_for_log_count : 0))
+ break;
+
+ if (!_alloc_parallel_area(ah, max_parallel, *areas_ptr, allocated,
+ *log_needs_allocating, ix_log_offset))
+ return_0;
+
+ *log_needs_allocating = 0;
+
+ } while ((alloc != ALLOC_CONTIGUOUS) && *allocated != needed && can_split);
+
+ return 1;
+}
+
+/*
+ * Allocate several segments, each the same size, in parallel.
+ * If mirrored_pv and mirrored_pe are supplied, it is used as
+ * the first area, and additional areas are allocated parallel to it.
+ */
+static int _allocate(struct alloc_handle *ah,
+ struct volume_group *vg,
+ struct logical_volume *lv,
+ unsigned can_split,
+ struct dm_list *allocatable_pvs)
+{
+ struct pv_area_used *areas;
+ uint32_t allocated = lv ? lv->le_count : 0;
+ uint32_t old_allocated;
+ struct lv_segment *prev_lvseg = NULL;
+ int r = 0;
+ struct dm_list *pvms;
+ uint32_t areas_size;
+ alloc_policy_t alloc;
+ unsigned log_needs_allocating = 0;
+
+ if (allocated >= ah->new_extents && !ah->log_area_count) {
+ log_error("_allocate called with no work to do!");
+ return 1;
+ }
+
+ if (ah->log_area_count)
+ log_needs_allocating = 1;
+
+ if (ah->alloc == ALLOC_CONTIGUOUS)
+ can_split = 0;
+
+ if (lv && !dm_list_empty(&lv->segments))
+ prev_lvseg = dm_list_item(dm_list_last(&lv->segments),
+ struct lv_segment);
+ /*
+ * Build the sets of available areas on the pv's.
+ */
+ if (!(pvms = create_pv_maps(ah->mem, vg, allocatable_pvs)))
+ return_0;
+
+ if (!_log_parallel_areas(ah->mem, ah->parallel_areas))
+ stack;
+
+ areas_size = dm_list_size(pvms);
+ if (areas_size && areas_size < (ah->area_count + ah->log_area_count)) {
+ if (ah->alloc != ALLOC_ANYWHERE) {
+ log_error("Not enough PVs with free space available "
+ "for parallel allocation.");
+ log_error("Consider --alloc anywhere if desperate.");
+ return 0;
+ }
+ areas_size = ah->area_count + ah->log_area_count;
+ }
+
+ /* Upper bound if none of the PVs in prev_lvseg is in pvms */
+ /* FIXME Work size out properly */
+ if (prev_lvseg)
+ areas_size += _stripes_per_mimage(prev_lvseg) * prev_lvseg->area_count;
+
+ /* Allocate an array of pv_areas to hold the largest space on each PV */
+ if (!(areas = dm_malloc(sizeof(*areas) * areas_size))) {
+ log_error("Couldn't allocate areas array.");
+ return 0;
+ }
+
+ /*
+ * cling includes implicit cling_by_tags
+ * but it does nothing unless the lvm.conf setting is present.
+ */
+ if (ah->alloc == ALLOC_CLING)
+ ah->alloc = ALLOC_CLING_BY_TAGS;
+
+ /* Attempt each defined allocation policy in turn */
+ for (alloc = ALLOC_CONTIGUOUS; alloc < ALLOC_INHERIT; alloc++) {
+ /* Skip cling_by_tags if no list defined */
+ if (alloc == ALLOC_CLING_BY_TAGS && !ah->cling_tag_list_cn)
+ continue;
+ old_allocated = allocated;
+ log_debug("Trying allocation using %s policy. "
+ "Need %" PRIu32 " extents for %" PRIu32 " parallel areas and %" PRIu32 " log areas of %" PRIu32 " extents. "
+ "(Total %" PRIu32 " extents.)",
+ get_alloc_string(alloc),
+ (ah->new_extents - allocated) / ah->area_multiple,
+ ah->area_count, log_needs_allocating ? ah->log_area_count : 0,
+ log_needs_allocating ? ah->log_len : 0,
+ (ah->new_extents - allocated) * ah->area_count / ah->area_multiple +
+ (log_needs_allocating ? ah->log_area_count * ah->log_len : 0));
+ if (!_find_parallel_space(ah, alloc, pvms, &areas,
+ &areas_size, can_split,
+ prev_lvseg, &allocated, &log_needs_allocating, ah->new_extents))
+ goto_out;
+ if ((allocated == ah->new_extents && !log_needs_allocating) || (ah->alloc == alloc) ||
+ (!can_split && (allocated != old_allocated)))
+ break;
+ }
+
+ if (allocated != ah->new_extents) {
+ log_error("Insufficient suitable %sallocatable extents "
+ "for logical volume %s: %u more required",
+ can_split ? "" : "contiguous ",
+ lv ? lv->name : "",
+ (ah->new_extents - allocated) * ah->area_count
+ / ah->area_multiple);
+ goto out;
+ }
+
+ if (log_needs_allocating) {
+ log_error("Insufficient extents for log allocation "
+ "for logical volume %s.",
+ lv ? lv->name : "");
+ goto out;
+ }
+
+ r = 1;
+
+ out:
+ dm_free(areas);
+ return r;
+}
+
+int lv_add_virtual_segment(struct logical_volume *lv, uint64_t status,
+ uint32_t extents, const struct segment_type *segtype)
+{
+ struct lv_segment *seg;
+
+ if (!(seg = alloc_lv_segment(lv->vg->cmd->mem, segtype, lv,
+ lv->le_count, extents, status, 0,
+ NULL, 0, extents, 0, 0, 0, NULL))) {
+ log_error("Couldn't allocate new zero segment.");
+ return 0;
+ }
+
+ dm_list_add(&lv->segments, &seg->list);
+
+ lv->le_count += extents;
+ lv->size += (uint64_t) extents *lv->vg->extent_size;
+
+ lv->status |= VIRTUAL;
+
+ return 1;
+}
+
+/*
+ * Entry point for all extent allocations.
+ */
+struct alloc_handle *allocate_extents(struct volume_group *vg,
+ struct logical_volume *lv,
+ const struct segment_type *segtype,
+ uint32_t stripes,
+ uint32_t mirrors, uint32_t log_count,
+ uint32_t region_size, uint32_t extents,
+ struct dm_list *allocatable_pvs,
+ alloc_policy_t alloc,
+ struct dm_list *parallel_areas)
+{
+ struct alloc_handle *ah;
+ uint32_t new_extents;
+
+ if (segtype_is_virtual(segtype)) {
+ log_error("allocate_extents does not handle virtual segments");
+ return NULL;
+ }
+
+ if (vg->fid->fmt->ops->segtype_supported &&
+ !vg->fid->fmt->ops->segtype_supported(vg->fid, segtype)) {
+ log_error("Metadata format (%s) does not support required "
+ "LV segment type (%s).", vg->fid->fmt->name,
+ segtype->name);
+ log_error("Consider changing the metadata format by running "
+ "vgconvert.");
+ return NULL;
+ }
+
+ if (alloc == ALLOC_INHERIT)
+ alloc = vg->alloc;
+
+ new_extents = (lv ? lv->le_count : 0) + extents;
+ if (!(ah = _alloc_init(vg->cmd, vg->cmd->mem, segtype, alloc,
+ new_extents, mirrors, stripes, log_count,
+ vg->extent_size, region_size,
+ parallel_areas)))
+ return_NULL;
+
+ if (!segtype_is_virtual(segtype) &&
+ !_allocate(ah, vg, lv, 1, allocatable_pvs)) {
+ alloc_destroy(ah);
+ return_NULL;
+ }
+
+ return ah;
+}
+
+/*
+ * Add new segments to an LV from supplied list of areas.
+ */
+int lv_add_segment(struct alloc_handle *ah,
+ uint32_t first_area, uint32_t num_areas,
+ struct logical_volume *lv,
+ const struct segment_type *segtype,
+ uint32_t stripe_size,
+ uint64_t status,
+ uint32_t region_size)
+{
+ if (!segtype) {
+ log_error("Missing segtype in lv_add_segment().");
+ return 0;
+ }
+
+ if (segtype_is_virtual(segtype)) {
+ log_error("lv_add_segment cannot handle virtual segments");
+ return 0;
+ }
+
+ if ((status & MIRROR_LOG) && dm_list_size(&lv->segments)) {
+ log_error("Log segments can only be added to an empty LV");
+ return 0;
+ }
+
+ if (!_setup_alloced_segments(lv, &ah->alloced_areas[first_area],
+ num_areas, status,
+ stripe_size, segtype,
+ region_size))
+ return_0;
+
+ if ((segtype->flags & SEG_CAN_SPLIT) && !lv_merge_segments(lv)) {
+ log_error("Couldn't merge segments after extending "
+ "logical volume.");
+ return 0;
+ }
+
+ if (lv->vg->fid->fmt->ops->lv_setup &&
+ !lv->vg->fid->fmt->ops->lv_setup(lv->vg->fid, lv))
+ return_0;
+
+ return 1;
+}
+
+/*
+ * "mirror" segment type doesn't support split.
+ * So, when adding mirrors to linear LV segment, first split it,
+ * then convert it to "mirror" and add areas.
+ */
+static struct lv_segment *_convert_seg_to_mirror(struct lv_segment *seg,
+ uint32_t region_size,
+ struct logical_volume *log_lv)
+{
+ struct lv_segment *newseg;
+ uint32_t s;
+
+ if (!seg_is_striped(seg)) {
+ log_error("Can't convert non-striped segment to mirrored.");
+ return NULL;
+ }
+
+ if (seg->area_count > 1) {
+ log_error("Can't convert striped segment with multiple areas "
+ "to mirrored.");
+ return NULL;
+ }
+
+ if (!(newseg = alloc_lv_segment(seg->lv->vg->cmd->mem,
+ get_segtype_from_string(seg->lv->vg->cmd, "mirror"),
+ seg->lv, seg->le, seg->len,
+ seg->status, seg->stripe_size,
+ log_lv,
+ seg->area_count, seg->area_len,
+ seg->chunk_size, region_size,
+ seg->extents_copied, NULL))) {
+ log_error("Couldn't allocate converted LV segment");
+ return NULL;
+ }
+
+ for (s = 0; s < seg->area_count; s++)
+ if (!move_lv_segment_area(newseg, s, seg, s))
+ return_NULL;
+
+ seg->pvmove_source_seg = NULL; /* Not maintained after allocation */
+
+ dm_list_add(&seg->list, &newseg->list);
+ dm_list_del(&seg->list);
+
+ return newseg;
+}
+
+/*
+ * Add new areas to mirrored segments
+ */
+int lv_add_mirror_areas(struct alloc_handle *ah,
+ struct logical_volume *lv, uint32_t le,
+ uint32_t region_size)
+{
+ struct alloced_area *aa;
+ struct lv_segment *seg;
+ uint32_t current_le = le;
+ uint32_t s, old_area_count, new_area_count;
+
+ dm_list_iterate_items(aa, &ah->alloced_areas[0]) {
+ if (!(seg = find_seg_by_le(lv, current_le))) {
+ log_error("Failed to find segment for %s extent %"
+ PRIu32, lv->name, current_le);
+ return 0;
+ }
+
+ /* Allocator assures aa[0].len <= seg->area_len */
+ if (aa[0].len < seg->area_len) {
+ if (!lv_split_segment(lv, seg->le + aa[0].len)) {
+ log_error("Failed to split segment at %s "
+ "extent %" PRIu32, lv->name, le);
+ return 0;
+ }
+ }
+
+ if (!seg_is_mirrored(seg) &&
+ (!(seg = _convert_seg_to_mirror(seg, region_size, NULL))))
+ return_0;
+
+ old_area_count = seg->area_count;
+ new_area_count = old_area_count + ah->area_count;
+
+ if (!_lv_segment_add_areas(lv, seg, new_area_count))
+ return_0;
+
+ for (s = 0; s < ah->area_count; s++) {
+ if (!set_lv_segment_area_pv(seg, s + old_area_count,
+ aa[s].pv, aa[s].pe))
+ return_0;
+ }
+
+ current_le += seg->area_len;
+ }
+
+ lv->status |= MIRRORED;
+
+ if (lv->vg->fid->fmt->ops->lv_setup &&
+ !lv->vg->fid->fmt->ops->lv_setup(lv->vg->fid, lv))
+ return_0;
+
+ return 1;
+}
+
+/*
+ * Add mirror image LVs to mirrored segments
+ */
+int lv_add_mirror_lvs(struct logical_volume *lv,
+ struct logical_volume **sub_lvs,
+ uint32_t num_extra_areas,
+ uint64_t status, uint32_t region_size)
+{
+ struct lv_segment *seg;
+ uint32_t old_area_count, new_area_count;
+ uint32_t m;
+ struct segment_type *mirror_segtype;
+
+ seg = first_seg(lv);
+
+ if (dm_list_size(&lv->segments) != 1 || seg_type(seg, 0) != AREA_LV) {
+ log_error("Mirror layer must be inserted before adding mirrors");
+ return_0;
+ }
+
+ mirror_segtype = get_segtype_from_string(lv->vg->cmd, "mirror");
+ if (seg->segtype != mirror_segtype)
+ if (!(seg = _convert_seg_to_mirror(seg, region_size, NULL)))
+ return_0;
+
+ if (region_size && region_size != seg->region_size) {
+ log_error("Conflicting region_size");
+ return 0;
+ }
+
+ old_area_count = seg->area_count;
+ new_area_count = old_area_count + num_extra_areas;
+
+ if (!_lv_segment_add_areas(lv, seg, new_area_count)) {
+ log_error("Failed to allocate widened LV segment for %s.",
+ lv->name);
+ return 0;
+ }
+
+ for (m = 0; m < old_area_count; m++)
+ seg_lv(seg, m)->status |= status;
+
+ for (m = old_area_count; m < new_area_count; m++) {
+ if (!set_lv_segment_area_lv(seg, m, sub_lvs[m - old_area_count],
+ 0, status))
+ return_0;
+ lv_set_hidden(sub_lvs[m - old_area_count]);
+ }
+
+ lv->status |= MIRRORED;
+
+ return 1;
+}
+
+/*
+ * Turn an empty LV into a mirror log.
+ *
+ * FIXME: Mirrored logs are built inefficiently.
+ * A mirrored log currently uses the same layout that a mirror
+ * LV uses. The mirror layer sits on top of AREA_LVs which form the
+ * legs, rather on AREA_PVs. This is done to allow re-use of the
+ * various mirror functions to also handle the mirrored LV that makes
+ * up the log.
+ *
+ * If we used AREA_PVs under the mirror layer of a log, we could
+ * assemble it all at once by calling 'lv_add_segment' with the
+ * appropriate segtype (mirror/stripe), like this:
+ * lv_add_segment(ah, ah->area_count, ah->log_area_count,
+ * log_lv, segtype, 0, MIRROR_LOG, 0);
+ *
+ * For now, we use the same mechanism to build a mirrored log as we
+ * do for building a mirrored LV: 1) create initial LV, 2) add a
+ * mirror layer, and 3) add the remaining copy LVs
+ */
+int lv_add_log_segment(struct alloc_handle *ah, uint32_t first_area,
+ struct logical_volume *log_lv, uint64_t status)
+{
+
+ return lv_add_segment(ah, ah->area_count + first_area, 1, log_lv,
+ get_segtype_from_string(log_lv->vg->cmd,
+ "striped"),
+ 0, status, 0);
+}
+
+static int _lv_extend_mirror(struct alloc_handle *ah,
+ struct logical_volume *lv,
+ uint32_t extents, uint32_t first_area,
+ uint32_t stripes, uint32_t stripe_size)
+{
+ struct lv_segment *seg;
+ uint32_t m, s;
+
+ seg = first_seg(lv);
+ for (m = first_area, s = 0; s < seg->area_count; s++) {
+ if (is_temporary_mirror_layer(seg_lv(seg, s))) {
+ if (!_lv_extend_mirror(ah, seg_lv(seg, s), extents, m, stripes, stripe_size))
+ return_0;
+ m += lv_mirror_count(seg_lv(seg, s));
+ continue;
+ }
+
+ if (!lv_add_segment(ah, m, stripes, seg_lv(seg, s),
+ get_segtype_from_string(lv->vg->cmd,
+ "striped"),
+ stripe_size, 0, 0)) {
+ log_error("Aborting. Failed to extend %s.",
+ seg_lv(seg, s)->name);
+ return 0;
+ }
+ m += stripes;
+ }
+ seg->area_len += extents;
+ seg->len += extents;
+ lv->le_count += extents;
+ lv->size += (uint64_t) extents *lv->vg->extent_size;
+
+ return 1;
+}
+
+/*
+ * Entry point for single-step LV allocation + extension.
+ */
+int lv_extend(struct logical_volume *lv,
+ const struct segment_type *segtype,
+ uint32_t stripes, uint32_t stripe_size,
+ uint32_t mirrors, uint32_t extents,
+ struct physical_volume *mirrored_pv __attribute__((unused)),
+ uint32_t mirrored_pe __attribute__((unused)),
+ uint64_t status, struct dm_list *allocatable_pvs,
+ alloc_policy_t alloc)
+{
+ int r = 1;
+ struct alloc_handle *ah;
+
+ if (segtype_is_virtual(segtype))
+ return lv_add_virtual_segment(lv, status, extents, segtype);
+
+ if (!(ah = allocate_extents(lv->vg, lv, segtype, stripes, mirrors, 0, 0,
+ extents, allocatable_pvs, alloc, NULL)))
+ return_0;
+
+ if (mirrors < 2)
+ r = lv_add_segment(ah, 0, ah->area_count, lv, segtype,
+ stripe_size, status, 0);
+ else
+ r = _lv_extend_mirror(ah, lv, extents, 0, stripes, stripe_size);
+
+ alloc_destroy(ah);
+ return r;
+}
+
+/*
+ * Minimal LV renaming function.
+ * Metadata transaction should be made by caller.
+ * Assumes new_name is allocated from cmd->mem pool.
+ */
+static int _rename_single_lv(struct logical_volume *lv, char *new_name)
+{
+ struct volume_group *vg = lv->vg;
+
+ if (find_lv_in_vg(vg, new_name)) {
+ log_error("Logical volume \"%s\" already exists in "
+ "volume group \"%s\"", new_name, vg->name);
+ return 0;
+ }
+
+ if (lv->status & LOCKED) {
+ log_error("Cannot rename locked LV %s", lv->name);
+ return 0;
+ }
+
+ lv->name = new_name;
+
+ return 1;
+}
+
+/*
+ * Rename sub LV.
+ * 'lv_name_old' and 'lv_name_new' are old and new names of the main LV.
+ */
+static int _rename_sub_lv(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ const char *lv_name_old, const char *lv_name_new)
+{
+ char *suffix, *new_name;
+ size_t len;
+
+ /*
+ * A sub LV name starts with lv_name_old + '_'.
+ * The suffix follows lv_name_old and includes '_'.
+ */
+ len = strlen(lv_name_old);
+ if (strncmp(lv->name, lv_name_old, len) || lv->name[len] != '_') {
+ log_error("Cannot rename \"%s\": name format not recognized "
+ "for internal LV \"%s\"",
+ lv_name_old, lv->name);
+ return 0;
+ }
+ suffix = lv->name + len;
+
+ /*
+ * Compose a new name for sub lv:
+ * e.g. new name is "lvol1_mlog"
+ * if the sub LV is "lvol0_mlog" and
+ * a new name for main LV is "lvol1"
+ */
+ len = strlen(lv_name_new) + strlen(suffix) + 1;
+ new_name = dm_pool_alloc(cmd->mem, len);
+ if (!new_name) {
+ log_error("Failed to allocate space for new name");
+ return 0;
+ }
+ if (!dm_snprintf(new_name, len, "%s%s", lv_name_new, suffix)) {
+ log_error("Failed to create new name");
+ return 0;
+ }
+
+ /* Rename it */
+ return _rename_single_lv(lv, new_name);
+}
+
+/* Callback for _for_each_sub_lv */
+static int _rename_cb(struct cmd_context *cmd, struct logical_volume *lv,
+ void *data)
+{
+ struct lv_names *lv_names = (struct lv_names *) data;
+
+ return _rename_sub_lv(cmd, lv, lv_names->old, lv_names->new);
+}
+
+/*
+ * Loop down sub LVs and call "func" for each.
+ * "func" is responsible to log necessary information on failure.
+ */
+static int _for_each_sub_lv(struct cmd_context *cmd, struct logical_volume *lv,
+ int (*func)(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ void *data),
+ void *data)
+{
+ struct logical_volume *org;
+ struct lv_segment *seg;
+ uint32_t s;
+
+ if (lv_is_cow(lv) && lv_is_virtual_origin(org = origin_from_cow(lv)))
+ if (!func(cmd, org, data))
+ return_0;
+
+ dm_list_iterate_items(seg, &lv->segments) {
+ if (seg->log_lv && !func(cmd, seg->log_lv, data))
+ return_0;
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) != AREA_LV)
+ continue;
+ if (!func(cmd, seg_lv(seg, s), data))
+ return_0;
+ if (!_for_each_sub_lv(cmd, seg_lv(seg, s), func, data))
+ return_0;
+ }
+ }
+
+ return 1;
+}
+
+
+/*
+ * Core of LV renaming routine.
+ * VG must be locked by caller.
+ */
+int lv_rename(struct cmd_context *cmd, struct logical_volume *lv,
+ const char *new_name)
+{
+ struct volume_group *vg = lv->vg;
+ struct lv_names lv_names;
+ DM_LIST_INIT(lvs_changed);
+ struct lv_list lvl, lvl2, *lvlp;
+ int r = 0;
+
+ /* rename is not allowed on sub LVs */
+ if (!lv_is_visible(lv)) {
+ log_error("Cannot rename internal LV \"%s\".", lv->name);
+ return 0;
+ }
+
+ if (find_lv_in_vg(vg, new_name)) {
+ log_error("Logical volume \"%s\" already exists in "
+ "volume group \"%s\"", new_name, vg->name);
+ return 0;
+ }
+
+ if (lv->status & LOCKED) {
+ log_error("Cannot rename locked LV %s", lv->name);
+ return 0;
+ }
+
+ if (!archive(vg))
+ return 0;
+
+ /* rename sub LVs */
+ lv_names.old = lv->name;
+ lv_names.new = new_name;
+ if (!_for_each_sub_lv(cmd, lv, _rename_cb, (void *) &lv_names))
+ return 0;
+
+ /* rename main LV */
+ if (!(lv->name = dm_pool_strdup(cmd->mem, new_name))) {
+ log_error("Failed to allocate space for new name");
+ return 0;
+ }
+
+ lvl.lv = lv;
+ dm_list_add(&lvs_changed, &lvl.list);
+
+ /* rename active virtual origin too */
+ if (lv_is_cow(lv) && lv_is_virtual_origin(lvl2.lv = origin_from_cow(lv)))
+ dm_list_add_h(&lvs_changed, &lvl2.list);
+
+ log_verbose("Writing out updated volume group");
+ if (!vg_write(vg))
+ return 0;
+
+
+ if (!suspend_lvs(cmd, &lvs_changed)) {
+ vg_revert(vg);
+ goto_out;
+ }
+
+ if (!(r = vg_commit(vg)))
+ stack;
+
+ /*
+ * FIXME: resume LVs in reverse order to prevent memory
+ * lock imbalance when resuming virtual snapshot origin
+ * (resume of snapshot resumes origin too)
+ */
+ dm_list_iterate_back_items(lvlp, &lvs_changed)
+ if (!resume_lv(cmd, lvlp->lv))
+ stack;
+out:
+ backup(vg);
+ return r;
+}
+
+char *generate_lv_name(struct volume_group *vg, const char *format,
+ char *buffer, size_t len)
+{
+ struct lv_list *lvl;
+ int high = -1, i;
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (sscanf(lvl->lv->name, format, &i) != 1)
+ continue;
+
+ if (i > high)
+ high = i;
+ }
+
+ if (dm_snprintf(buffer, len, format, high + 1) < 0)
+ return NULL;
+
+ return buffer;
+}
+
+int vg_max_lv_reached(struct volume_group *vg)
+{
+ if (!vg->max_lv)
+ return 0;
+
+ if (vg->max_lv > vg_visible_lvs(vg))
+ return 0;
+
+ log_verbose("Maximum number of logical volumes (%u) reached "
+ "in volume group %s", vg->max_lv, vg->name);
+
+ return 1;
+}
+
+struct logical_volume *alloc_lv(struct dm_pool *mem)
+{
+ struct logical_volume *lv;
+
+ if (!(lv = dm_pool_zalloc(mem, sizeof(*lv)))) {
+ log_error("Unable to allocate logical volume structure");
+ return NULL;
+ }
+
+ lv->snapshot = NULL;
+ dm_list_init(&lv->snapshot_segs);
+ dm_list_init(&lv->segments);
+ dm_list_init(&lv->tags);
+ dm_list_init(&lv->segs_using_this_lv);
+ dm_list_init(&lv->rsites);
+
+ return lv;
+}
+
+/*
+ * Create a new empty LV.
+ */
+struct logical_volume *lv_create_empty(const char *name,
+ union lvid *lvid,
+ uint64_t status,
+ alloc_policy_t alloc,
+ struct volume_group *vg)
+{
+ struct format_instance *fi = vg->fid;
+ struct logical_volume *lv;
+ char dname[NAME_LEN];
+
+ if (vg_max_lv_reached(vg))
+ stack;
+
+ if (strstr(name, "%d") &&
+ !(name = generate_lv_name(vg, name, dname, sizeof(dname)))) {
+ log_error("Failed to generate unique name for the new "
+ "logical volume");
+ return NULL;
+ } else if (find_lv_in_vg(vg, name)) {
+ log_error("Unable to create LV %s in Volume Group %s: "
+ "name already in use.", name, vg->name);
+ return NULL;
+ }
+
+ log_verbose("Creating logical volume %s", name);
+
+ if (!(lv = alloc_lv(vg->vgmem)))
+ return_NULL;
+
+ if (!(lv->name = dm_pool_strdup(vg->vgmem, name)))
+ goto_bad;
+
+ lv->status = status;
+ lv->alloc = alloc;
+ lv->read_ahead = vg->cmd->default_settings.read_ahead;
+ lv->major = -1;
+ lv->minor = -1;
+ lv->size = UINT64_C(0);
+ lv->le_count = 0;
+
+ if (lvid)
+ lv->lvid = *lvid;
+
+ if (!link_lv_to_vg(vg, lv))
+ goto_bad;
+
+ if (fi->fmt->ops->lv_setup && !fi->fmt->ops->lv_setup(fi, lv))
+ goto_bad;
+
+ return lv;
+bad:
+ dm_pool_free(vg->vgmem, lv);
+ return NULL;
+}
+
+static int _add_pvs(struct cmd_context *cmd, struct pv_segment *peg,
+ uint32_t s __attribute__((unused)), void *data)
+{
+ struct seg_pvs *spvs = (struct seg_pvs *) data;
+ struct pv_list *pvl;
+
+ /* Don't add again if it's already on list. */
+ if (find_pv_in_pv_list(&spvs->pvs, peg->pv))
+ return 1;
+
+ if (!(pvl = dm_pool_alloc(cmd->mem, sizeof(*pvl)))) {
+ log_error("pv_list allocation failed");
+ return 0;
+ }
+
+ pvl->pv = peg->pv;
+
+ dm_list_add(&spvs->pvs, &pvl->list);
+
+ return 1;
+}
+
+/*
+ * Construct dm_list of segments of LVs showing which PVs they use.
+ * For pvmove we use the *parent* LV so we can pick up stripes & existing mirrors etc.
+ */
+struct dm_list *build_parallel_areas_from_lv(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ unsigned use_pvmove_parent_lv)
+{
+ struct dm_list *parallel_areas;
+ struct seg_pvs *spvs;
+ uint32_t current_le = 0;
+ struct lv_segment * uninitialized_var(seg);
+
+ if (!(parallel_areas = dm_pool_alloc(cmd->mem, sizeof(*parallel_areas)))) {
+ log_error("parallel_areas allocation failed");
+ return NULL;
+ }
+
+ dm_list_init(parallel_areas);
+
+ do {
+ if (!(spvs = dm_pool_zalloc(cmd->mem, sizeof(*spvs)))) {
+ log_error("allocation failed");
+ return NULL;
+ }
+
+ dm_list_init(&spvs->pvs);
+
+ spvs->le = current_le;
+ spvs->len = lv->le_count - current_le;
+
+ dm_list_add(parallel_areas, &spvs->list);
+
+ if (use_pvmove_parent_lv && !(seg = find_seg_by_le(lv, current_le))) {
+ log_error("Failed to find segment for %s extent %" PRIu32,
+ lv->name, current_le);
+ return 0;
+ }
+
+ /* Find next segment end */
+ /* FIXME Unnecessary nesting! */
+ if (!_for_each_pv(cmd, use_pvmove_parent_lv ? seg->pvmove_source_seg->lv : lv,
+ use_pvmove_parent_lv ? seg->pvmove_source_seg->le : current_le,
+ use_pvmove_parent_lv ? spvs->len * _calc_area_multiple(seg->pvmove_source_seg->segtype, seg->pvmove_source_seg->area_count, 0) : spvs->len,
+ use_pvmove_parent_lv ? seg->pvmove_source_seg : NULL,
+ &spvs->len,
+ 0, 0, -1, 0, _add_pvs, (void *) spvs))
+ return_NULL;
+
+ current_le = spvs->le + spvs->len;
+ } while (current_le < lv->le_count);
+
+ /* FIXME Merge adjacent segments with identical PV lists (avoids need for contiguous allocation attempts between successful allocations) */
+
+ return parallel_areas;
+}
+
+int link_lv_to_vg(struct volume_group *vg, struct logical_volume *lv)
+{
+ struct lv_list *lvl;
+
+ if (vg_max_lv_reached(vg))
+ stack;
+
+ if (!(lvl = dm_pool_zalloc(vg->vgmem, sizeof(*lvl))))
+ return_0;
+
+ lvl->lv = lv;
+ lv->vg = vg;
+ dm_list_add(&vg->lvs, &lvl->list);
+
+ return 1;
+}
+
+int unlink_lv_from_vg(struct logical_volume *lv)
+{
+ struct lv_list *lvl;
+
+ if (!(lvl = find_lv_in_vg(lv->vg, lv->name)))
+ return_0;
+
+ dm_list_del(&lvl->list);
+
+ return 1;
+}
+
+void lv_set_visible(struct logical_volume *lv)
+{
+ if (lv_is_visible(lv))
+ return;
+
+ lv->status |= VISIBLE_LV;
+
+ log_debug("LV %s in VG %s is now visible.", lv->name, lv->vg->name);
+}
+
+void lv_set_hidden(struct logical_volume *lv)
+{
+ if (!lv_is_visible(lv))
+ return;
+
+ lv->status &= ~VISIBLE_LV;
+
+ log_debug("LV %s in VG %s is now hidden.", lv->name, lv->vg->name);
+}
+
+int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
+ const force_t force)
+{
+ struct volume_group *vg;
+ struct lvinfo info;
+ struct logical_volume *origin = NULL;
+ int was_merging = 0;
+
+ vg = lv->vg;
+
+ if (!vg_check_status(vg, LVM_WRITE))
+ return 0;
+
+ if (lv_is_origin(lv)) {
+ log_error("Can't remove logical volume \"%s\" under snapshot",
+ lv->name);
+ return 0;
+ }
+
+ if (lv->status & MIRROR_IMAGE) {
+ log_error("Can't remove logical volume %s used by a mirror",
+ lv->name);
+ return 0;
+ }
+
+ if (lv->status & MIRROR_LOG) {
+ log_error("Can't remove logical volume %s used as mirror log",
+ lv->name);
+ return 0;
+ }
+
+ if (lv->status & LOCKED) {
+ log_error("Can't remove locked LV %s", lv->name);
+ return 0;
+ }
+
+ /* FIXME Ensure not referred to by another existing LVs */
+
+ if (lv_info(cmd, lv, 0, &info, 1, 0)) {
+ if (info.open_count) {
+ log_error("Can't remove open logical volume \"%s\"",
+ lv->name);
+ return 0;
+ }
+
+ if (lv_is_active(lv) && (force == PROMPT) &&
+ lv_is_visible(lv) &&
+ yes_no_prompt("Do you really want to remove active "
+ "%slogical volume %s? [y/n]: ",
+ vg_is_clustered(vg) ? "clustered " : "",
+ lv->name) == 'n') {
+ log_error("Logical volume %s not removed", lv->name);
+ return 0;
+ }
+ }
+
+ if (!archive(vg))
+ return 0;
+
+ if (lv_is_cow(lv)) {
+ origin = origin_from_cow(lv);
+ was_merging = lv_is_merging_origin(origin);
+ log_verbose("Removing snapshot %s", lv->name);
+ /* vg_remove_snapshot() will preload origin if it was merging */
+ if (!vg_remove_snapshot(lv))
+ return_0;
+ }
+
+ if (!deactivate_lv(cmd, lv)) {
+ log_error("Unable to deactivate logical volume \"%s\"",
+ lv->name);
+ return 0;
+ }
+
+ log_verbose("Releasing logical volume \"%s\"", lv->name);
+ if (!lv_remove(lv)) {
+ log_error("Error releasing logical volume \"%s\"", lv->name);
+ return 0;
+ }
+
+ /* store it on disks */
+ if (!vg_write(vg) || !vg_commit(vg))
+ return_0;
+
+ /* If no snapshots left, and was not merging, reload without -real. */
+ if (origin && (!lv_is_origin(origin) && !was_merging)) {
+ if (!suspend_lv(cmd, origin)) {
+ log_error("Failed to refresh %s without snapshot.", origin->name);
+ return 0;
+ }
+ if (!resume_lv(cmd, origin)) {
+ log_error("Failed to resume %s.", origin->name);
+ return 0;
+ }
+ }
+
+ backup(vg);
+
+ if (lv_is_visible(lv))
+ log_print("Logical volume \"%s\" successfully removed", lv->name);
+
+ return 1;
+}
+
+/*
+ * remove LVs with its dependencies - LV leaf nodes should be removed first
+ */
+int lv_remove_with_dependencies(struct cmd_context *cmd, struct logical_volume *lv,
+ const force_t force, unsigned level)
+{
+ struct dm_list *snh, *snht;
+
+ if (lv_is_cow(lv)) {
+ /* A merging snapshot cannot be removed directly */
+ if (lv_is_merging_cow(lv) && !level) {
+ log_error("Can't remove merging snapshot logical volume \"%s\"",
+ lv->name);
+ return 0;
+ }
+ }
+
+ if (lv_is_origin(lv)) {
+ /* remove snapshot LVs first */
+ dm_list_iterate_safe(snh, snht, &lv->snapshot_segs) {
+ if (!lv_remove_with_dependencies(cmd, dm_list_struct_base(snh, struct lv_segment,
+ origin_list)->cow,
+ force, level + 1))
+ return 0;
+ }
+ }
+
+ return lv_remove_single(cmd, lv, force);
+}
+
+/*
+ * insert_layer_for_segments_on_pv() inserts a layer segment for a segment area.
+ * However, layer modification could split the underlying layer segment.
+ * This function splits the parent area according to keep the 1:1 relationship
+ * between the parent area and the underlying layer segment.
+ * Since the layer LV might have other layers below, build_parallel_areas()
+ * is used to find the lowest-level segment boundaries.
+ */
+static int _split_parent_area(struct lv_segment *seg, uint32_t s,
+ struct dm_list *layer_seg_pvs)
+{
+ uint32_t parent_area_len, parent_le, layer_le;
+ uint32_t area_multiple;
+ struct seg_pvs *spvs;
+
+ if (seg_is_striped(seg))
+ area_multiple = seg->area_count;
+ else
+ area_multiple = 1;
+
+ parent_area_len = seg->area_len;
+ parent_le = seg->le;
+ layer_le = seg_le(seg, s);
+
+ while (parent_area_len > 0) {
+ /* Find the layer segment pointed at */
+ if (!(spvs = _find_seg_pvs_by_le(layer_seg_pvs, layer_le))) {
+ log_error("layer segment for %s:%" PRIu32 " not found",
+ seg->lv->name, parent_le);
+ return 0;
+ }
+
+ if (spvs->le != layer_le) {
+ log_error("Incompatible layer boundary: "
+ "%s:%" PRIu32 "[%" PRIu32 "] on %s:%" PRIu32,
+ seg->lv->name, parent_le, s,
+ seg_lv(seg, s)->name, layer_le);
+ return 0;
+ }
+
+ if (spvs->len < parent_area_len) {
+ parent_le += spvs->len * area_multiple;
+ if (!lv_split_segment(seg->lv, parent_le))
+ return_0;
+ }
+
+ parent_area_len -= spvs->len;
+ layer_le += spvs->len;
+ }
+
+ return 1;
+}
+
+/*
+ * Split the parent LV segments if the layer LV below it is splitted.
+ */
+int split_parent_segments_for_layer(struct cmd_context *cmd,
+ struct logical_volume *layer_lv)
+{
+ struct lv_list *lvl;
+ struct logical_volume *parent_lv;
+ struct lv_segment *seg;
+ uint32_t s;
+ struct dm_list *parallel_areas;
+
+ if (!(parallel_areas = build_parallel_areas_from_lv(cmd, layer_lv, 0)))
+ return_0;
+
+ /* Loop through all LVs except itself */
+ dm_list_iterate_items(lvl, &layer_lv->vg->lvs) {
+ parent_lv = lvl->lv;
+ if (parent_lv == layer_lv)
+ continue;
+
+ /* Find all segments that point at the layer LV */
+ dm_list_iterate_items(seg, &parent_lv->segments) {
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) != AREA_LV ||
+ seg_lv(seg, s) != layer_lv)
+ continue;
+
+ if (!_split_parent_area(seg, s, parallel_areas))
+ return_0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* Remove a layer from the LV */
+int remove_layers_for_segments(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ struct logical_volume *layer_lv,
+ uint64_t status_mask, struct dm_list *lvs_changed)
+{
+ struct lv_segment *seg, *lseg;
+ uint32_t s;
+ int lv_changed = 0;
+ struct lv_list *lvl;
+
+ log_very_verbose("Removing layer %s for segments of %s",
+ layer_lv->name, lv->name);
+
+ /* Find all segments that point at the temporary mirror */
+ dm_list_iterate_items(seg, &lv->segments) {
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) != AREA_LV ||
+ seg_lv(seg, s) != layer_lv)
+ continue;
+
+ /* Find the layer segment pointed at */
+ if (!(lseg = find_seg_by_le(layer_lv, seg_le(seg, s)))) {
+ log_error("Layer segment found: %s:%" PRIu32,
+ layer_lv->name, seg_le(seg, s));
+ return 0;
+ }
+
+ /* Check the segment params are compatible */
+ if (!seg_is_striped(lseg) || lseg->area_count != 1) {
+ log_error("Layer is not linear: %s:%" PRIu32,
+ layer_lv->name, lseg->le);
+ return 0;
+ }
+ if ((lseg->status & status_mask) != status_mask) {
+ log_error("Layer status does not match: "
+ "%s:%" PRIu32 " status: 0x%" PRIx64 "/0x%" PRIx64,
+ layer_lv->name, lseg->le,
+ lseg->status, status_mask);
+ return 0;
+ }
+ if (lseg->le != seg_le(seg, s) ||
+ lseg->area_len != seg->area_len) {
+ log_error("Layer boundary mismatch: "
+ "%s:%" PRIu32 "-%" PRIu32 " on "
+ "%s:%" PRIu32 " / "
+ "%" PRIu32 "-%" PRIu32 " / ",
+ lv->name, seg->le, seg->area_len,
+ layer_lv->name, seg_le(seg, s),
+ lseg->le, lseg->area_len);
+ return 0;
+ }
+
+ if (!move_lv_segment_area(seg, s, lseg, 0))
+ return_0;
+
+ /* Replace mirror with error segment */
+ if (!(lseg->segtype =
+ get_segtype_from_string(lv->vg->cmd, "error"))) {
+ log_error("Missing error segtype");
+ return 0;
+ }
+ lseg->area_count = 0;
+
+ /* First time, add LV to list of LVs affected */
+ if (!lv_changed && lvs_changed) {
+ if (!(lvl = dm_pool_alloc(cmd->mem, sizeof(*lvl)))) {
+ log_error("lv_list alloc failed");
+ return 0;
+ }
+ lvl->lv = lv;
+ dm_list_add(lvs_changed, &lvl->list);
+ lv_changed = 1;
+ }
+ }
+ }
+ if (lv_changed && !lv_merge_segments(lv))
+ stack;
+
+ return 1;
+}
+
+/* Remove a layer */
+int remove_layers_for_segments_all(struct cmd_context *cmd,
+ struct logical_volume *layer_lv,
+ uint64_t status_mask,
+ struct dm_list *lvs_changed)
+{
+ struct lv_list *lvl;
+ struct logical_volume *lv1;
+
+ /* Loop through all LVs except the temporary mirror */
+ dm_list_iterate_items(lvl, &layer_lv->vg->lvs) {
+ lv1 = lvl->lv;
+ if (lv1 == layer_lv)
+ continue;
+
+ if (!remove_layers_for_segments(cmd, lv1, layer_lv,
+ status_mask, lvs_changed))
+ return_0;
+ }
+
+ if (!lv_empty(layer_lv))
+ return_0;
+
+ return 1;
+}
+
+static int _move_lv_segments(struct logical_volume *lv_to,
+ struct logical_volume *lv_from,
+ uint64_t set_status, uint64_t reset_status)
+{
+ struct lv_segment *seg;
+
+ dm_list_iterate_items(seg, &lv_to->segments) {
+ if (seg->origin) {
+ log_error("Can't move snapshot segment");
+ return 0;
+ }
+ }
+
+ lv_to->segments = lv_from->segments;
+ lv_to->segments.n->p = &lv_to->segments;
+ lv_to->segments.p->n = &lv_to->segments;
+
+ dm_list_iterate_items(seg, &lv_to->segments) {
+ seg->lv = lv_to;
+ seg->status &= ~reset_status;
+ seg->status |= set_status;
+ }
+
+ dm_list_init(&lv_from->segments);
+
+ lv_to->le_count = lv_from->le_count;
+ lv_to->size = lv_from->size;
+
+ lv_from->le_count = 0;
+ lv_from->size = 0;
+
+ return 1;
+}
+
+/* Remove a layer from the LV */
+int remove_layer_from_lv(struct logical_volume *lv,
+ struct logical_volume *layer_lv)
+{
+ struct logical_volume *parent;
+ struct lv_segment *parent_seg;
+ struct segment_type *segtype;
+
+ log_very_verbose("Removing layer %s for %s", layer_lv->name, lv->name);
+
+ if (!(parent_seg = get_only_segment_using_this_lv(layer_lv))) {
+ log_error("Failed to find layer %s in %s",
+ layer_lv->name, lv->name);
+ return 0;
+ }
+ parent = parent_seg->lv;
+
+ /*
+ * Before removal, the layer should be cleaned up,
+ * i.e. additional segments and areas should have been removed.
+ */
+ if (dm_list_size(&parent->segments) != 1 ||
+ parent_seg->area_count != 1 ||
+ seg_type(parent_seg, 0) != AREA_LV ||
+ layer_lv != seg_lv(parent_seg, 0) ||
+ parent->le_count != layer_lv->le_count)
+ return_0;
+
+ if (!lv_empty(parent))
+ return_0;
+
+ if (!_move_lv_segments(parent, layer_lv, 0, 0))
+ return_0;
+
+ /* Replace the empty layer with error segment */
+ segtype = get_segtype_from_string(lv->vg->cmd, "error");
+ if (!lv_add_virtual_segment(layer_lv, 0, parent->le_count, segtype))
+ return_0;
+
+ return 1;
+}
+
+/*
+ * Create and insert a linear LV "above" lv_where.
+ * After the insertion, a new LV named lv_where->name + suffix is created
+ * and all segments of lv_where is moved to the new LV.
+ * lv_where will have a single segment which maps linearly to the new LV.
+ */
+struct logical_volume *insert_layer_for_lv(struct cmd_context *cmd,
+ struct logical_volume *lv_where,
+ uint64_t status,
+ const char *layer_suffix)
+{
+ struct logical_volume *layer_lv;
+ char *name;
+ size_t len;
+ struct segment_type *segtype;
+ struct lv_segment *mapseg;
+
+ /* create an empty layer LV */
+ len = strlen(lv_where->name) + 32;
+ if (!(name = alloca(len))) {
+ log_error("layer name allocation failed. "
+ "Remove new LV and retry.");
+ return NULL;
+ }
+
+ if (dm_snprintf(name, len, "%s%s", lv_where->name, layer_suffix) < 0) {
+ log_error("layer name allocation failed. "
+ "Remove new LV and retry.");
+ return NULL;
+ }
+
+ if (!(layer_lv = lv_create_empty(name, NULL, LVM_READ | LVM_WRITE,
+ ALLOC_INHERIT, lv_where->vg))) {
+ log_error("Creation of layer LV failed");
+ return NULL;
+ }
+
+ if (lv_is_active(lv_where) && strstr(name, "_mimagetmp")) {
+ log_very_verbose("Creating transient LV %s for mirror conversion in VG %s.", name, lv_where->vg->name);
+
+ segtype = get_segtype_from_string(cmd, "error");
+
+ if (!lv_add_virtual_segment(layer_lv, 0, lv_where->le_count, segtype)) {
+ log_error("Creation of transient LV %s for mirror conversion in VG %s failed.", name, lv_where->vg->name);
+ return NULL;
+ }
+
+ if (!vg_write(lv_where->vg)) {
+ log_error("Failed to write intermediate VG %s metadata for mirror conversion.", lv_where->vg->name);
+ return NULL;
+ }
+
+ if (!vg_commit(lv_where->vg)) {
+ log_error("Failed to commit intermediate VG %s metadata for mirror conversion.", lv_where->vg->name);
+ vg_revert(lv_where->vg);
+ return NULL;
+ }
+
+ if (!activate_lv(cmd, layer_lv)) {
+ log_error("Failed to resume transient error LV %s for mirror conversion in VG %s.", name, lv_where->vg->name);
+ return NULL;
+ }
+ }
+
+ log_very_verbose("Inserting layer %s for %s",
+ layer_lv->name, lv_where->name);
+
+ if (!_move_lv_segments(layer_lv, lv_where, 0, 0))
+ return_NULL;
+
+ if (!(segtype = get_segtype_from_string(cmd, "striped")))
+ return_NULL;
+
+ /* allocate a new linear segment */
+ if (!(mapseg = alloc_lv_segment(cmd->mem, segtype,
+ lv_where, 0, layer_lv->le_count,
+ status, 0, NULL, 1, layer_lv->le_count,
+ 0, 0, 0, NULL)))
+ return_NULL;
+
+ /* map the new segment to the original underlying are */
+ if (!set_lv_segment_area_lv(mapseg, 0, layer_lv, 0, 0))
+ return_NULL;
+
+ /* add the new segment to the layer LV */
+ dm_list_add(&lv_where->segments, &mapseg->list);
+ lv_where->le_count = layer_lv->le_count;
+ lv_where->size = lv_where->le_count * lv_where->vg->extent_size;
+
+ return layer_lv;
+}
+
+/*
+ * Extend and insert a linear layer LV beneath the source segment area.
+ */
+static int _extend_layer_lv_for_segment(struct logical_volume *layer_lv,
+ struct lv_segment *seg, uint32_t s,
+ uint64_t status)
+{
+ struct lv_segment *mapseg;
+ struct segment_type *segtype;
+ struct physical_volume *src_pv = seg_pv(seg, s);
+ uint32_t src_pe = seg_pe(seg, s);
+
+ if (seg_type(seg, s) != AREA_PV && seg_type(seg, s) != AREA_LV)
+ return_0;
+
+ if (!(segtype = get_segtype_from_string(layer_lv->vg->cmd, "striped")))
+ return_0;
+
+ /* FIXME Incomplete message? Needs more context */
+ log_very_verbose("Inserting %s:%" PRIu32 "-%" PRIu32 " of %s/%s",
+ pv_dev_name(src_pv),
+ src_pe, src_pe + seg->area_len - 1,
+ seg->lv->vg->name, seg->lv->name);
+
+ /* allocate a new segment */
+ if (!(mapseg = alloc_lv_segment(layer_lv->vg->cmd->mem, segtype,
+ layer_lv, layer_lv->le_count,
+ seg->area_len, status, 0,
+ NULL, 1, seg->area_len, 0, 0, 0, seg)))
+ return_0;
+
+ /* map the new segment to the original underlying are */
+ if (!move_lv_segment_area(mapseg, 0, seg, s))
+ return_0;
+
+ /* add the new segment to the layer LV */
+ dm_list_add(&layer_lv->segments, &mapseg->list);
+ layer_lv->le_count += seg->area_len;
+ layer_lv->size += seg->area_len * layer_lv->vg->extent_size;
+
+ /* map the original area to the new segment */
+ if (!set_lv_segment_area_lv(seg, s, layer_lv, mapseg->le, 0))
+ return_0;
+
+ return 1;
+}
+
+/*
+ * Match the segment area to PEs in the pvl
+ * (the segment area boundary should be aligned to PE ranges by
+ * _adjust_layer_segments() so that there is no partial overlap.)
+ */
+static int _match_seg_area_to_pe_range(struct lv_segment *seg, uint32_t s,
+ struct pv_list *pvl)
+{
+ struct pe_range *per;
+ uint32_t pe_start, per_end;
+
+ if (!pvl)
+ return 1;
+
+ if (seg_type(seg, s) != AREA_PV || seg_dev(seg, s) != pvl->pv->dev)
+ return 0;
+
+ pe_start = seg_pe(seg, s);
+
+ /* Do these PEs match to any of the PEs in pvl? */
+ dm_list_iterate_items(per, pvl->pe_ranges) {
+ per_end = per->start + per->count - 1;
+
+ if ((pe_start < per->start) || (pe_start > per_end))
+ continue;
+
+ /* FIXME Missing context in this message - add LV/seg details */
+ log_debug("Matched PE range %s:%" PRIu32 "-%" PRIu32 " against "
+ "%s %" PRIu32 " len %" PRIu32, dev_name(pvl->pv->dev),
+ per->start, per_end, dev_name(seg_dev(seg, s)),
+ seg_pe(seg, s), seg->area_len);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * For each segment in lv_where that uses a PV in pvl directly,
+ * split the segment if it spans more than one underlying PV.
+ */
+static int _align_segment_boundary_to_pe_range(struct logical_volume *lv_where,
+ struct pv_list *pvl)
+{
+ struct lv_segment *seg;
+ struct pe_range *per;
+ uint32_t pe_start, pe_end, per_end, stripe_multiplier, s;
+
+ if (!pvl)
+ return 1;
+
+ /* Split LV segments to match PE ranges */
+ dm_list_iterate_items(seg, &lv_where->segments) {
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) != AREA_PV ||
+ seg_dev(seg, s) != pvl->pv->dev)
+ continue;
+
+ /* Do these PEs match with the condition? */
+ dm_list_iterate_items(per, pvl->pe_ranges) {
+ pe_start = seg_pe(seg, s);
+ pe_end = pe_start + seg->area_len - 1;
+ per_end = per->start + per->count - 1;
+
+ /* No overlap? */
+ if ((pe_end < per->start) ||
+ (pe_start > per_end))
+ continue;
+
+ if (seg_is_striped(seg))
+ stripe_multiplier = seg->area_count;
+ else
+ stripe_multiplier = 1;
+
+ if ((per->start != pe_start &&
+ per->start > pe_start) &&
+ !lv_split_segment(lv_where, seg->le +
+ (per->start - pe_start) *
+ stripe_multiplier))
+ return_0;
+
+ if ((per_end != pe_end &&
+ per_end < pe_end) &&
+ !lv_split_segment(lv_where, seg->le +
+ (per_end - pe_start + 1) *
+ stripe_multiplier))
+ return_0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Scan lv_where for segments on a PV in pvl, and for each one found
+ * append a linear segment to lv_layer and insert it between the two.
+ *
+ * If pvl is empty, a layer is placed under the whole of lv_where.
+ * If the layer is inserted, lv_where is added to lvs_changed.
+ */
+int insert_layer_for_segments_on_pv(struct cmd_context *cmd,
+ struct logical_volume *lv_where,
+ struct logical_volume *layer_lv,
+ uint64_t status,
+ struct pv_list *pvl,
+ struct dm_list *lvs_changed)
+{
+ struct lv_segment *seg;
+ struct lv_list *lvl;
+ int lv_used = 0;
+ uint32_t s;
+
+ log_very_verbose("Inserting layer %s for segments of %s on %s",
+ layer_lv->name, lv_where->name,
+ pvl ? pv_dev_name(pvl->pv) : "any");
+
+ if (!_align_segment_boundary_to_pe_range(lv_where, pvl))
+ return_0;
+
+ /* Work through all segments on the supplied PV */
+ dm_list_iterate_items(seg, &lv_where->segments) {
+ for (s = 0; s < seg->area_count; s++) {
+ if (!_match_seg_area_to_pe_range(seg, s, pvl))
+ continue;
+
+ /* First time, add LV to list of LVs affected */
+ if (!lv_used && lvs_changed) {
+ if (!(lvl = dm_pool_alloc(cmd->mem, sizeof(*lvl)))) {
+ log_error("lv_list alloc failed");
+ return 0;
+ }
+ lvl->lv = lv_where;
+ dm_list_add(lvs_changed, &lvl->list);
+ lv_used = 1;
+ }
+
+ if (!_extend_layer_lv_for_segment(layer_lv, seg, s,
+ status)) {
+ log_error("Failed to insert segment in layer "
+ "LV %s under %s:%" PRIu32 "-%" PRIu32,
+ layer_lv->name, lv_where->name,
+ seg->le, seg->le + seg->len);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Initialize the LV with 'value'.
+ */
+int set_lv(struct cmd_context *cmd, struct logical_volume *lv,
+ uint64_t sectors, int value)
+{
+ struct device *dev;
+ char *name;
+
+ /*
+ * FIXME:
+ * <clausen> also, more than 4k
+ * <clausen> say, reiserfs puts it's superblock 32k in, IIRC
+ * <ejt_> k, I'll drop a fixme to that effect
+ * (I know the device is at least 4k, but not 32k)
+ */
+ if (!(name = dm_pool_alloc(cmd->mem, PATH_MAX))) {
+ log_error("Name allocation failed - device not cleared");
+ return 0;
+ }
+
+ if (dm_snprintf(name, PATH_MAX, "%s%s/%s", cmd->dev_dir,
+ lv->vg->name, lv->name) < 0) {
+ log_error("Name too long - device not cleared (%s)", lv->name);
+ return 0;
+ }
+
+ log_verbose("Clearing start of logical volume \"%s\"", lv->name);
+
+ if (!(dev = dev_cache_get(name, NULL))) {
+ log_error("%s: not found: device not cleared", name);
+ return 0;
+ }
+
+ if (!dev_open_quiet(dev))
+ return_0;
+
+ if (!sectors)
+ sectors = UINT64_C(4096) >> SECTOR_SHIFT;
+
+ if (sectors > lv->size)
+ sectors = lv->size;
+
+ if (!dev_set(dev, UINT64_C(0), (size_t) sectors << SECTOR_SHIFT, value))
+ stack;
+
+ dev_flush(dev);
+
+ if (!dev_close_immediate(dev))
+ stack;
+
+ return 1;
+}
+
+
+static struct logical_volume *_create_virtual_origin(struct cmd_context *cmd,
+ struct volume_group *vg,
+ const char *lv_name,
+ uint32_t permission,
+ uint64_t voriginextents)
+{
+ const struct segment_type *segtype;
+ size_t len;
+ char *vorigin_name;
+ struct logical_volume *lv;
+
+ if (!(segtype = get_segtype_from_string(cmd, "zero"))) {
+ log_error("Zero segment type for virtual origin not found");
+ return NULL;
+ }
+
+ len = strlen(lv_name) + 32;
+ if (!(vorigin_name = alloca(len)) ||
+ dm_snprintf(vorigin_name, len, "%s_vorigin", lv_name) < 0) {
+ log_error("Virtual origin name allocation failed.");
+ return NULL;
+ }
+
+ if (!(lv = lv_create_empty(vorigin_name, NULL, permission,
+ ALLOC_INHERIT, vg)))
+ return_NULL;
+
+ if (!lv_extend(lv, segtype, 1, 0, 1, voriginextents, NULL, 0u, 0u,
+ NULL, ALLOC_INHERIT))
+ return_NULL;
+
+ /* store vg on disk(s) */
+ if (!vg_write(vg) || !vg_commit(vg))
+ return_NULL;
+
+ backup(vg);
+
+ return lv;
+}
+
+int lv_create_single(struct volume_group *vg,
+ struct lvcreate_params *lp)
+{
+ struct cmd_context *cmd = vg->cmd;
+ uint32_t size_rest;
+ uint64_t status = UINT64_C(0);
+ struct logical_volume *lv, *org = NULL;
+ int origin_active = 0;
+ struct lvinfo info;
+
+ if (lp->lv_name && find_lv_in_vg(vg, lp->lv_name)) {
+ log_error("Logical volume \"%s\" already exists in "
+ "volume group \"%s\"", lp->lv_name, lp->vg_name);
+ return 0;
+ }
+
+ if (vg_max_lv_reached(vg)) {
+ log_error("Maximum number of logical volumes (%u) reached "
+ "in volume group %s", vg->max_lv, vg->name);
+ return 0;
+ }
+
+ if (lp->mirrors > 1 && !(vg->fid->fmt->features & FMT_SEGMENTS)) {
+ log_error("Metadata does not support mirroring.");
+ return 0;
+ }
+
+ if (lp->read_ahead != DM_READ_AHEAD_AUTO &&
+ lp->read_ahead != DM_READ_AHEAD_NONE &&
+ (vg->fid->fmt->features & FMT_RESTRICTED_READAHEAD) &&
+ (lp->read_ahead < 2 || lp->read_ahead > 120)) {
+ log_error("Metadata only supports readahead values between 2 and 120.");
+ return 0;
+ }
+
+ if (lp->stripe_size > vg->extent_size) {
+ log_error("Reducing requested stripe size %s to maximum, "
+ "physical extent size %s",
+ display_size(cmd, (uint64_t) lp->stripe_size),
+ display_size(cmd, (uint64_t) vg->extent_size));
+ lp->stripe_size = vg->extent_size;
+ }
+
+ /* Need to check the vg's format to verify this - the cmd format isn't setup properly yet */
+ if (lp->stripes > 1 &&
+ !(vg->fid->fmt->features & FMT_UNLIMITED_STRIPESIZE) &&
+ (lp->stripe_size > STRIPE_SIZE_MAX)) {
+ log_error("Stripe size may not exceed %s",
+ display_size(cmd, (uint64_t) STRIPE_SIZE_MAX));
+ return 0;
+ }
+
+ if ((size_rest = lp->extents % lp->stripes)) {
+ log_print("Rounding size (%d extents) up to stripe boundary "
+ "size (%d extents)", lp->extents,
+ lp->extents - size_rest + lp->stripes);
+ lp->extents = lp->extents - size_rest + lp->stripes;
+ }
+
+ if (lp->zero && !activation()) {
+ log_error("Can't wipe start of new LV without using "
+ "device-mapper kernel driver");
+ return 0;
+ }
+
+ status |= lp->permission | VISIBLE_LV;
+
+ if (lp->snapshot) {
+ if (!activation()) {
+ log_error("Can't create snapshot without using "
+ "device-mapper kernel driver");
+ return 0;
+ }
+ /* FIXME Allow exclusive activation. */
+ if (vg_is_clustered(vg)) {
+ log_error("Clustered snapshots are not yet supported.");
+ return 0;
+ }
+
+ /* Must zero cow */
+ status |= LVM_WRITE;
+
+ if (lp->voriginsize)
+ origin_active = 1;
+ else {
+
+ if (!(org = find_lv(vg, lp->origin))) {
+ log_error("Couldn't find origin volume '%s'.",
+ lp->origin);
+ return 0;
+ }
+ if (lv_is_virtual_origin(org)) {
+ log_error("Can't share virtual origins. "
+ "Use --virtualsize.");
+ return 0;
+ }
+ if (lv_is_cow(org)) {
+ log_error("Snapshots of snapshots are not "
+ "supported yet.");
+ return 0;
+ }
+ if (org->status & LOCKED) {
+ log_error("Snapshots of locked devices are not "
+ "supported yet");
+ return 0;
+ }
+ if (lv_is_merging_origin(org)) {
+ log_error("Snapshots of an origin that has a "
+ "merging snapshot is not supported");
+ return 0;
+ }
+ if ((org->status & MIRROR_IMAGE) ||
+ (org->status & MIRROR_LOG)) {
+ log_error("Snapshots of mirror %ss "
+ "are not supported",
+ (org->status & MIRROR_LOG) ?
+ "log" : "image");
+ return 0;
+ }
+
+ if (!lv_info(cmd, org, 0, &info, 0, 0)) {
+ log_error("Check for existence of snapshot "
+ "origin '%s' failed.", org->name);
+ return 0;
+ }
+ origin_active = info.exists;
+ }
+ }
+
+ if (!lp->extents) {
+ log_error("Unable to create new logical volume with no extents");
+ return 0;
+ }
+
+ if (lp->snapshot && (lp->extents * vg->extent_size < 2 * lp->chunk_size)) {
+ log_error("Unable to create a snapshot smaller than 2 chunks.");
+ return 0;
+ }
+
+ if (!seg_is_virtual(lp) &&
+ vg->free_count < lp->extents) {
+ log_error("Insufficient free extents (%u) in volume group %s: "
+ "%u required", vg->free_count, vg->name, lp->extents);
+ return 0;
+ }
+
+ if (lp->stripes > dm_list_size(lp->pvh) && lp->alloc != ALLOC_ANYWHERE) {
+ log_error("Number of stripes (%u) must not exceed "
+ "number of physical volumes (%d)", lp->stripes,
+ dm_list_size(lp->pvh));
+ return 0;
+ }
+
+ if (lp->mirrors > 1 && !activation()) {
+ log_error("Can't create mirror without using "
+ "device-mapper kernel driver.");
+ return 0;
+ }
+
+ /* The snapshot segment gets created later */
+ if (lp->snapshot &&
+ !(lp->segtype = get_segtype_from_string(cmd, "striped")))
+ return_0;
+
+ if (!archive(vg))
+ return 0;
+
+ if (!dm_list_empty(&lp->tags)) {
+ if (!(vg->fid->fmt->features & FMT_TAGS)) {
+ log_error("Volume group %s does not support tags",
+ vg->name);
+ return 0;
+ }
+ }
+
+ if (lp->mirrors > 1) {
+ init_mirror_in_sync(lp->nosync);
+
+ if (lp->nosync) {
+ log_warn("WARNING: New mirror won't be synchronised. "
+ "Don't read what you didn't write!");
+ status |= MIRROR_NOTSYNCED;
+ }
+ }
+
+ if (!(lv = lv_create_empty(lp->lv_name ? lp->lv_name : "lvol%d", NULL,
+ status, lp->alloc, vg)))
+ return_0;
+
+ if (lp->read_ahead != lv->read_ahead) {
+ log_verbose("Setting read ahead sectors");
+ lv->read_ahead = lp->read_ahead;
+ }
+
+ if (lp->minor >= 0) {
+ lv->major = lp->major;
+ lv->minor = lp->minor;
+ lv->status |= FIXED_MINOR;
+ log_verbose("Setting device number to (%d, %d)", lv->major,
+ lv->minor);
+ }
+
+ if (!dm_list_empty(&lp->tags))
+ dm_list_splice(&lv->tags, &lp->tags);
+
+ if (!lv_extend(lv, lp->segtype, lp->stripes, lp->stripe_size,
+ 1, lp->extents, NULL, 0u, 0u, lp->pvh, lp->alloc))
+ return_0;
+
+ if (lp->mirrors > 1) {
+ if (!lv_add_mirrors(cmd, lv, lp->mirrors - 1, lp->stripes,
+ lp->stripe_size,
+ adjusted_mirror_region_size(
+ vg->extent_size,
+ lv->le_count,
+ lp->region_size),
+ lp->log_count, lp->pvh, lp->alloc,
+ MIRROR_BY_LV |
+ (lp->nosync ? MIRROR_SKIP_INIT_SYNC : 0))) {
+ stack;
+ goto revert_new_lv;
+ }
+ }
+
+ /* store vg on disk(s) */
+ if (!vg_write(vg) || !vg_commit(vg))
+ return_0;
+
+ backup(vg);
+
+ init_dmeventd_monitor(lp->activation_monitoring);
+
+ if (lp->snapshot) {
+ if (!activate_lv_excl(cmd, lv)) {
+ log_error("Aborting. Failed to activate snapshot "
+ "exception store.");
+ goto revert_new_lv;
+ }
+ } else if (!activate_lv(cmd, lv)) {
+ log_error("Failed to activate new LV.");
+ if (lp->zero)
+ goto deactivate_and_revert_new_lv;
+ return 0;
+ }
+
+ if (!lp->zero && !lp->snapshot)
+ log_warn("WARNING: \"%s\" not zeroed", lv->name);
+ else if (!set_lv(cmd, lv, UINT64_C(0), 0)) {
+ log_error("Aborting. Failed to wipe %s.",
+ lp->snapshot ? "snapshot exception store" :
+ "start of new LV");
+ goto deactivate_and_revert_new_lv;
+ }
+
+ if (lp->snapshot) {
+ /* Reset permission after zeroing */
+ if (!(lp->permission & LVM_WRITE))
+ lv->status &= ~LVM_WRITE;
+
+ /* COW area must be deactivated if origin is not active */
+ if (!origin_active && !deactivate_lv(cmd, lv)) {
+ log_error("Aborting. Couldn't deactivate snapshot "
+ "COW area. Manual intervention required.");
+ return 0;
+ }
+
+ /* A virtual origin must be activated explicitly. */
+ if (lp->voriginsize &&
+ (!(org = _create_virtual_origin(cmd, vg, lv->name,
+ lp->permission,
+ lp->voriginextents)) ||
+ !activate_lv(cmd, org))) {
+ log_error("Couldn't create virtual origin for LV %s",
+ lv->name);
+ if (org && !lv_remove(org))
+ stack;
+ goto deactivate_and_revert_new_lv;
+ }
+
+ /* cow LV remains active and becomes snapshot LV */
+
+ if (!vg_add_snapshot(org, lv, NULL,
+ org->le_count, lp->chunk_size)) {
+ log_error("Couldn't create snapshot.");
+ goto deactivate_and_revert_new_lv;
+ }
+
+ /* store vg on disk(s) */
+ if (!vg_write(vg))
+ return_0;
+
+ if (!suspend_lv(cmd, org)) {
+ log_error("Failed to suspend origin %s", org->name);
+ vg_revert(vg);
+ return 0;
+ }
+
+ if (!vg_commit(vg))
+ return_0;
+
+ if (!resume_lv(cmd, org)) {
+ log_error("Problem reactivating origin %s", org->name);
+ return 0;
+ }
+ }
+ /* FIXME out of sequence */
+ backup(vg);
+
+ log_print("Logical volume \"%s\" created", lv->name);
+
+ /*
+ * FIXME: as a sanity check we could try reading the
+ * last block of the device ?
+ */
+
+ return 1;
+
+deactivate_and_revert_new_lv:
+ if (!deactivate_lv(cmd, lv)) {
+ log_error("Unable to deactivate failed new LV. "
+ "Manual intervention required.");
+ return 0;
+ }
+
+revert_new_lv:
+ /* FIXME Better to revert to backup of metadata? */
+ if (!lv_remove(lv) || !vg_write(vg) || !vg_commit(vg))
+ log_error("Manual intervention may be required to remove "
+ "abandoned LV(s) before retrying.");
+ else
+ backup(vg);
+
+ return 0;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "toolcontext.h"
+#include "lv_alloc.h"
+#include "pv_alloc.h"
+#include "str_list.h"
+#include "segtype.h"
+
+/*
+ * Attempt to merge two adjacent segments.
+ * Currently only supports striped segments on AREA_PV.
+ * Returns success if successful, in which case 'first'
+ * gets adjusted to contain both areas.
+ */
+static int _merge(struct lv_segment *first, struct lv_segment *second)
+{
+ if (!first || !second || first->segtype != second->segtype ||
+ !first->segtype->ops->merge_segments)
+ return 0;
+
+ return first->segtype->ops->merge_segments(first, second);
+}
+
+int lv_merge_segments(struct logical_volume *lv)
+{
+ struct dm_list *segh, *t;
+ struct lv_segment *current, *prev = NULL;
+
+ if (lv->status & LOCKED || lv->status & PVMOVE)
+ return 1;
+
+ dm_list_iterate_safe(segh, t, &lv->segments) {
+ current = dm_list_item(segh, struct lv_segment);
+
+ if (_merge(prev, current))
+ dm_list_del(¤t->list);
+ else
+ prev = current;
+ }
+
+ return 1;
+}
+
+#define ERROR_MAX 100
+#define inc_error_count \
+ if (error_count++ > ERROR_MAX) \
+ goto out
+
+/*
+ * Verify that an LV's segments are consecutive, complete and don't overlap.
+ */
+int check_lv_segments(struct logical_volume *lv, int complete_vg)
+{
+ struct lv_segment *seg, *seg2;
+ uint32_t le = 0;
+ unsigned seg_count = 0, seg_found;
+ uint32_t area_multiplier, s;
+ struct seg_list *sl;
+ int error_count = 0;
+ struct replicator_site *rsite;
+ struct replicator_device *rdev;
+
+ dm_list_iterate_items(seg, &lv->segments) {
+ seg_count++;
+ if (seg->le != le) {
+ log_error("LV %s invalid: segment %u should begin at "
+ "LE %" PRIu32 " (found %" PRIu32 ").",
+ lv->name, seg_count, le, seg->le);
+ inc_error_count;
+ }
+
+ area_multiplier = segtype_is_striped(seg->segtype) ?
+ seg->area_count : 1;
+
+ if (seg->area_len * area_multiplier != seg->len) {
+ log_error("LV %s: segment %u has inconsistent "
+ "area_len %u",
+ lv->name, seg_count, seg->area_len);
+ inc_error_count;
+ }
+
+ if (complete_vg && seg->log_lv) {
+ if (!seg_is_mirrored(seg)) {
+ log_error("LV %s: segment %u has log LV but "
+ "is not mirrored",
+ lv->name, seg_count);
+ inc_error_count;
+ }
+
+ if (!(seg->log_lv->status & MIRROR_LOG)) {
+ log_error("LV %s: segment %u log LV %s is not "
+ "a mirror log",
+ lv->name, seg_count, seg->log_lv->name);
+ inc_error_count;
+ }
+
+ if (!(seg2 = first_seg(seg->log_lv)) ||
+ find_mirror_seg(seg2) != seg) {
+ log_error("LV %s: segment %u log LV does not "
+ "point back to mirror segment",
+ lv->name, seg_count);
+ inc_error_count;
+ }
+ }
+
+ if (complete_vg && seg->status & MIRROR_IMAGE) {
+ if (!find_mirror_seg(seg) ||
+ !seg_is_mirrored(find_mirror_seg(seg))) {
+ log_error("LV %s: segment %u mirror image "
+ "is not mirrored",
+ lv->name, seg_count);
+ inc_error_count;
+ }
+ }
+
+ if (seg_is_snapshot(seg)) {
+ if (seg->cow && seg->cow == seg->origin) {
+ log_error("LV %s: segment %u has same LV %s for "
+ "both origin and snapshot",
+ lv->name, seg_count, seg->cow->name);
+ inc_error_count;
+ }
+ }
+
+ if (seg_is_replicator(seg) && !check_replicator_segment(seg))
+ inc_error_count;
+
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) == AREA_UNASSIGNED) {
+ log_error("LV %s: segment %u has unassigned "
+ "area %u.",
+ lv->name, seg_count, s);
+ inc_error_count;
+ } else if (seg_type(seg, s) == AREA_PV) {
+ if (!seg_pvseg(seg, s) ||
+ seg_pvseg(seg, s)->lvseg != seg ||
+ seg_pvseg(seg, s)->lv_area != s) {
+ log_error("LV %s: segment %u has "
+ "inconsistent PV area %u",
+ lv->name, seg_count, s);
+ inc_error_count;
+ }
+ } else {
+ if (!seg_lv(seg, s) ||
+ seg_lv(seg, s)->vg != lv->vg ||
+ seg_lv(seg, s) == lv) {
+ log_error("LV %s: segment %u has "
+ "inconsistent LV area %u",
+ lv->name, seg_count, s);
+ inc_error_count;
+ }
+
+ if (complete_vg && seg_lv(seg, s) &&
+ (seg_lv(seg, s)->status & MIRROR_IMAGE) &&
+ (!(seg2 = find_seg_by_le(seg_lv(seg, s),
+ seg_le(seg, s))) ||
+ find_mirror_seg(seg2) != seg)) {
+ log_error("LV %s: segment %u mirror "
+ "image %u missing mirror ptr",
+ lv->name, seg_count, s);
+ inc_error_count;
+ }
+
+/* FIXME I don't think this ever holds?
+ if (seg_le(seg, s) != le) {
+ log_error("LV %s: segment %u has "
+ "inconsistent LV area %u "
+ "size",
+ lv->name, seg_count, s);
+ inc_error_count;
+ }
+ */
+ seg_found = 0;
+ dm_list_iterate_items(sl, &seg_lv(seg, s)->segs_using_this_lv)
+ if (sl->seg == seg)
+ seg_found++;
+ if (!seg_found) {
+ log_error("LV %s segment %d uses LV %s,"
+ " but missing ptr from %s to %s",
+ lv->name, seg_count,
+ seg_lv(seg, s)->name,
+ seg_lv(seg, s)->name, lv->name);
+ inc_error_count;
+ } else if (seg_found > 1) {
+ log_error("LV %s has duplicated links "
+ "to LV %s segment %d",
+ seg_lv(seg, s)->name,
+ lv->name, seg_count);
+ inc_error_count;
+ }
+ }
+ }
+
+ le += seg->len;
+ }
+
+ dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
+ seg = sl->seg;
+ seg_found = 0;
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) != AREA_LV)
+ continue;
+ if (lv == seg_lv(seg, s))
+ seg_found++;
+ }
+ if (seg_is_replicator_dev(seg)) {
+ dm_list_iterate_items(rsite, &seg->replicator->rsites) {
+ dm_list_iterate_items(rdev, &rsite->rdevices) {
+ if (lv == rdev->lv || lv == rdev->slog)
+ seg_found++;
+ }
+ }
+ if (lv == seg->replicator)
+ seg_found++;
+ }
+ if (seg_is_replicator(seg) && lv == seg->rlog_lv)
+ seg_found++;
+ if (seg->log_lv == lv)
+ seg_found++;
+ if (!seg_found) {
+ log_error("LV %s is used by LV %s:%" PRIu32 "-%" PRIu32
+ ", but missing ptr from %s to %s",
+ lv->name, seg->lv->name, seg->le,
+ seg->le + seg->len - 1,
+ seg->lv->name, lv->name);
+ inc_error_count;
+ } else if (seg_found != sl->count) {
+ log_error("Reference count mismatch: LV %s has %d "
+ "links to LV %s:%" PRIu32 "-%" PRIu32
+ ", which has %d links",
+ lv->name, sl->count, seg->lv->name, seg->le,
+ seg->le + seg->len - 1, seg_found);
+ inc_error_count;
+ }
+
+ seg_found = 0;
+ dm_list_iterate_items(seg2, &seg->lv->segments)
+ if (sl->seg == seg2) {
+ seg_found++;
+ break;
+ }
+ if (!seg_found) {
+ log_error("LV segment %s:%" PRIu32 "-%" PRIu32
+ " is incorrectly listed as being used by LV %s",
+ seg->lv->name, seg->le, seg->le + seg->len - 1,
+ lv->name);
+ inc_error_count;
+ }
+ }
+
+ if (le != lv->le_count) {
+ log_error("LV %s: inconsistent LE count %u != %u",
+ lv->name, le, lv->le_count);
+ inc_error_count;
+ }
+
+out:
+ return !error_count;
+}
+
+/*
+ * Split the supplied segment at the supplied logical extent
+ * NB Use LE numbering that works across stripes PV1: 0,2,4 PV2: 1,3,5 etc.
+ */
+static int _lv_split_segment(struct logical_volume *lv, struct lv_segment *seg,
+ uint32_t le)
+{
+ struct lv_segment *split_seg;
+ uint32_t s;
+ uint32_t offset = le - seg->le;
+ uint32_t area_offset;
+
+ if (!seg_can_split(seg)) {
+ log_error("Unable to split the %s segment at LE %" PRIu32
+ " in LV %s", seg->segtype->name, le, lv->name);
+ return 0;
+ }
+
+ /* Clone the existing segment */
+ if (!(split_seg = alloc_lv_segment(lv->vg->vgmem, seg->segtype,
+ seg->lv, seg->le, seg->len,
+ seg->status, seg->stripe_size,
+ seg->log_lv,
+ seg->area_count, seg->area_len,
+ seg->chunk_size, seg->region_size,
+ seg->extents_copied, seg->pvmove_source_seg))) {
+ log_error("Couldn't allocate cloned LV segment.");
+ return 0;
+ }
+
+ if (!str_list_dup(lv->vg->vgmem, &split_seg->tags, &seg->tags)) {
+ log_error("LV segment tags duplication failed");
+ return 0;
+ }
+
+ /* In case of a striped segment, the offset has to be / stripes */
+ area_offset = offset;
+ if (seg_is_striped(seg))
+ area_offset /= seg->area_count;
+
+ split_seg->area_len -= area_offset;
+ seg->area_len = area_offset;
+
+ split_seg->len -= offset;
+ seg->len = offset;
+
+ split_seg->le = seg->le + seg->len;
+
+ /* Adjust the PV mapping */
+ for (s = 0; s < seg->area_count; s++) {
+ seg_type(split_seg, s) = seg_type(seg, s);
+
+ /* Split area at the offset */
+ switch (seg_type(seg, s)) {
+ case AREA_LV:
+ if (!set_lv_segment_area_lv(split_seg, s, seg_lv(seg, s),
+ seg_le(seg, s) + seg->area_len, 0))
+ return_0;
+ log_debug("Split %s:%u[%u] at %u: %s LE %u", lv->name,
+ seg->le, s, le, seg_lv(seg, s)->name,
+ seg_le(split_seg, s));
+ break;
+
+ case AREA_PV:
+ if (!(seg_pvseg(split_seg, s) =
+ assign_peg_to_lvseg(seg_pv(seg, s),
+ seg_pe(seg, s) +
+ seg->area_len,
+ seg_pvseg(seg, s)->len -
+ seg->area_len,
+ split_seg, s)))
+ return_0;
+ log_debug("Split %s:%u[%u] at %u: %s PE %u", lv->name,
+ seg->le, s, le,
+ dev_name(seg_dev(seg, s)),
+ seg_pe(split_seg, s));
+ break;
+
+ case AREA_UNASSIGNED:
+ log_error("Unassigned area %u found in segment", s);
+ return 0;
+ }
+ }
+
+ /* Add split off segment to the list _after_ the original one */
+ dm_list_add_h(&seg->list, &split_seg->list);
+
+ return 1;
+}
+
+/*
+ * Ensure there's a segment boundary at the given logical extent
+ */
+int lv_split_segment(struct logical_volume *lv, uint32_t le)
+{
+ struct lv_segment *seg;
+
+ if (!(seg = find_seg_by_le(lv, le))) {
+ log_error("Segment with extent %" PRIu32 " in LV %s not found",
+ le, lv->name);
+ return 0;
+ }
+
+ /* This is a segment start already */
+ if (le == seg->le)
+ return 1;
+
+ if (!_lv_split_segment(lv, seg, le))
+ return_0;
+
+ if (!vg_validate(lv->vg))
+ return_0;
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This is the representation of LVM metadata that is being adapted
+ * for library export.
+ */
+
+#ifndef _LVM_METADATA_EXPORTED_H
+#define _LVM_METADATA_EXPORTED_H
+
+#include "uuid.h"
+#include "pv.h"
+#include "vg.h"
+#include "lv.h"
+#include "lvm-percent.h"
+
+#define MAX_STRIPES 128U
+#define SECTOR_SHIFT 9L
+#define SECTOR_SIZE ( 1L << SECTOR_SHIFT )
+#define STRIPE_SIZE_MIN ( (unsigned) lvm_getpagesize() >> SECTOR_SHIFT) /* PAGESIZE in sectors */
+#define STRIPE_SIZE_MAX ( 512L * 1024L >> SECTOR_SHIFT) /* 512 KB in sectors */
+#define STRIPE_SIZE_LIMIT ((UINT_MAX >> 2) + 1)
+#define PV_MIN_SIZE ( 512L * 1024L >> SECTOR_SHIFT) /* 512 KB in sectors */
+#define MAX_RESTRICTED_LVS 255 /* Used by FMT_RESTRICTED_LVIDS */
+
+/* Layer suffix */
+#define MIRROR_SYNC_LAYER "_mimagetmp"
+
+/* Various flags */
+/* Note that the bits no longer necessarily correspond to LVM1 disk format */
+
+#define PARTIAL_VG 0x00000001U /* VG */
+#define EXPORTED_VG 0x00000002U /* VG PV */
+#define RESIZEABLE_VG 0x00000004U /* VG */
+
+/* May any free extents on this PV be used or must they be left free? */
+#define ALLOCATABLE_PV 0x00000008U /* PV */
+
+//#define SPINDOWN_LV 0x00000010U /* LV */
+//#define BADBLOCK_ON 0x00000020U /* LV */
+#define VISIBLE_LV 0x00000040U /* LV */
+#define FIXED_MINOR 0x00000080U /* LV */
+/* FIXME Remove when metadata restructuring is completed */
+#define SNAPSHOT 0x00001000U /* LV - internal use only */
+#define PVMOVE 0x00002000U /* VG LV SEG */
+#define LOCKED 0x00004000U /* LV */
+#define MIRRORED 0x00008000U /* LV - internal use only */
+//#define VIRTUAL 0x00010000U /* LV - internal use only */
+#define MIRROR_LOG 0x00020000U /* LV */
+#define MIRROR_IMAGE 0x00040000U /* LV */
+#define MIRROR_NOTSYNCED 0x00080000U /* LV */
+//#define ACTIVATE_EXCL 0x00100000U /* LV - internal use only */
+//#define PRECOMMITTED 0x00200000U /* VG - internal use only */
+#define CONVERTING 0x00400000U /* LV */
+
+#define MISSING_PV 0x00800000U /* PV */
+#define PARTIAL_LV 0x01000000U /* LV - derived flag, not
+ written out in metadata*/
+
+//#define POSTORDER_FLAG 0x02000000U /* Not real flags, reserved for
+//#define POSTORDER_OPEN_FLAG 0x04000000U temporary use inside vg_read_internal. */
+//#define VIRTUAL_ORIGIN 0x08000000U /* LV - internal use only */
+
+#define MERGING 0x10000000U /* LV SEG */
+
+#define REPLICATOR 0x20000000U /* LV -internal use only for replicator */
+#define REPLICATOR_LOG 0x40000000U /* LV -internal use only for replicator-dev */
+
+#define LVM_READ 0x00000100U /* LV VG */
+#define LVM_WRITE 0x00000200U /* LV VG */
+#define CLUSTERED 0x00000400U /* VG */
+//#define SHARED 0x00000800U /* VG */
+
+/* Format features flags */
+#define FMT_SEGMENTS 0x00000001U /* Arbitrary segment params? */
+#define FMT_MDAS 0x00000002U /* Proper metadata areas? */
+#define FMT_TAGS 0x00000004U /* Tagging? */
+#define FMT_UNLIMITED_VOLS 0x00000008U /* Unlimited PVs/LVs? */
+#define FMT_RESTRICTED_LVIDS 0x00000010U /* LVID <= 255 */
+#define FMT_ORPHAN_ALLOCATABLE 0x00000020U /* Orphan PV allocatable? */
+//#define FMT_PRECOMMIT 0x00000040U /* Supports pre-commit? */
+#define FMT_RESIZE_PV 0x00000080U /* Supports pvresize? */
+#define FMT_UNLIMITED_STRIPESIZE 0x00000100U /* Unlimited stripe size? */
+#define FMT_RESTRICTED_READAHEAD 0x00000200U /* Readahead restricted to 2-120? */
+
+/* Mirror conversion type flags */
+#define MIRROR_BY_SEG 0x00000001U /* segment-by-segment mirror */
+#define MIRROR_BY_LV 0x00000002U /* mirror using whole mimage LVs */
+#define MIRROR_SKIP_INIT_SYNC 0x00000010U /* skip initial sync */
+
+/* vg_read and vg_read_for_update flags */
+#define READ_ALLOW_INCONSISTENT 0x00010000U
+#define READ_ALLOW_EXPORTED 0x00020000U
+#define READ_WITHOUT_LOCK 0x00040000U
+
+/* A meta-flag, useful with toollib for_each_* functions. */
+#define READ_FOR_UPDATE 0x00100000U
+
+/* vg's "read_status" field */
+#define FAILED_INCONSISTENT 0x00000001U
+#define FAILED_LOCKING 0x00000002U
+#define FAILED_NOTFOUND 0x00000004U
+#define FAILED_READ_ONLY 0x00000008U
+#define FAILED_EXPORTED 0x00000010U
+#define FAILED_RESIZEABLE 0x00000020U
+#define FAILED_CLUSTERED 0x00000040U
+#define FAILED_ALLOCATION 0x00000080U
+#define FAILED_EXIST 0x00000100U
+#define SUCCESS 0x00000000U
+
+#define VGMETADATACOPIES_ALL UINT32_MAX
+#define VGMETADATACOPIES_UNMANAGED 0
+
+/* Ordered list - see lv_manip.c */
+typedef enum {
+ AREA_UNASSIGNED,
+ AREA_PV,
+ AREA_LV
+} area_type_t;
+
+/*
+ * Whether or not to force an operation.
+ */
+typedef enum {
+ PROMPT = 0, /* Issue yes/no prompt to confirm operation */
+ DONT_PROMPT = 1, /* Skip yes/no prompt */
+ DONT_PROMPT_OVERRIDE = 2 /* Skip prompt + override a second condition */
+} force_t;
+
+struct cmd_context;
+struct format_handler;
+struct labeller;
+
+struct format_type {
+ struct dm_list list;
+ struct cmd_context *cmd;
+ struct format_handler *ops;
+ struct labeller *labeller;
+ const char *name;
+ const char *alias;
+ const char *orphan_vg_name;
+ uint32_t features;
+ void *library;
+ void *private;
+};
+
+struct pv_segment {
+ struct dm_list list; /* Member of pv->segments: ordered list
+ * covering entire data area on this PV */
+
+ struct physical_volume *pv;
+ uint32_t pe;
+ uint32_t len;
+
+ struct lv_segment *lvseg; /* NULL if free space */
+ uint32_t lv_area; /* Index to area in LV segment */
+};
+
+#define pvseg_is_allocated(pvseg) ((pvseg)->lvseg)
+
+struct format_instance {
+ const struct format_type *fmt;
+ /*
+ * Each mda in a vg is on exactly one of the below lists.
+ * MDAs on the 'in_use' list will be read from / written to
+ * disk, while MDAs on the 'ignored' list will not be read
+ * or written to.
+ */
+ struct dm_list metadata_areas_in_use;
+ struct dm_list metadata_areas_ignored;
+ void *private;
+};
+
+/* There will be one area for each stripe */
+struct lv_segment_area {
+ area_type_t type;
+ union {
+ struct {
+ struct pv_segment *pvseg;
+ } pv;
+ struct {
+ struct logical_volume *lv;
+ uint32_t le;
+ } lv;
+ } u;
+};
+
+struct segment_type;
+
+/* List with vg_name, vgid and flags */
+struct cmd_vg {
+ struct dm_list list;
+ const char *vg_name;
+ const char *vgid;
+ uint32_t flags;
+ struct volume_group *vg;
+};
+
+/* ++ Replicator datatypes */
+typedef enum {
+ REPLICATOR_STATE_PASSIVE,
+ REPLICATOR_STATE_ACTIVE,
+ NUM_REPLICATOR_STATE
+} replicator_state_t;
+
+struct replicator_site {
+ struct dm_list list; /* Chained list of sites */
+ struct dm_list rdevices; /* Device list */
+
+ struct logical_volume *replicator; /* Reference to replicator */
+
+ const char *name; /* Site name */
+ const char *vg_name; /* VG name */
+ struct volume_group *vg; /* resolved vg (activate/deactive) */
+ unsigned site_index;
+ replicator_state_t state; /* Active or pasive state of site */
+ dm_replicator_mode_t op_mode; /* Operation mode sync or async fail|warn|drop|stall */
+ uint64_t fall_behind_data; /* Bytes */
+ uint32_t fall_behind_ios; /* IO operations */
+ uint32_t fall_behind_timeout; /* Seconds */
+};
+
+struct replicator_device {
+ struct dm_list list; /* Chained list of devices from same site */
+
+ struct lv_segment *replicator_dev; /* Reference to replicator-dev segment */
+ struct replicator_site *rsite; /* Reference to site parameters */
+
+ uint64_t device_index;
+ const char *name; /* Device LV name */
+ struct logical_volume *lv; /* LV from replicator site's VG */
+ struct logical_volume *slog; /* Synclog lv from VG */
+ const char *slog_name; /* Debug - specify size of core synclog */
+};
+/* -- Replicator datatypes */
+
+struct lv_segment {
+ struct dm_list list;
+ struct logical_volume *lv;
+
+ const struct segment_type *segtype;
+ uint32_t le;
+ uint32_t len;
+
+ uint64_t status;
+
+ /* FIXME Fields depend on segment type */
+ uint32_t stripe_size;
+ uint32_t area_count;
+ uint32_t area_len;
+ uint32_t chunk_size; /* For snapshots - in sectors */
+ struct logical_volume *origin;
+ struct logical_volume *cow;
+ struct dm_list origin_list;
+ uint32_t region_size; /* For mirrors, replicators - in sectors */
+ uint32_t extents_copied;
+ struct logical_volume *log_lv;
+ struct lv_segment *pvmove_source_seg;
+ void *segtype_private;
+
+ struct dm_list tags;
+
+ struct lv_segment_area *areas;
+
+ struct logical_volume *replicator;/* For replicator-devs - link to replicator LV */
+ struct logical_volume *rlog_lv; /* For replicators */
+ const char *rlog_type; /* For replicators */
+ uint64_t rdevice_index_highest; /* For replicators */
+ unsigned rsite_index_highest; /* For replicators */
+};
+
+#define seg_type(seg, s) (seg)->areas[(s)].type
+#define seg_pv(seg, s) (seg)->areas[(s)].u.pv.pvseg->pv
+#define seg_lv(seg, s) (seg)->areas[(s)].u.lv.lv
+
+struct pe_range {
+ struct dm_list list;
+ uint32_t start; /* PEs */
+ uint32_t count; /* PEs */
+};
+
+struct pv_list {
+ struct dm_list list;
+ struct physical_volume *pv;
+ struct dm_list *mdas; /* Metadata areas */
+ struct dm_list *pe_ranges; /* Ranges of PEs e.g. for allocation */
+};
+
+struct lv_list {
+ struct dm_list list;
+ struct logical_volume *lv;
+};
+
+struct pvcreate_params {
+ int zero;
+ uint64_t size;
+ uint64_t data_alignment;
+ uint64_t data_alignment_offset;
+ int pvmetadatacopies;
+ uint64_t pvmetadatasize;
+ int64_t labelsector;
+ struct id id; /* FIXME: redundant */
+ struct id *idp; /* 0 if no --uuid option */
+ uint64_t pe_start;
+ uint32_t extent_count;
+ uint32_t extent_size;
+ const char *restorefile; /* 0 if no --restorefile option */
+ force_t force;
+ unsigned yes;
+ unsigned metadataignore;
+};
+
+struct physical_volume *pvcreate_single(struct cmd_context *cmd,
+ const char *pv_name,
+ struct pvcreate_params *pp);
+void pvcreate_params_set_defaults(struct pvcreate_params *pp);
+
+/*
+* Utility functions
+*/
+int vg_write(struct volume_group *vg);
+int vg_commit(struct volume_group *vg);
+int vg_revert(struct volume_group *vg);
+struct volume_group *vg_read_internal(struct cmd_context *cmd, const char *vg_name,
+ const char *vgid, int warnings, int *consistent);
+struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name,
+ struct dm_list *mdas, uint64_t *label_sector,
+ int warnings, int scan_label_only);
+struct dm_list *get_pvs(struct cmd_context *cmd);
+
+/*
+ * Add/remove LV to/from volume group
+ */
+int link_lv_to_vg(struct volume_group *vg, struct logical_volume *lv);
+int unlink_lv_from_vg(struct logical_volume *lv);
+void lv_set_visible(struct logical_volume *lv);
+void lv_set_hidden(struct logical_volume *lv);
+
+struct dm_list *get_vgnames(struct cmd_context *cmd, int include_internal);
+struct dm_list *get_vgids(struct cmd_context *cmd, int include_internal);
+int scan_vgs_for_pvs(struct cmd_context *cmd, int warnings);
+
+int pv_write(struct cmd_context *cmd, struct physical_volume *pv,
+ struct dm_list *mdas, int64_t label_sector);
+int move_pv(struct volume_group *vg_from, struct volume_group *vg_to,
+ const char *pv_name);
+int move_pvs_used_by_lv(struct volume_group *vg_from,
+ struct volume_group *vg_to,
+ const char *lv_name);
+int is_global_vg(const char *vg_name);
+int is_orphan_vg(const char *vg_name);
+int vg_missing_pv_count(const struct volume_group *vg);
+int vgs_are_compatible(struct cmd_context *cmd,
+ struct volume_group *vg_from,
+ struct volume_group *vg_to);
+uint32_t vg_lock_newname(struct cmd_context *cmd, const char *vgname);
+
+/*
+ * Return a handle to VG metadata.
+ */
+struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name,
+ const char *vgid, uint32_t flags);
+struct volume_group *vg_read_for_update(struct cmd_context *cmd, const char *vg_name,
+ const char *vgid, uint32_t flags);
+
+/*
+ * Test validity of a VG handle.
+ */
+uint32_t vg_read_error(struct volume_group *vg_handle);
+
+/* pe_start and pe_end relate to any existing data so that new metadata
+* areas can avoid overlap */
+struct physical_volume *pv_create(const struct cmd_context *cmd,
+ struct device *dev,
+ struct id *id,
+ uint64_t size,
+ unsigned long data_alignment,
+ unsigned long data_alignment_offset,
+ uint64_t pe_start,
+ uint32_t existing_extent_count,
+ uint32_t existing_extent_size,
+ int pvmetadatacopies, uint64_t pvmetadatasize,
+ unsigned metadataignore,
+ struct dm_list *mdas);
+int pv_resize(struct physical_volume *pv, struct volume_group *vg,
+ uint32_t new_pe_count);
+int pv_analyze(struct cmd_context *cmd, const char *pv_name,
+ uint64_t label_sector);
+
+/* FIXME: move internal to library */
+uint32_t pv_list_extents_free(const struct dm_list *pvh);
+
+struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name);
+int vg_remove_mdas(struct volume_group *vg);
+int vg_remove_check(struct volume_group *vg);
+void vg_remove_pvs(struct volume_group *vg);
+int vg_remove(struct volume_group *vg);
+int vg_rename(struct cmd_context *cmd, struct volume_group *vg,
+ const char *new_name);
+int vg_extend(struct volume_group *vg, int pv_count, char **pv_names,
+ struct pvcreate_params *pp);
+int vg_reduce(struct volume_group *vg, char *pv_name);
+int vg_change_tag(struct volume_group *vg, const char *tag, int add_tag);
+int vg_split_mdas(struct cmd_context *cmd, struct volume_group *vg_from,
+ struct volume_group *vg_to);
+/* FIXME: Investigate refactoring these functions to take a pv ISO pv_list */
+void add_pvl_to_vgs(struct volume_group *vg, struct pv_list *pvl);
+void del_pvl_from_vgs(struct volume_group *vg, struct pv_list *pvl);
+
+/* FIXME: refactor / unexport when lvremove liblvm refactoring dones */
+int remove_lvs_in_vg(struct cmd_context *cmd,
+ struct volume_group *vg,
+ force_t force);
+/*
+ * free_vg() must be called on every struct volume_group allocated
+ * by vg_create() or vg_read_internal() to free it when no longer required.
+ */
+void free_vg(struct volume_group *vg);
+
+/* Manipulate LVs */
+struct logical_volume *lv_create_empty(const char *name,
+ union lvid *lvid,
+ uint64_t status,
+ alloc_policy_t alloc,
+ struct volume_group *vg);
+
+/* Write out LV contents */
+int set_lv(struct cmd_context *cmd, struct logical_volume *lv,
+ uint64_t sectors, int value);
+
+int lv_change_tag(struct logical_volume *lv, const char *tag, int add_tag);
+
+/* Reduce the size of an LV by extents */
+int lv_reduce(struct logical_volume *lv, uint32_t extents);
+
+/* Empty an LV prior to deleting it */
+int lv_empty(struct logical_volume *lv);
+
+/* Empty an LV and add error segment */
+int replace_lv_with_error_segment(struct logical_volume *lv);
+
+/* Entry point for all LV extent allocations */
+int lv_extend(struct logical_volume *lv,
+ const struct segment_type *segtype,
+ uint32_t stripes, uint32_t stripe_size,
+ uint32_t mirrors, uint32_t extents,
+ struct physical_volume *mirrored_pv, uint32_t mirrored_pe,
+ uint64_t status, struct dm_list *allocatable_pvs,
+ alloc_policy_t alloc);
+
+/* lv must be part of lv->vg->lvs */
+int lv_remove(struct logical_volume *lv);
+
+int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
+ force_t force);
+
+int lv_remove_with_dependencies(struct cmd_context *cmd, struct logical_volume *lv,
+ force_t force, unsigned level);
+
+int lv_rename(struct cmd_context *cmd, struct logical_volume *lv,
+ const char *new_name);
+
+uint64_t extents_from_size(struct cmd_context *cmd, uint64_t size,
+ uint32_t extent_size);
+
+/* FIXME: refactor and reduce the size of this struct! */
+struct lvcreate_params {
+ /* flags */
+ int snapshot; /* snap */
+ int zero; /* all */
+ int major; /* all */
+ int minor; /* all */
+ int log_count; /* mirror */
+ int nosync; /* mirror */
+ int activation_monitoring; /* all */
+
+ char *origin; /* snap */
+ const char *vg_name; /* all */
+ const char *lv_name; /* all */
+
+ uint32_t stripes; /* striped */
+ uint32_t stripe_size; /* striped */
+ uint32_t chunk_size; /* snapshot */
+ uint32_t region_size; /* mirror */
+
+ uint32_t mirrors; /* mirror */
+
+ const struct segment_type *segtype; /* all */
+
+ /* size */
+ uint32_t extents; /* all */
+ uint32_t voriginextents; /* snapshot */
+ uint64_t voriginsize; /* snapshot */
+ struct dm_list *pvh; /* all */
+
+ uint32_t permission; /* all */
+ uint32_t read_ahead; /* all */
+ alloc_policy_t alloc; /* all */
+
+ struct dm_list tags; /* all */
+};
+
+int lv_create_single(struct volume_group *vg,
+ struct lvcreate_params *lp);
+
+/*
+ * Functions for layer manipulation
+ */
+int insert_layer_for_segments_on_pv(struct cmd_context *cmd,
+ struct logical_volume *lv_where,
+ struct logical_volume *layer_lv,
+ uint64_t status,
+ struct pv_list *pv,
+ struct dm_list *lvs_changed);
+int remove_layers_for_segments(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ struct logical_volume *layer_lv,
+ uint64_t status_mask, struct dm_list *lvs_changed);
+int remove_layers_for_segments_all(struct cmd_context *cmd,
+ struct logical_volume *layer_lv,
+ uint64_t status_mask,
+ struct dm_list *lvs_changed);
+int split_parent_segments_for_layer(struct cmd_context *cmd,
+ struct logical_volume *layer_lv);
+int remove_layer_from_lv(struct logical_volume *lv,
+ struct logical_volume *layer_lv);
+struct logical_volume *insert_layer_for_lv(struct cmd_context *cmd,
+ struct logical_volume *lv_where,
+ uint64_t status,
+ const char *layer_suffix);
+
+/* Find a PV within a given VG */
+struct pv_list *find_pv_in_vg(const struct volume_group *vg,
+ const char *pv_name);
+struct pv_list *find_pv_in_vg_by_uuid(const struct volume_group *vg,
+ const struct id *id);
+
+/* Find an LV within a given VG */
+struct lv_list *find_lv_in_vg(const struct volume_group *vg,
+ const char *lv_name);
+
+/* FIXME Merge these functions with ones above */
+struct logical_volume *find_lv(const struct volume_group *vg,
+ const char *lv_name);
+struct physical_volume *find_pv_by_name(struct cmd_context *cmd,
+ const char *pv_name);
+
+const char *find_vgname_from_pvname(struct cmd_context *cmd,
+ const char *pvname);
+const char *find_vgname_from_pvid(struct cmd_context *cmd,
+ const char *pvid);
+/* Find LV segment containing given LE */
+struct lv_segment *first_seg(const struct logical_volume *lv);
+
+
+/*
+* Useful functions for managing snapshots.
+*/
+int lv_is_origin(const struct logical_volume *lv);
+int lv_is_virtual_origin(const struct logical_volume *lv);
+int lv_is_cow(const struct logical_volume *lv);
+int lv_is_merging_origin(const struct logical_volume *origin);
+int lv_is_merging_cow(const struct logical_volume *snapshot);
+
+/* Test if given LV is visible from user's perspective */
+int lv_is_visible(const struct logical_volume *lv);
+
+int pv_is_in_vg(struct volume_group *vg, struct physical_volume *pv);
+
+struct lv_segment *find_merging_cow(const struct logical_volume *origin);
+
+/* Given a cow LV, return return the snapshot lv_segment that uses it */
+struct lv_segment *find_cow(const struct logical_volume *lv);
+
+/* Given a cow LV, return its origin */
+struct logical_volume *origin_from_cow(const struct logical_volume *lv);
+
+void init_snapshot_seg(struct lv_segment *seg, struct logical_volume *origin,
+ struct logical_volume *cow, uint32_t chunk_size, int merge);
+
+void init_snapshot_merge(struct lv_segment *cow_seg, struct logical_volume *origin);
+
+void clear_snapshot_merge(struct logical_volume *origin);
+
+int vg_add_snapshot(struct logical_volume *origin, struct logical_volume *cow,
+ union lvid *lvid, uint32_t extent_count,
+ uint32_t chunk_size);
+
+int vg_remove_snapshot(struct logical_volume *cow);
+
+int vg_check_status(const struct volume_group *vg, uint64_t status);
+
+
+/*
+ * Check if the VG reached maximal LVs count (if set)
+ */
+int vg_max_lv_reached(struct volume_group *vg);
+
+/*
+* Mirroring functions
+*/
+struct lv_segment *find_mirror_seg(struct lv_segment *seg);
+int lv_add_mirrors(struct cmd_context *cmd, struct logical_volume *lv,
+ uint32_t mirrors, uint32_t stripes, uint32_t stripe_size,
+ uint32_t region_size, uint32_t log_count,
+ struct dm_list *pvs, alloc_policy_t alloc, uint32_t flags);
+int lv_split_mirror_images(struct logical_volume *lv, const char *split_lv_name,
+ uint32_t split_count, struct dm_list *removable_pvs);
+int lv_remove_mirrors(struct cmd_context *cmd, struct logical_volume *lv,
+ uint32_t mirrors, uint32_t log_count,
+ int (*is_removable)(struct logical_volume *, void *),
+ void *removable_baton, uint64_t status_mask);
+
+int is_temporary_mirror_layer(const struct logical_volume *lv);
+struct logical_volume * find_temporary_mirror(const struct logical_volume *lv);
+int lv_is_mirrored(const struct logical_volume *lv);
+uint32_t lv_mirror_count(const struct logical_volume *lv);
+uint32_t adjusted_mirror_region_size(uint32_t extent_size, uint32_t extents,
+ uint32_t region_size);
+int remove_mirrors_from_segments(struct logical_volume *lv,
+ uint32_t new_mirrors, uint64_t status_mask);
+int add_mirrors_to_segments(struct cmd_context *cmd, struct logical_volume *lv,
+ uint32_t mirrors, uint32_t region_size,
+ struct dm_list *allocatable_pvs, alloc_policy_t alloc);
+
+int remove_mirror_images(struct logical_volume *lv, uint32_t num_mirrors,
+ int (*is_removable)(struct logical_volume *, void *),
+ void *removable_baton, unsigned remove_log);
+int add_mirror_images(struct cmd_context *cmd, struct logical_volume *lv,
+ uint32_t mirrors, uint32_t stripes, uint32_t stripe_size, uint32_t region_size,
+ struct dm_list *allocatable_pvs, alloc_policy_t alloc,
+ uint32_t log_count);
+struct logical_volume *detach_mirror_log(struct lv_segment *seg);
+int attach_mirror_log(struct lv_segment *seg, struct logical_volume *lv);
+int remove_mirror_log(struct cmd_context *cmd, struct logical_volume *lv,
+ struct dm_list *removable_pvs, int force);
+int add_mirror_log(struct cmd_context *cmd, struct logical_volume *lv,
+ uint32_t log_count, uint32_t region_size,
+ struct dm_list *allocatable_pvs, alloc_policy_t alloc);
+
+int reconfigure_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirrors,
+ struct dm_list *removable_pvs, unsigned remove_log);
+int collapse_mirrored_lv(struct logical_volume *lv);
+int shift_mirror_images(struct lv_segment *mirrored_seg, unsigned mimage);
+
+/* ++ metadata/replicator_manip.c */
+int replicator_add_replicator_dev(struct logical_volume *replicator_lv,
+ struct lv_segment *rdev_seg);
+struct logical_volume *replicator_remove_replicator_dev(struct lv_segment *rdev_seg);
+int replicator_add_rlog(struct lv_segment *replicator_seg, struct logical_volume *rlog_lv);
+struct logical_volume *replicator_remove_rlog(struct lv_segment *replicator_seg);
+
+int replicator_dev_add_slog(struct replicator_device *rdev, struct logical_volume *slog_lv);
+struct logical_volume *replicator_dev_remove_slog(struct replicator_device *rdev);
+int replicator_dev_add_rimage(struct replicator_device *rdev, struct logical_volume *lv);
+struct logical_volume *replicator_dev_remove_rimage(struct replicator_device *rdev);
+
+int lv_is_active_replicator_dev(const struct logical_volume *lv);
+int lv_is_replicator(const struct logical_volume *lv);
+int lv_is_replicator_dev(const struct logical_volume *lv);
+int lv_is_rimage(const struct logical_volume *lv);
+int lv_is_rlog(const struct logical_volume *lv);
+int lv_is_slog(const struct logical_volume *lv);
+struct logical_volume *first_replicator_dev(const struct logical_volume *lv);
+/* -- metadata/replicator_manip.c */
+struct cmd_vg *cmd_vg_add(struct dm_pool *mem, struct dm_list *cmd_vgs,
+ const char *vg_name, const char *vgid,
+ uint32_t flags);
+struct cmd_vg *cmd_vg_lookup(struct dm_list *cmd_vgs,
+ const char *vg_name, const char *vgid);
+int cmd_vg_read(struct cmd_context *cmd, struct dm_list *cmd_vgs);
+void free_cmd_vgs(struct dm_list *cmd_vgs);
+
+int find_replicator_vgs(struct logical_volume *lv);
+
+int lv_read_replicator_vgs(struct logical_volume *lv);
+void lv_release_replicator_vgs(struct logical_volume *lv);
+
+struct logical_volume *find_pvmove_lv(struct volume_group *vg,
+ struct device *dev, uint32_t lv_type);
+struct logical_volume *find_pvmove_lv_from_pvname(struct cmd_context *cmd,
+ struct volume_group *vg,
+ const char *name,
+ const char *uuid,
+ uint32_t lv_type);
+const char *get_pvmove_pvname_from_lv(struct logical_volume *lv);
+const char *get_pvmove_pvname_from_lv_mirr(struct logical_volume *lv_mirr);
+percent_t copy_percent(struct logical_volume *lv_mirr);
+struct dm_list *lvs_using_lv(struct cmd_context *cmd, struct volume_group *vg,
+ struct logical_volume *lv);
+
+uint32_t find_free_lvnum(struct logical_volume *lv);
+char *generate_lv_name(struct volume_group *vg, const char *format,
+ char *buffer, size_t len);
+
+/*
+* Begin skeleton for external LVM library
+*/
+int pv_change_metadataignore(struct physical_volume *pv, uint32_t mda_ignore);
+
+
+int vg_check_write_mode(struct volume_group *vg);
+#define vg_is_clustered(vg) (vg_status((vg)) & CLUSTERED)
+#define vg_is_exported(vg) (vg_status((vg)) & EXPORTED_VG)
+#define vg_is_resizeable(vg) (vg_status((vg)) & RESIZEABLE_VG)
+
+int lv_has_unknown_segments(const struct logical_volume *lv);
+int vg_has_unknown_segments(const struct volume_group *vg);
+
+struct vgcreate_params {
+ char *vg_name;
+ uint32_t extent_size;
+ size_t max_pv;
+ size_t max_lv;
+ alloc_policy_t alloc;
+ int clustered; /* FIXME: put this into a 'status' variable instead? */
+ uint32_t vgmetadatacopies;
+};
+
+int vgcreate_params_validate(struct cmd_context *cmd,
+ struct vgcreate_params *vp);
+
+int validate_vg_rename_params(struct cmd_context *cmd,
+ const char *vg_name_old,
+ const char *vg_name_new);
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "device.h"
+#include "metadata.h"
+#include "toolcontext.h"
+#include "lvm-string.h"
+#include "lvm-file.h"
+#include "lvmcache.h"
+#include "memlock.h"
+#include "str_list.h"
+#include "pv_alloc.h"
+#include "segtype.h"
+#include "activate.h"
+#include "display.h"
+#include "locking.h"
+#include "archiver.h"
+#include "defaults.h"
+#include "filter-persistent.h"
+
+#include <math.h>
+#include <sys/param.h>
+
+static struct physical_volume *_pv_read(struct cmd_context *cmd,
+ struct dm_pool *pvmem,
+ const char *pv_name,
+ struct dm_list *mdas,
+ uint64_t *label_sector,
+ int warnings, int scan_label_only);
+
+static struct physical_volume *_find_pv_by_name(struct cmd_context *cmd,
+ const char *pv_name);
+
+static struct pv_list *_find_pv_in_vg(const struct volume_group *vg,
+ const char *pv_name);
+
+static struct pv_list *_find_pv_in_vg_by_uuid(const struct volume_group *vg,
+ const struct id *id);
+
+static uint32_t _vg_bad_status_bits(const struct volume_group *vg,
+ uint64_t status);
+
+const char _really_init[] =
+ "Really INITIALIZE physical volume \"%s\" of volume group \"%s\" [y/n]? ";
+
+static int _alignment_overrides_default(unsigned long data_alignment,
+ unsigned long default_pe_align)
+{
+ return data_alignment && (default_pe_align % data_alignment);
+}
+
+unsigned long set_pe_align(struct physical_volume *pv, unsigned long data_alignment)
+{
+ unsigned long default_pe_align, temp_pe_align;
+
+ if (pv->pe_align)
+ goto out;
+
+ if (data_alignment) {
+ /* Always use specified data_alignment */
+ pv->pe_align = data_alignment;
+ goto out;
+ }
+
+ default_pe_align = find_config_tree_int(pv->fmt->cmd,
+ "devices/default_data_alignment",
+ DEFAULT_DATA_ALIGNMENT);
+
+ if (default_pe_align)
+ /* align on 1 MiB multiple */
+ default_pe_align *= DEFAULT_PE_ALIGN;
+ else
+ /* align on 64 KiB multiple (old default) */
+ default_pe_align = DEFAULT_PE_ALIGN_OLD;
+
+ pv->pe_align = MAX((default_pe_align << SECTOR_SHIFT),
+ lvm_getpagesize()) >> SECTOR_SHIFT;
+
+ if (!pv->dev)
+ goto out;
+
+ /*
+ * Align to stripe-width of underlying md device if present
+ */
+ if (find_config_tree_bool(pv->fmt->cmd, "devices/md_chunk_alignment",
+ DEFAULT_MD_CHUNK_ALIGNMENT)) {
+ temp_pe_align = dev_md_stripe_width(pv->fmt->cmd->sysfs_dir, pv->dev);
+ if (_alignment_overrides_default(temp_pe_align, default_pe_align))
+ pv->pe_align = MAX(pv->pe_align, temp_pe_align);
+ }
+
+ /*
+ * Align to topology's minimum_io_size or optimal_io_size if present
+ * - minimum_io_size - the smallest request the device can perform
+ * w/o incurring a read-modify-write penalty (e.g. MD's chunk size)
+ * - optimal_io_size - the device's preferred unit of receiving I/O
+ * (e.g. MD's stripe width)
+ */
+ if (find_config_tree_bool(pv->fmt->cmd,
+ "devices/data_alignment_detection",
+ DEFAULT_DATA_ALIGNMENT_DETECTION)) {
+ temp_pe_align = dev_minimum_io_size(pv->fmt->cmd->sysfs_dir, pv->dev);
+ if (_alignment_overrides_default(temp_pe_align, default_pe_align))
+ pv->pe_align = MAX(pv->pe_align, temp_pe_align);
+
+ temp_pe_align = dev_optimal_io_size(pv->fmt->cmd->sysfs_dir, pv->dev);
+ if (_alignment_overrides_default(temp_pe_align, default_pe_align))
+ pv->pe_align = MAX(pv->pe_align, temp_pe_align);
+ }
+
+out:
+ log_very_verbose("%s: Setting PE alignment to %lu sectors.",
+ dev_name(pv->dev), pv->pe_align);
+
+ return pv->pe_align;
+}
+
+unsigned long set_pe_align_offset(struct physical_volume *pv,
+ unsigned long data_alignment_offset)
+{
+ if (pv->pe_align_offset)
+ goto out;
+
+ if (data_alignment_offset) {
+ /* Always use specified data_alignment_offset */
+ pv->pe_align_offset = data_alignment_offset;
+ goto out;
+ }
+
+ if (!pv->dev)
+ goto out;
+
+ if (find_config_tree_bool(pv->fmt->cmd,
+ "devices/data_alignment_offset_detection",
+ DEFAULT_DATA_ALIGNMENT_OFFSET_DETECTION)) {
+ int align_offset = dev_alignment_offset(pv->fmt->cmd->sysfs_dir,
+ pv->dev);
+ /* must handle a -1 alignment_offset; means dev is misaligned */
+ if (align_offset < 0)
+ align_offset = 0;
+ pv->pe_align_offset = MAX(pv->pe_align_offset, align_offset);
+ }
+
+out:
+ log_very_verbose("%s: Setting PE alignment offset to %lu sectors.",
+ dev_name(pv->dev), pv->pe_align_offset);
+
+ return pv->pe_align_offset;
+}
+
+void add_pvl_to_vgs(struct volume_group *vg, struct pv_list *pvl)
+{
+ dm_list_add(&vg->pvs, &pvl->list);
+ vg->pv_count++;
+ pvl->pv->vg = vg;
+}
+
+void del_pvl_from_vgs(struct volume_group *vg, struct pv_list *pvl)
+{
+ vg->pv_count--;
+ dm_list_del(&pvl->list);
+ pvl->pv->vg = NULL; /* orphan */
+}
+
+
+/**
+ * add_pv_to_vg - Add a physical volume to a volume group
+ * @vg - volume group to add to
+ * @pv_name - name of the pv (to be removed)
+ * @pv - physical volume to add to volume group
+ *
+ * Returns:
+ * 0 - failure
+ * 1 - success
+ * FIXME: remove pv_name - obtain safely from pv
+ */
+int add_pv_to_vg(struct volume_group *vg, const char *pv_name,
+ struct physical_volume *pv)
+{
+ struct pv_list *pvl;
+ struct format_instance *fid = vg->fid;
+ struct dm_pool *mem = vg->vgmem;
+ char uuid[64] __attribute__((aligned(8)));
+ struct dm_list *mdas;
+
+ log_verbose("Adding physical volume '%s' to volume group '%s'",
+ pv_name, vg->name);
+
+ if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl)))) {
+ log_error("pv_list allocation for '%s' failed", pv_name);
+ return 0;
+ }
+
+ if (!is_orphan_vg(pv->vg_name)) {
+ log_error("Physical volume '%s' is already in volume group "
+ "'%s'", pv_name, pv->vg_name);
+ return 0;
+ }
+
+ if (pv->fmt != fid->fmt) {
+ log_error("Physical volume %s is of different format type (%s)",
+ pv_name, pv->fmt->name);
+ return 0;
+ }
+
+ /* Ensure PV doesn't depend on another PV already in the VG */
+ if (pv_uses_vg(pv, vg)) {
+ log_error("Physical volume %s might be constructed from same "
+ "volume group %s", pv_name, vg->name);
+ return 0;
+ }
+
+ if (!(pv->vg_name = dm_pool_strdup(mem, vg->name))) {
+ log_error("vg->name allocation failed for '%s'", pv_name);
+ return 0;
+ }
+
+ memcpy(&pv->vgid, &vg->id, sizeof(vg->id));
+
+ /* Units of 512-byte sectors */
+ pv->pe_size = vg->extent_size;
+
+ /*
+ * pe_count must always be calculated by pv_setup
+ */
+ pv->pe_alloc_count = 0;
+
+ /*
+ * FIXME: this does not work entirely correctly in the case where a PV
+ * has 2 mdas and only one is ignored; ideally all non-ignored mdas
+ * should be placed on metadata_areas list and ignored on the
+ * metadata_areas_ignored list; however this requires another
+ * fairly complex refactoring to remove the 'mdas' parameter from both
+ * pv_setup and pv_write. For now, we only put ignored mdas on the
+ * metadata_areas_ignored list if all mdas in the PV are ignored;
+ * otherwise, we use the non-ignored list.
+ */
+ if (!pv_mda_used_count(pv))
+ mdas = &fid->metadata_areas_ignored;
+ else
+ mdas = &fid->metadata_areas_in_use;
+
+ if (!fid->fmt->ops->pv_setup(fid->fmt, UINT64_C(0), 0,
+ vg->extent_size, 0, 0, 0UL, UINT64_C(0),
+ 0, mdas, pv, vg)) {
+ log_error("Format-specific setup of physical volume '%s' "
+ "failed.", pv_name);
+ return 0;
+ }
+
+ if (_find_pv_in_vg(vg, pv_name) ||
+ _find_pv_in_vg_by_uuid(vg, &pv->id)) {
+ if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
+ stack;
+ uuid[0] = '\0';
+ }
+ log_error("Physical volume '%s (%s)' listed more than once.",
+ pv_name, uuid);
+ return 0;
+ }
+
+ if (vg->pv_count && (vg->pv_count == vg->max_pv)) {
+ log_error("No space for '%s' - volume group '%s' "
+ "holds max %d physical volume(s).", pv_name,
+ vg->name, vg->max_pv);
+ return 0;
+ }
+
+ if (!alloc_pv_segment_whole_pv(mem, pv))
+ return_0;
+
+ if ((uint64_t) vg->extent_count + pv->pe_count > UINT32_MAX) {
+ log_error("Unable to add %s to %s: new extent count (%"
+ PRIu64 ") exceeds limit (%" PRIu32 ").",
+ pv_name, vg->name,
+ (uint64_t) vg->extent_count + pv->pe_count,
+ UINT32_MAX);
+ return 0;
+ }
+
+ pvl->pv = pv;
+ add_pvl_to_vgs(vg, pvl);
+ vg->extent_count += pv->pe_count;
+ vg->free_count += pv->pe_count;
+
+ return 1;
+}
+
+static int _copy_pv(struct dm_pool *pvmem,
+ struct physical_volume *pv_to,
+ struct physical_volume *pv_from)
+{
+ memcpy(pv_to, pv_from, sizeof(*pv_to));
+
+ if (!(pv_to->vg_name = dm_pool_strdup(pvmem, pv_from->vg_name)))
+ return_0;
+
+ if (!str_list_dup(pvmem, &pv_to->tags, &pv_from->tags))
+ return_0;
+
+ if (!peg_dup(pvmem, &pv_to->segments, &pv_from->segments))
+ return_0;
+
+ return 1;
+}
+
+static struct pv_list *_copy_pvl(struct dm_pool *pvmem, struct pv_list *pvl_from)
+{
+ struct pv_list *pvl_to = NULL;
+
+ if (!(pvl_to = dm_pool_zalloc(pvmem, sizeof(*pvl_to))))
+ return_NULL;
+
+ if (!(pvl_to->pv = dm_pool_alloc(pvmem, sizeof(*pvl_to->pv))))
+ goto_bad;
+
+ if(!_copy_pv(pvmem, pvl_to->pv, pvl_from->pv))
+ goto_bad;
+
+ return pvl_to;
+bad:
+ dm_pool_free(pvmem, pvl_to);
+ return NULL;
+}
+
+int get_pv_from_vg_by_id(const struct format_type *fmt, const char *vg_name,
+ const char *vgid, const char *pvid,
+ struct physical_volume *pv)
+{
+ struct volume_group *vg;
+ struct pv_list *pvl;
+ int r = 0, consistent = 0;
+
+ if (!(vg = vg_read_internal(fmt->cmd, vg_name, vgid, 1, &consistent))) {
+ log_error("get_pv_from_vg_by_id: vg_read_internal failed to read VG %s",
+ vg_name);
+ return 0;
+ }
+
+ if (!consistent)
+ log_warn("WARNING: Volume group %s is not consistent",
+ vg_name);
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (id_equal(&pvl->pv->id, (const struct id *) pvid)) {
+ if (!_copy_pv(fmt->cmd->mem, pv, pvl->pv)) {
+ log_error("internal PV duplication failed");
+ r = 0;
+ goto out;
+ }
+ r = 1;
+ goto out;
+ }
+ }
+out:
+ free_vg(vg);
+ return r;
+}
+
+int move_pv(struct volume_group *vg_from, struct volume_group *vg_to,
+ const char *pv_name)
+{
+ struct physical_volume *pv;
+ struct pv_list *pvl;
+
+ /* FIXME: handle tags */
+ if (!(pvl = find_pv_in_vg(vg_from, pv_name))) {
+ log_error("Physical volume %s not in volume group %s",
+ pv_name, vg_from->name);
+ return 0;
+ }
+
+ if (_vg_bad_status_bits(vg_from, RESIZEABLE_VG) ||
+ _vg_bad_status_bits(vg_to, RESIZEABLE_VG))
+ return 0;
+
+ del_pvl_from_vgs(vg_from, pvl);
+ add_pvl_to_vgs(vg_to, pvl);
+
+ pv = pvl->pv;
+
+ vg_from->extent_count -= pv_pe_count(pv);
+ vg_to->extent_count += pv_pe_count(pv);
+
+ vg_from->free_count -= pv_pe_count(pv) - pv_pe_alloc_count(pv);
+ vg_to->free_count += pv_pe_count(pv) - pv_pe_alloc_count(pv);
+
+ return 1;
+}
+
+int move_pvs_used_by_lv(struct volume_group *vg_from,
+ struct volume_group *vg_to,
+ const char *lv_name)
+{
+ struct lv_segment *lvseg;
+ unsigned s;
+ struct lv_list *lvl;
+ struct logical_volume *lv;
+
+ /* FIXME: handle tags */
+ if (!(lvl = find_lv_in_vg(vg_from, lv_name))) {
+ log_error("Logical volume %s not in volume group %s",
+ lv_name, vg_from->name);
+ return 0;
+ }
+
+ if (_vg_bad_status_bits(vg_from, RESIZEABLE_VG) ||
+ _vg_bad_status_bits(vg_to, RESIZEABLE_VG))
+ return 0;
+
+ dm_list_iterate_items(lvseg, &lvl->lv->segments) {
+ if (lvseg->log_lv)
+ if (!move_pvs_used_by_lv(vg_from, vg_to,
+ lvseg->log_lv->name))
+ return_0;
+ for (s = 0; s < lvseg->area_count; s++) {
+ if (seg_type(lvseg, s) == AREA_PV) {
+ if (!move_pv(vg_from, vg_to,
+ pv_dev_name(seg_pv(lvseg, s))))
+ return_0;
+ } else if (seg_type(lvseg, s) == AREA_LV) {
+ lv = seg_lv(lvseg, s);
+ if (!move_pvs_used_by_lv(vg_from, vg_to,
+ lv->name))
+ return_0;
+ }
+ }
+ }
+ return 1;
+}
+
+static int validate_new_vg_name(struct cmd_context *cmd, const char *vg_name)
+{
+ char vg_path[PATH_MAX];
+
+ if (!validate_name(vg_name))
+ return_0;
+
+ snprintf(vg_path, PATH_MAX, "%s%s", cmd->dev_dir, vg_name);
+ if (path_exists(vg_path)) {
+ log_error("%s: already exists in filesystem", vg_path);
+ return 0;
+ }
+
+ return 1;
+}
+
+int validate_vg_rename_params(struct cmd_context *cmd,
+ const char *vg_name_old,
+ const char *vg_name_new)
+{
+ unsigned length;
+ char *dev_dir;
+
+ dev_dir = cmd->dev_dir;
+ length = strlen(dev_dir);
+
+ /* Check sanity of new name */
+ if (strlen(vg_name_new) > NAME_LEN - length - 2) {
+ log_error("New volume group path exceeds maximum length "
+ "of %d!", NAME_LEN - length - 2);
+ return 0;
+ }
+
+ if (!validate_new_vg_name(cmd, vg_name_new)) {
+ log_error("New volume group name \"%s\" is invalid",
+ vg_name_new);
+ return 0;
+ }
+
+ if (!strcmp(vg_name_old, vg_name_new)) {
+ log_error("Old and new volume group names must differ");
+ return 0;
+ }
+
+ return 1;
+}
+
+int vg_rename(struct cmd_context *cmd, struct volume_group *vg,
+ const char *new_name)
+{
+ struct dm_pool *mem = vg->vgmem;
+ struct pv_list *pvl;
+
+ vg->old_name = vg->name;
+
+ if (!(vg->name = dm_pool_strdup(mem, new_name))) {
+ log_error("vg->name allocation failed for '%s'", new_name);
+ return 0;
+ }
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (!(pvl->pv->vg_name = dm_pool_strdup(mem, new_name))) {
+ log_error("pv->vg_name allocation failed for '%s'",
+ pv_dev_name(pvl->pv));
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int remove_lvs_in_vg(struct cmd_context *cmd,
+ struct volume_group *vg,
+ force_t force)
+{
+ struct dm_list *lst;
+ struct lv_list *lvl;
+
+ while ((lst = dm_list_first(&vg->lvs))) {
+ lvl = dm_list_item(lst, struct lv_list);
+ if (!lv_remove_with_dependencies(cmd, lvl->lv, force, 0))
+ return 0;
+ }
+
+ return 1;
+}
+
+int vg_remove_check(struct volume_group *vg)
+{
+ unsigned lv_count;
+
+ if (vg_read_error(vg) || vg_missing_pv_count(vg)) {
+ log_error("Volume group \"%s\" not found, is inconsistent "
+ "or has PVs missing.", vg ? vg->name : "");
+ log_error("Consider vgreduce --removemissing if metadata "
+ "is inconsistent.");
+ return 0;
+ }
+
+ if (!vg_check_status(vg, EXPORTED_VG))
+ return 0;
+
+ lv_count = vg_visible_lvs(vg);
+
+ if (lv_count) {
+ log_error("Volume group \"%s\" still contains %u "
+ "logical volume(s)", vg->name, lv_count);
+ return 0;
+ }
+
+ if (!archive(vg))
+ return 0;
+
+ return 1;
+}
+
+void vg_remove_pvs(struct volume_group *vg)
+{
+ struct pv_list *pvl, *tpvl;
+
+ dm_list_iterate_items_safe(pvl, tpvl, &vg->pvs) {
+ del_pvl_from_vgs(vg, pvl);
+ dm_list_add(&vg->removed_pvs, &pvl->list);
+ }
+}
+
+int vg_remove(struct volume_group *vg)
+{
+ struct physical_volume *pv;
+ struct pv_list *pvl;
+ int ret = 1;
+
+ if (!lock_vol(vg->cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+ log_error("Can't get lock for orphan PVs");
+ return 0;
+ }
+
+ if (!vg_remove_mdas(vg)) {
+ log_error("vg_remove_mdas %s failed", vg->name);
+ unlock_vg(vg->cmd, VG_ORPHANS);
+ return 0;
+ }
+
+ /* init physical volumes */
+ dm_list_iterate_items(pvl, &vg->removed_pvs) {
+ pv = pvl->pv;
+ if (is_missing_pv(pv))
+ continue;
+
+ log_verbose("Removing physical volume \"%s\" from "
+ "volume group \"%s\"", pv_dev_name(pv), vg->name);
+ pv->vg_name = vg->fid->fmt->orphan_vg_name;
+ pv->status = ALLOCATABLE_PV;
+
+ if (!dev_get_size(pv_dev(pv), &pv->size)) {
+ log_error("%s: Couldn't get size.", pv_dev_name(pv));
+ ret = 0;
+ continue;
+ }
+
+ /* FIXME Write to same sector label was read from */
+ if (!pv_write(vg->cmd, pv, NULL, INT64_C(-1))) {
+ log_error("Failed to remove physical volume \"%s\""
+ " from volume group \"%s\"",
+ pv_dev_name(pv), vg->name);
+ ret = 0;
+ }
+ }
+
+ backup_remove(vg->cmd, vg->name);
+
+ if (ret)
+ log_print("Volume group \"%s\" successfully removed", vg->name);
+ else
+ log_error("Volume group \"%s\" not properly removed", vg->name);
+
+ unlock_vg(vg->cmd, VG_ORPHANS);
+ return ret;
+}
+
+/*
+ * Extend a VG by a single PV / device path
+ *
+ * Parameters:
+ * - vg: handle of volume group to extend by 'pv_name'
+ * - pv_name: device path of PV to add to VG
+ * - pp: parameters to pass to implicit pvcreate; if NULL, do not pvcreate
+ *
+ */
+static int vg_extend_single_pv(struct volume_group *vg, char *pv_name,
+ struct pvcreate_params *pp)
+{
+ struct physical_volume *pv;
+
+ pv = pv_by_path(vg->fid->fmt->cmd, pv_name);
+ if (!pv && !pp) {
+ log_error("%s not identified as an existing "
+ "physical volume", pv_name);
+ return 0;
+ } else if (!pv && pp) {
+ pv = pvcreate_single(vg->cmd, pv_name, pp);
+ if (!pv)
+ return 0;
+ }
+ if (!add_pv_to_vg(vg, pv_name, pv))
+ return 0;
+ return 1;
+}
+
+/*
+ * Extend a VG by a single PV / device path
+ *
+ * Parameters:
+ * - vg: handle of volume group to extend by 'pv_name'
+ * - pv_count: count of device paths of PVs
+ * - pv_names: device paths of PVs to add to VG
+ * - pp: parameters to pass to implicit pvcreate; if NULL, do not pvcreate
+ *
+ */
+int vg_extend(struct volume_group *vg, int pv_count, char **pv_names,
+ struct pvcreate_params *pp)
+{
+ int i;
+
+ if (_vg_bad_status_bits(vg, RESIZEABLE_VG))
+ return 0;
+
+ /* attach each pv */
+ for (i = 0; i < pv_count; i++) {
+ unescape_colons_and_at_signs(pv_names[i], NULL, NULL);
+ if (!vg_extend_single_pv(vg, pv_names[i], pp))
+ goto bad;
+ }
+
+/* FIXME Decide whether to initialise and add new mdahs to format instance */
+
+ return 1;
+
+ bad:
+ log_error("Unable to add physical volume '%s' to "
+ "volume group '%s'.", pv_names[i], vg->name);
+ return 0;
+}
+
+/* FIXME: use this inside vgreduce_single? */
+int vg_reduce(struct volume_group *vg, char *pv_name)
+{
+ struct physical_volume *pv;
+ struct pv_list *pvl;
+
+ if (_vg_bad_status_bits(vg, RESIZEABLE_VG))
+ return 0;
+
+ if (!archive(vg))
+ goto bad;
+
+ /* remove each pv */
+ if (!(pvl = find_pv_in_vg(vg, pv_name))) {
+ log_error("Physical volume %s not in volume group %s.",
+ pv_name, vg->name);
+ goto bad;
+ }
+
+ pv = pvl->pv;
+
+ if (pv_pe_alloc_count(pv)) {
+ log_error("Physical volume %s still in use.",
+ pv_name);
+ goto bad;
+ }
+
+ if (!dev_get_size(pv_dev(pv), &pv->size)) {
+ log_error("%s: Couldn't get size.", pv_name);
+ goto bad;
+ }
+
+ vg->free_count -= pv_pe_count(pv) - pv_pe_alloc_count(pv);
+ vg->extent_count -= pv_pe_count(pv);
+ del_pvl_from_vgs(vg, pvl);
+
+ /* add pv to the remove_pvs list */
+ dm_list_add(&vg->removed_pvs, &pvl->list);
+
+ return 1;
+
+ bad:
+ log_error("Unable to remove physical volume '%s' from "
+ "volume group '%s'.", pv_name, vg->name);
+ return 0;
+}
+
+int lv_change_tag(struct logical_volume *lv, const char *tag, int add_tag)
+{
+ char *tag_new;
+
+ if (!(lv->vg->fid->fmt->features & FMT_TAGS)) {
+ log_error("Logical volume %s/%s does not support tags",
+ lv->vg->name, lv->name);
+ return 0;
+ }
+
+ if (add_tag) {
+ if (!(tag_new = dm_pool_strdup(lv->vg->vgmem, tag))) {
+ log_error("Failed to duplicate tag %s from %s/%s",
+ tag, lv->vg->name, lv->name);
+ return 0;
+ }
+ if (!str_list_add(lv->vg->vgmem, &lv->tags, tag_new)) {
+ log_error("Failed to add tag %s to %s/%s",
+ tag, lv->vg->name, lv->name);
+ return 0;
+ }
+ } else {
+ if (!str_list_del(&lv->tags, tag)) {
+ log_error("Failed to remove tag %s from %s/%s",
+ tag, lv->vg->name, lv->name);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int vg_change_tag(struct volume_group *vg, const char *tag, int add_tag)
+{
+ char *tag_new;
+
+ if (!(vg->fid->fmt->features & FMT_TAGS)) {
+ log_error("Volume group %s does not support tags", vg->name);
+ return 0;
+ }
+
+ if (add_tag) {
+ if (!(tag_new = dm_pool_strdup(vg->vgmem, tag))) {
+ log_error("Failed to duplicate tag %s from %s",
+ tag, vg->name);
+ return 0;
+ }
+ if (!str_list_add(vg->vgmem, &vg->tags, tag_new)) {
+ log_error("Failed to add tag %s to volume group %s",
+ tag, vg->name);
+ return 0;
+ }
+ } else {
+ if (!str_list_del(&vg->tags, tag)) {
+ log_error("Failed to remove tag %s from volume group "
+ "%s", tag, vg->name);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+const char *strip_dir(const char *vg_name, const char *dev_dir)
+{
+ size_t len = strlen(dev_dir);
+ if (!strncmp(vg_name, dev_dir, len))
+ vg_name += len;
+
+ return vg_name;
+}
+
+/*
+ * Validate parameters to vg_create() before calling.
+ * FIXME: Move inside vg_create library function.
+ * FIXME: Change vgcreate_params struct to individual gets/sets
+ */
+int vgcreate_params_validate(struct cmd_context *cmd,
+ struct vgcreate_params *vp)
+{
+ if (!validate_new_vg_name(cmd, vp->vg_name)) {
+ log_error("New volume group name \"%s\" is invalid",
+ vp->vg_name);
+ return 1;
+ }
+
+ if (vp->alloc == ALLOC_INHERIT) {
+ log_error("Volume Group allocation policy cannot inherit "
+ "from anything");
+ return 1;
+ }
+
+ if (!vp->extent_size) {
+ log_error("Physical extent size may not be zero");
+ return 1;
+ }
+
+ if (!(cmd->fmt->features & FMT_UNLIMITED_VOLS)) {
+ if (!vp->max_lv)
+ vp->max_lv = 255;
+ if (!vp->max_pv)
+ vp->max_pv = 255;
+ if (vp->max_lv > 255 || vp->max_pv > 255) {
+ log_error("Number of volumes may not exceed 255");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Create a (struct volume_group) volume group handle from a struct volume_group pointer and a
+ * possible failure code or zero for success.
+ */
+static struct volume_group *_vg_make_handle(struct cmd_context *cmd,
+ struct volume_group *vg,
+ uint32_t failure)
+{
+ struct dm_pool *vgmem;
+
+ if (!vg) {
+ if (!(vgmem = dm_pool_create("lvm2 vg_handle", VG_MEMPOOL_CHUNK)) ||
+ !(vg = dm_pool_zalloc(vgmem, sizeof(*vg)))) {
+ log_error("Error allocating vg handle.");
+ if (vgmem)
+ dm_pool_destroy(vgmem);
+ return_NULL;
+ }
+ vg->vgmem = vgmem;
+ }
+
+ vg->read_status = failure;
+
+ return (struct volume_group *)vg;
+}
+
+int lv_has_unknown_segments(const struct logical_volume *lv)
+{
+ struct lv_segment *seg;
+ /* foreach segment */
+ dm_list_iterate_items(seg, &lv->segments)
+ if (seg_unknown(seg))
+ return 1;
+ return 0;
+}
+
+int vg_has_unknown_segments(const struct volume_group *vg)
+{
+ struct lv_list *lvl;
+
+ /* foreach LV */
+ dm_list_iterate_items(lvl, &vg->lvs)
+ if (lv_has_unknown_segments(lvl->lv))
+ return 1;
+ return 0;
+}
+
+/*
+ * Create a VG with default parameters.
+ * Returns:
+ * - struct volume_group* with SUCCESS code: VG structure created
+ * - NULL or struct volume_group* with FAILED_* code: error creating VG structure
+ * Use vg_read_error() to determine success or failure.
+ * FIXME: cleanup usage of _vg_make_handle()
+ */
+struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name)
+{
+ struct volume_group *vg;
+ int consistent = 0;
+ struct dm_pool *mem;
+ uint32_t rc;
+
+ if (!validate_name(vg_name)) {
+ log_error("Invalid vg name %s", vg_name);
+ /* FIXME: use _vg_make_handle() w/proper error code */
+ return NULL;
+ }
+
+ rc = vg_lock_newname(cmd, vg_name);
+ if (rc != SUCCESS)
+ /* NOTE: let caller decide - this may be check for existence */
+ return _vg_make_handle(cmd, NULL, rc);
+
+ /* FIXME: Is this vg_read_internal necessary? Move it inside
+ vg_lock_newname? */
+ /* is this vg name already in use ? */
+ if ((vg = vg_read_internal(cmd, vg_name, NULL, 1, &consistent))) {
+ log_error("A volume group called '%s' already exists.", vg_name);
+ unlock_and_free_vg(cmd, vg, vg_name);
+ return _vg_make_handle(cmd, NULL, FAILED_EXIST);
+ }
+
+ if (!(mem = dm_pool_create("lvm2 vg_create", VG_MEMPOOL_CHUNK)))
+ goto_bad;
+
+ if (!(vg = dm_pool_zalloc(mem, sizeof(*vg))))
+ goto_bad;
+
+ if (!id_create(&vg->id)) {
+ log_error("Couldn't create uuid for volume group '%s'.",
+ vg_name);
+ goto bad;
+ }
+
+ /* Strip dev_dir if present */
+ vg_name = strip_dir(vg_name, cmd->dev_dir);
+
+ vg->vgmem = mem;
+ vg->cmd = cmd;
+
+ if (!(vg->name = dm_pool_strdup(mem, vg_name)))
+ goto_bad;
+
+ vg->seqno = 0;
+
+ vg->status = (RESIZEABLE_VG | LVM_READ | LVM_WRITE);
+ if (!(vg->system_id = dm_pool_alloc(mem, NAME_LEN)))
+ goto_bad;
+
+ *vg->system_id = '\0';
+
+ vg->extent_size = DEFAULT_EXTENT_SIZE * 2;
+ vg->extent_count = 0;
+ vg->free_count = 0;
+
+ vg->max_lv = DEFAULT_MAX_LV;
+ vg->max_pv = DEFAULT_MAX_PV;
+
+ vg->alloc = DEFAULT_ALLOC_POLICY;
+ vg->mda_copies = DEFAULT_VGMETADATACOPIES;
+
+ vg->pv_count = 0;
+ dm_list_init(&vg->pvs);
+
+ dm_list_init(&vg->lvs);
+
+ dm_list_init(&vg->tags);
+
+ /* initialize removed_pvs list */
+ dm_list_init(&vg->removed_pvs);
+
+ if (!(vg->fid = cmd->fmt->ops->create_instance(cmd->fmt, vg_name,
+ NULL, NULL))) {
+ log_error("Failed to create format instance");
+ goto bad;
+ }
+
+ if (vg->fid->fmt->ops->vg_setup &&
+ !vg->fid->fmt->ops->vg_setup(vg->fid, vg)) {
+ log_error("Format specific setup of volume group '%s' failed.",
+ vg_name);
+ goto bad;
+ }
+ return _vg_make_handle(cmd, vg, SUCCESS);
+
+bad:
+ unlock_and_free_vg(cmd, vg, vg_name);
+ /* FIXME: use _vg_make_handle() w/proper error code */
+ return NULL;
+}
+
+uint64_t extents_from_size(struct cmd_context *cmd, uint64_t size,
+ uint32_t extent_size)
+{
+ if (size % extent_size) {
+ size += extent_size - size % extent_size;
+ log_print("Rounding up size to full physical extent %s",
+ display_size(cmd, size));
+ }
+
+ if (size > (uint64_t) UINT32_MAX * extent_size) {
+ log_error("Volume too large (%s) for extent size %s. "
+ "Upper limit is %s.",
+ display_size(cmd, size),
+ display_size(cmd, (uint64_t) extent_size),
+ display_size(cmd, (uint64_t) UINT32_MAX *
+ extent_size));
+ return 0;
+ }
+
+ return (uint64_t) size / extent_size;
+}
+
+/*
+ * Return random integer in [0,max) interval
+ *
+ * The loop rejects numbers that come from an "incomplete" slice of the
+ * RAND_MAX space (considering the number space [0, RAND_MAX] is divided
+ * into some "max"-sized slices and at most a single smaller slice,
+ * between [n*max, RAND_MAX] for suitable n -- numbers from this last slice
+ * are discarded because they could distort the distribution in favour of
+ * smaller numbers.
+ */
+static unsigned _even_rand( unsigned *seed, unsigned max )
+{
+ unsigned r, ret;
+
+ /* make sure distribution is even */
+ do {
+ r = (unsigned) rand_r( seed );
+ ret = r % max;
+ } while ( r - ret > RAND_MAX - max );
+
+ return ret;
+}
+
+static dm_bitset_t _bitset_with_random_bits(struct dm_pool *mem, uint32_t num_bits,
+ uint32_t num_set_bits, unsigned *seed)
+{
+ dm_bitset_t bs;
+ unsigned bit_selected;
+ char buf[32];
+ uint32_t i = num_bits - num_set_bits;
+
+ if (!(bs = dm_bitset_create(mem, (unsigned) num_bits))) {
+ log_error("Failed to allocate bitset for setting random bits.");
+ return NULL;
+ }
+
+ if (!dm_pool_begin_object(mem, 512)) {
+ log_error("dm_pool_begin_object failed for random list of bits.");
+ dm_pool_free(mem, bs);
+ return NULL;
+ }
+
+ /* Perform loop num_set_bits times, selecting one bit each time */
+ while (i++ < num_bits) {
+ /* Select a random bit between 0 and (i-1) inclusive. */
+ bit_selected = _even_rand(seed, i);
+
+ /*
+ * If the bit was already set, set the new bit that became
+ * choosable for the first time during this pass.
+ * This maintains a uniform probability distribution by compensating
+ * for being unable to select it until this pass.
+ */
+ if (dm_bit(bs, bit_selected))
+ bit_selected = i - 1;
+
+ dm_bit_set(bs, bit_selected);
+
+ if (dm_snprintf(buf, sizeof(buf), "%u ", bit_selected) < 0) {
+ log_error("snprintf random bit failed.");
+ dm_pool_free(mem, bs);
+ return NULL;
+ }
+ if (!dm_pool_grow_object(mem, buf, strlen(buf))) {
+ log_error("Failed to generate list of random bits.");
+ dm_pool_free(mem, bs);
+ return NULL;
+ }
+ }
+
+ log_debug("Selected %" PRIu32 " random bits from %" PRIu32 ": %s", num_set_bits, num_bits, (char *) dm_pool_end_object(mem));
+
+ return bs;
+}
+
+static int _vg_ignore_mdas(struct volume_group *vg, uint32_t num_to_ignore)
+{
+ struct metadata_area *mda;
+ uint32_t mda_used_count = vg_mda_used_count(vg);
+ dm_bitset_t mda_to_ignore_bs;
+ int r = 1;
+
+ log_debug("Adjusting ignored mdas for %s: %" PRIu32 " of %" PRIu32 " mdas in use "
+ "but %" PRIu32 " required. Changing %" PRIu32 " mda.",
+ vg->name, mda_used_count, vg_mda_count(vg), vg_mda_copies(vg), num_to_ignore);
+
+ if (!num_to_ignore)
+ return 1;
+
+ if (!(mda_to_ignore_bs = _bitset_with_random_bits(vg->vgmem, mda_used_count,
+ num_to_ignore, &vg->cmd->rand_seed)))
+ return_0;
+
+ dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use)
+ if (!mda_is_ignored(mda) && (--mda_used_count,
+ dm_bit(mda_to_ignore_bs, mda_used_count))) {
+ mda_set_ignored(mda, 1);
+ if (!--num_to_ignore)
+ goto out;
+ }
+
+ log_error(INTERNAL_ERROR "Unable to find %"PRIu32" metadata areas to ignore "
+ "on volume group %s", num_to_ignore, vg->name);
+
+ r = 0;
+
+out:
+ dm_pool_free(vg->vgmem, mda_to_ignore_bs);
+ return r;
+}
+
+static int _vg_unignore_mdas(struct volume_group *vg, uint32_t num_to_unignore)
+{
+ struct metadata_area *mda, *tmda;
+ uint32_t mda_used_count = vg_mda_used_count(vg);
+ uint32_t mda_count = vg_mda_count(vg);
+ uint32_t mda_free_count = mda_count - mda_used_count;
+ dm_bitset_t mda_to_unignore_bs;
+ int r = 1;
+
+ if (!num_to_unignore)
+ return 1;
+
+ log_debug("Adjusting ignored mdas for %s: %" PRIu32 " of %" PRIu32 " mdas in use "
+ "but %" PRIu32 " required. Changing %" PRIu32 " mda.",
+ vg->name, mda_used_count, mda_count, vg_mda_copies(vg), num_to_unignore);
+
+ if (!(mda_to_unignore_bs = _bitset_with_random_bits(vg->vgmem, mda_free_count,
+ num_to_unignore, &vg->cmd->rand_seed)))
+ return_0;
+
+ dm_list_iterate_items_safe(mda, tmda, &vg->fid->metadata_areas_ignored)
+ if (mda_is_ignored(mda) && (--mda_free_count,
+ dm_bit(mda_to_unignore_bs, mda_free_count))) {
+ mda_set_ignored(mda, 0);
+ dm_list_move(&vg->fid->metadata_areas_in_use,
+ &mda->list);
+ if (!--num_to_unignore)
+ goto out;
+ }
+
+ dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use)
+ if (mda_is_ignored(mda) && (--mda_free_count,
+ dm_bit(mda_to_unignore_bs, mda_free_count))) {
+ mda_set_ignored(mda, 0);
+ if (!--num_to_unignore)
+ goto out;
+ }
+
+ log_error(INTERNAL_ERROR "Unable to find %"PRIu32" metadata areas to unignore "
+ "on volume group %s", num_to_unignore, vg->name);
+
+ r = 0;
+
+out:
+ dm_pool_free(vg->vgmem, mda_to_unignore_bs);
+ return r;
+}
+
+static int _vg_adjust_ignored_mdas(struct volume_group *vg)
+{
+ uint32_t mda_copies_used = vg_mda_used_count(vg);
+
+ if (vg->mda_copies == VGMETADATACOPIES_UNMANAGED) {
+ /* Ensure at least one mda is in use. */
+ if (!mda_copies_used && vg_mda_count(vg) && !_vg_unignore_mdas(vg, 1))
+ return_0;
+ else
+ return 1;
+ }
+
+
+ /* Not an error to have vg_mda_count larger than total mdas. */
+ if (vg->mda_copies == VGMETADATACOPIES_ALL ||
+ vg->mda_copies >= vg_mda_count(vg)) {
+ /* Use all */
+ if (!_vg_unignore_mdas(vg, vg_mda_count(vg) - mda_copies_used))
+ return_0;
+ } else if (mda_copies_used < vg->mda_copies) {
+ if (!_vg_unignore_mdas(vg, vg->mda_copies - mda_copies_used))
+ return_0;
+ } else if (mda_copies_used > vg->mda_copies)
+ if (!_vg_ignore_mdas(vg, mda_copies_used - vg->mda_copies))
+ return_0;
+
+ /*
+ * The VGMETADATACOPIES_ALL value will never be written disk.
+ * It is a special cmdline value that means 2 things:
+ * 1. clear all ignore bits in all mdas in this vg
+ * 2. set the "unmanaged" policy going forward for metadata balancing
+ */
+ if (vg->mda_copies == VGMETADATACOPIES_ALL)
+ vg->mda_copies = VGMETADATACOPIES_UNMANAGED;
+
+ return 1;
+}
+
+uint64_t find_min_mda_size(struct dm_list *mdas)
+{
+ uint64_t min_mda_size = UINT64_MAX, mda_size;
+ struct metadata_area *mda;
+
+ dm_list_iterate_items(mda, mdas) {
+ if (!mda->ops->mda_total_sectors)
+ continue;
+ mda_size = mda->ops->mda_total_sectors(mda);
+ if (mda_size < min_mda_size)
+ min_mda_size = mda_size;
+ }
+
+ if (min_mda_size == UINT64_MAX)
+ min_mda_size = UINT64_C(0);
+
+ return min_mda_size;
+}
+
+static int _move_mdas(struct volume_group *vg_from, struct volume_group *vg_to,
+ struct dm_list *mdas_from, struct dm_list *mdas_to)
+{
+ struct metadata_area *mda, *mda2;
+ int common_mda = 0;
+
+ dm_list_iterate_items_safe(mda, mda2, mdas_from) {
+ if (!mda->ops->mda_in_vg) {
+ common_mda = 1;
+ continue;
+ }
+
+ if (!mda->ops->mda_in_vg(vg_from->fid, vg_from, mda)) {
+ if (is_orphan_vg(vg_to->name))
+ dm_list_del(&mda->list);
+ else
+ dm_list_move(mdas_to, &mda->list);
+ }
+ }
+ return common_mda;
+}
+
+/*
+ * Separate metadata areas after splitting a VG.
+ * Also accepts orphan VG as destination (for vgreduce).
+ */
+int vg_split_mdas(struct cmd_context *cmd __attribute__((unused)),
+ struct volume_group *vg_from, struct volume_group *vg_to)
+{
+ struct dm_list *mdas_from_in_use, *mdas_to_in_use;
+ struct dm_list *mdas_from_ignored, *mdas_to_ignored;
+ int common_mda = 0;
+
+ mdas_from_in_use = &vg_from->fid->metadata_areas_in_use;
+ mdas_from_ignored = &vg_from->fid->metadata_areas_ignored;
+ mdas_to_in_use = &vg_to->fid->metadata_areas_in_use;
+ mdas_to_ignored = &vg_to->fid->metadata_areas_ignored;
+
+ common_mda = _move_mdas(vg_from, vg_to,
+ mdas_from_in_use, mdas_to_in_use);
+ common_mda = _move_mdas(vg_from, vg_to,
+ mdas_from_ignored, mdas_to_ignored);
+
+ if ((dm_list_empty(mdas_from_in_use) &&
+ dm_list_empty(mdas_from_ignored)) ||
+ ((!is_orphan_vg(vg_to->name) &&
+ dm_list_empty(mdas_to_in_use) &&
+ dm_list_empty(mdas_to_ignored))))
+ return common_mda;
+
+ return 1;
+}
+
+static int _wipe_sb(struct device *dev, const char *type, const char *name,
+ int wipe_len, struct pvcreate_params *pp,
+ int (*func)(struct device *dev, uint64_t *signature))
+{
+ int wipe;
+ uint64_t superblock;
+
+ wipe = func(dev, &superblock);
+ if (wipe == -1) {
+ log_error("Fatal error while trying to detect %s on %s.",
+ type, name);
+ return 0;
+ }
+
+ if (wipe == 0)
+ return 1;
+
+ /* Specifying --yes => do not ask. */
+ if (!pp->yes && (pp->force == PROMPT) &&
+ yes_no_prompt("WARNING: %s detected on %s. Wipe it? [y/n] ",
+ type, name) != 'y') {
+ log_error("Aborting pvcreate on %s.", name);
+ return 0;
+ }
+
+ log_print("Wiping %s on %s.", type, name);
+ if (!dev_set(dev, superblock, wipe_len, 0)) {
+ log_error("Failed to wipe %s on %s.", type, name);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * See if we may pvcreate on this device.
+ * 0 indicates we may not.
+ */
+static int pvcreate_check(struct cmd_context *cmd, const char *name,
+ struct pvcreate_params *pp)
+{
+ struct physical_volume *pv;
+ struct device *dev;
+ struct dm_list mdas;
+
+ dm_list_init(&mdas);
+
+ /* FIXME Check partition type is LVM unless --force is given */
+
+ /* Is there a pv here already? */
+ pv = pv_read(cmd, name, &mdas, NULL, 0, 0);
+
+ /*
+ * If a PV has no MDAs it may appear to be an orphan until the
+ * metadata is read off another PV in the same VG. Detecting
+ * this means checking every VG by scanning every PV on the
+ * system.
+ */
+ if (pv && is_orphan(pv) && mdas_empty_or_ignored(&mdas)) {
+ if (!scan_vgs_for_pvs(cmd, 0))
+ return_0;
+ pv = pv_read(cmd, name, NULL, NULL, 0, 0);
+ }
+
+ /* Allow partial & exported VGs to be destroyed. */
+ /* We must have -ff to overwrite a non orphan */
+ if (pv && !is_orphan(pv) && pp->force != DONT_PROMPT_OVERRIDE) {
+ log_error("Can't initialize physical volume \"%s\" of "
+ "volume group \"%s\" without -ff", name, pv_vg_name(pv));
+ return 0;
+ }
+
+ /* prompt */
+ if (pv && !is_orphan(pv) && !pp->yes &&
+ yes_no_prompt(_really_init, name, pv_vg_name(pv)) == 'n') {
+ log_error("%s: physical volume not initialized", name);
+ return 0;
+ }
+
+ if (sigint_caught())
+ return 0;
+
+ dev = dev_cache_get(name, cmd->filter);
+
+ /* Is there an md superblock here? */
+ /* FIXME: still possible issues here - rescan cache? */
+ if (!dev && md_filtering()) {
+ refresh_filters(cmd);
+ init_md_filtering(0);
+ dev = dev_cache_get(name, cmd->filter);
+ init_md_filtering(1);
+ }
+
+ if (!dev) {
+ log_error("Device %s not found (or ignored by filtering).", name);
+ return 0;
+ }
+
+ /*
+ * This test will fail if the device belongs to an MD array.
+ */
+ if (!dev_test_excl(dev)) {
+ /* FIXME Detect whether device-mapper itself is still using it */
+ log_error("Can't open %s exclusively. Mounted filesystem?",
+ name);
+ return 0;
+ }
+
+ if (!_wipe_sb(dev, "software RAID md superblock", name, 4, pp, dev_is_md))
+ return 0;
+
+ if (!_wipe_sb(dev, "swap signature", name, 10, pp, dev_is_swap))
+ return 0;
+
+ if (!_wipe_sb(dev, "LUKS signature", name, 8, pp, dev_is_luks))
+ return 0;
+
+ if (sigint_caught())
+ return 0;
+
+ if (pv && !is_orphan(pv) && pp->force) {
+ log_warn("WARNING: Forcing physical volume creation on "
+ "%s%s%s%s", name,
+ !is_orphan(pv) ? " of volume group \"" : "",
+ !is_orphan(pv) ? pv_vg_name(pv) : "",
+ !is_orphan(pv) ? "\"" : "");
+ }
+
+ return 1;
+}
+
+void pvcreate_params_set_defaults(struct pvcreate_params *pp)
+{
+ memset(pp, 0, sizeof(*pp));
+ pp->zero = 1;
+ pp->size = 0;
+ pp->data_alignment = UINT64_C(0);
+ pp->data_alignment_offset = UINT64_C(0);
+ pp->pvmetadatacopies = DEFAULT_PVMETADATACOPIES;
+ pp->pvmetadatasize = DEFAULT_PVMETADATASIZE;
+ pp->labelsector = DEFAULT_LABELSECTOR;
+ pp->idp = 0;
+ pp->pe_start = 0;
+ pp->extent_count = 0;
+ pp->extent_size = 0;
+ pp->restorefile = 0;
+ pp->force = PROMPT;
+ pp->yes = 0;
+ pp->metadataignore = DEFAULT_PVMETADATAIGNORE;
+}
+
+/*
+ * pvcreate_single() - initialize a device with PV label and metadata area
+ *
+ * Parameters:
+ * - pv_name: device path to initialize
+ * - pp: parameters to pass to pv_create; if NULL, use default values
+ *
+ * Returns:
+ * NULL: error
+ * struct physical_volume * (non-NULL): handle to physical volume created
+ */
+struct physical_volume * pvcreate_single(struct cmd_context *cmd,
+ const char *pv_name,
+ struct pvcreate_params *pp)
+{
+ struct physical_volume *pv;
+ struct device *dev;
+ struct dm_list mdas;
+ struct pvcreate_params default_pp;
+ char buffer[64] __attribute__((aligned(8)));
+
+ pvcreate_params_set_defaults(&default_pp);
+ if (!pp)
+ pp = &default_pp;
+
+ if (pp->idp) {
+ if ((dev = device_from_pvid(cmd, pp->idp, NULL)) &&
+ (dev != dev_cache_get(pv_name, cmd->filter))) {
+ if (!id_write_format((const struct id*)&pp->idp->uuid,
+ buffer, sizeof(buffer)))
+ return_NULL;
+ log_error("uuid %s already in use on \"%s\"", buffer,
+ dev_name(dev));
+ return NULL;
+ }
+ }
+
+ if (!pvcreate_check(cmd, pv_name, pp))
+ goto error;
+
+ if (sigint_caught())
+ goto error;
+
+ if (!(dev = dev_cache_get(pv_name, cmd->filter))) {
+ log_error("%s: Couldn't find device. Check your filters?",
+ pv_name);
+ goto error;
+ }
+
+ dm_list_init(&mdas);
+ if (!(pv = pv_create(cmd, dev, pp->idp, pp->size,
+ pp->data_alignment, pp->data_alignment_offset,
+ pp->pe_start, pp->extent_count, pp->extent_size,
+ pp->pvmetadatacopies, pp->pvmetadatasize,
+ pp->metadataignore, &mdas))) {
+ log_error("Failed to setup physical volume \"%s\"", pv_name);
+ goto error;
+ }
+
+ log_verbose("Set up physical volume for \"%s\" with %" PRIu64
+ " available sectors", pv_name, pv_size(pv));
+
+ /* Wipe existing label first */
+ if (!label_remove(pv_dev(pv))) {
+ log_error("Failed to wipe existing label on %s", pv_name);
+ goto error;
+ }
+
+ if (pp->zero) {
+ log_verbose("Zeroing start of device %s", pv_name);
+ if (!dev_open_quiet(dev)) {
+ log_error("%s not opened: device not zeroed", pv_name);
+ goto error;
+ }
+
+ if (!dev_set(dev, UINT64_C(0), (size_t) 2048, 0)) {
+ log_error("%s not wiped: aborting", pv_name);
+ dev_close(dev);
+ goto error;
+ }
+ dev_close(dev);
+ }
+
+ log_very_verbose("Writing physical volume data to disk \"%s\"",
+ pv_name);
+
+ if (!(pv_write(cmd, pv, &mdas, pp->labelsector))) {
+ log_error("Failed to write physical volume \"%s\"", pv_name);
+ goto error;
+ }
+
+ log_print("Physical volume \"%s\" successfully created", pv_name);
+
+ return pv;
+
+ error:
+ return NULL;
+}
+
+static void _free_pv(struct dm_pool *mem, struct physical_volume *pv)
+{
+ dm_pool_free(mem, pv);
+}
+
+static struct physical_volume *_alloc_pv(struct dm_pool *mem, struct device *dev)
+{
+ struct physical_volume *pv = dm_pool_zalloc(mem, sizeof(*pv));
+
+ if (!pv)
+ return_NULL;
+
+ pv->pe_size = 0;
+ pv->pe_start = 0;
+ pv->pe_count = 0;
+ pv->pe_alloc_count = 0;
+ pv->pe_align = 0;
+ pv->pe_align_offset = 0;
+ pv->fmt = NULL;
+ pv->dev = dev;
+
+ pv->status = ALLOCATABLE_PV;
+
+ dm_list_init(&pv->tags);
+ dm_list_init(&pv->segments);
+
+ return pv;
+}
+
+/**
+ * pv_create - initialize a physical volume for use with a volume group
+ *
+ * @fmt: format type
+ * @dev: PV device to initialize
+ * @size: size of the PV in sectors
+ * @data_alignment: requested alignment of data
+ * @data_alignment_offset: requested offset to aligned data
+ * @pe_start: physical extent start
+ * @existing_extent_count
+ * @existing_extent_size
+ * @pvmetadatacopies
+ * @pvmetadatasize
+ * @mdas
+ *
+ * Returns:
+ * PV handle - physical volume initialized successfully
+ * NULL - invalid parameter or problem initializing the physical volume
+ *
+ * Note:
+ * FIXME: shorten argument list and replace with explict 'set' functions
+ */
+struct physical_volume *pv_create(const struct cmd_context *cmd,
+ struct device *dev,
+ struct id *id, uint64_t size,
+ unsigned long data_alignment,
+ unsigned long data_alignment_offset,
+ uint64_t pe_start,
+ uint32_t existing_extent_count,
+ uint32_t existing_extent_size,
+ int pvmetadatacopies, uint64_t pvmetadatasize,
+ unsigned metadataignore, struct dm_list *mdas)
+{
+ const struct format_type *fmt = cmd->fmt;
+ struct dm_pool *mem = fmt->cmd->mem;
+ struct physical_volume *pv = _alloc_pv(mem, dev);
+
+ if (!pv)
+ return NULL;
+
+ if (id)
+ memcpy(&pv->id, id, sizeof(*id));
+ else if (!id_create(&pv->id)) {
+ log_error("Failed to create random uuid for %s.",
+ dev_name(dev));
+ goto bad;
+ }
+
+ if (!dev_get_size(pv->dev, &pv->size)) {
+ log_error("%s: Couldn't get size.", pv_dev_name(pv));
+ goto bad;
+ }
+
+ if (size) {
+ if (size > pv->size)
+ log_warn("WARNING: %s: Overriding real size. "
+ "You could lose data.", pv_dev_name(pv));
+ log_verbose("%s: Pretending size is %" PRIu64 " sectors.",
+ pv_dev_name(pv), size);
+ pv->size = size;
+ }
+
+ if (pv->size < PV_MIN_SIZE) {
+ log_error("%s: Size must exceed minimum of %ld sectors.",
+ pv_dev_name(pv), PV_MIN_SIZE);
+ goto bad;
+ }
+
+ if (pv->size < data_alignment) {
+ log_error("%s: Data alignment must not exceed device size.",
+ pv_dev_name(pv));
+ goto bad;
+ }
+
+ pv->fmt = fmt;
+ pv->vg_name = fmt->orphan_vg_name;
+
+ if (!fmt->ops->pv_setup(fmt, pe_start, existing_extent_count,
+ existing_extent_size, data_alignment,
+ data_alignment_offset,
+ pvmetadatacopies, pvmetadatasize,
+ metadataignore, mdas, pv, NULL)) {
+ log_error("%s: Format-specific setup of physical volume "
+ "failed.", pv_dev_name(pv));
+ goto bad;
+ }
+
+ return pv;
+
+ bad:
+ _free_pv(mem, pv);
+ return NULL;
+}
+
+/* FIXME: liblvm todo - make into function that returns handle */
+struct pv_list *find_pv_in_vg(const struct volume_group *vg,
+ const char *pv_name)
+{
+ return _find_pv_in_vg(vg, pv_name);
+}
+
+static struct pv_list *_find_pv_in_vg(const struct volume_group *vg,
+ const char *pv_name)
+{
+ struct pv_list *pvl;
+
+ dm_list_iterate_items(pvl, &vg->pvs)
+ if (pvl->pv->dev == dev_cache_get(pv_name, vg->cmd->filter))
+ return pvl;
+
+ return NULL;
+}
+
+struct pv_list *find_pv_in_pv_list(const struct dm_list *pl,
+ const struct physical_volume *pv)
+{
+ struct pv_list *pvl;
+
+ dm_list_iterate_items(pvl, pl)
+ if (pvl->pv == pv)
+ return pvl;
+
+ return NULL;
+}
+
+int pv_is_in_vg(struct volume_group *vg, struct physical_volume *pv)
+{
+ struct pv_list *pvl;
+
+ dm_list_iterate_items(pvl, &vg->pvs)
+ if (pv == pvl->pv)
+ return 1;
+
+ return 0;
+}
+
+static struct pv_list *_find_pv_in_vg_by_uuid(const struct volume_group *vg,
+ const struct id *id)
+{
+ struct pv_list *pvl;
+
+ dm_list_iterate_items(pvl, &vg->pvs)
+ if (id_equal(&pvl->pv->id, id))
+ return pvl;
+
+ return NULL;
+}
+
+/**
+ * find_pv_in_vg_by_uuid - Find PV in VG by PV UUID
+ * @vg: volume group to search
+ * @id: UUID of the PV to match
+ *
+ * Returns:
+ * struct pv_list within owning struct volume_group - if UUID of PV found in VG
+ * NULL - invalid parameter or UUID of PV not found in VG
+ *
+ * Note
+ * FIXME - liblvm todo - make into function that takes VG handle
+ */
+struct pv_list *find_pv_in_vg_by_uuid(const struct volume_group *vg,
+ const struct id *id)
+{
+ return _find_pv_in_vg_by_uuid(vg, id);
+}
+
+struct lv_list *find_lv_in_vg(const struct volume_group *vg,
+ const char *lv_name)
+{
+ struct lv_list *lvl;
+ const char *ptr;
+
+ /* Use last component */
+ if ((ptr = strrchr(lv_name, '/')))
+ ptr++;
+ else
+ ptr = lv_name;
+
+ dm_list_iterate_items(lvl, &vg->lvs)
+ if (!strcmp(lvl->lv->name, ptr))
+ return lvl;
+
+ return NULL;
+}
+
+struct lv_list *find_lv_in_lv_list(const struct dm_list *ll,
+ const struct logical_volume *lv)
+{
+ struct lv_list *lvl;
+
+ dm_list_iterate_items(lvl, ll)
+ if (lvl->lv == lv)
+ return lvl;
+
+ return NULL;
+}
+
+struct lv_list *find_lv_in_vg_by_lvid(struct volume_group *vg,
+ const union lvid *lvid)
+{
+ struct lv_list *lvl;
+
+ dm_list_iterate_items(lvl, &vg->lvs)
+ if (!strncmp(lvl->lv->lvid.s, lvid->s, sizeof(*lvid)))
+ return lvl;
+
+ return NULL;
+}
+
+struct logical_volume *find_lv(const struct volume_group *vg,
+ const char *lv_name)
+{
+ struct lv_list *lvl = find_lv_in_vg(vg, lv_name);
+ return lvl ? lvl->lv : NULL;
+}
+
+struct physical_volume *find_pv(struct volume_group *vg, struct device *dev)
+{
+ struct pv_list *pvl;
+
+ dm_list_iterate_items(pvl, &vg->pvs)
+ if (dev == pvl->pv->dev)
+ return pvl->pv;
+
+ return NULL;
+}
+
+/* FIXME: liblvm todo - make into function that returns handle */
+struct physical_volume *find_pv_by_name(struct cmd_context *cmd,
+ const char *pv_name)
+{
+ return _find_pv_by_name(cmd, pv_name);
+}
+
+
+static struct physical_volume *_find_pv_by_name(struct cmd_context *cmd,
+ const char *pv_name)
+{
+ struct dm_list mdas;
+ struct physical_volume *pv;
+
+ dm_list_init(&mdas);
+ if (!(pv = _pv_read(cmd, cmd->mem, pv_name, &mdas, NULL, 1, 0))) {
+ log_error("Physical volume %s not found", pv_name);
+ return NULL;
+ }
+
+ if (is_orphan_vg(pv->vg_name) && mdas_empty_or_ignored(&mdas)) {
+ /* If a PV has no MDAs - need to search all VGs for it */
+ if (!scan_vgs_for_pvs(cmd, 1))
+ return_NULL;
+ if (!(pv = _pv_read(cmd, cmd->mem, pv_name, NULL, NULL, 1, 0))) {
+ log_error("Physical volume %s not found", pv_name);
+ return NULL;
+ }
+ }
+
+ if (is_orphan_vg(pv->vg_name)) {
+ log_error("Physical volume %s not in a volume group", pv_name);
+ return NULL;
+ }
+
+ return pv;
+}
+
+/* Find segment at a given logical extent in an LV */
+struct lv_segment *find_seg_by_le(const struct logical_volume *lv, uint32_t le)
+{
+ struct lv_segment *seg;
+
+ dm_list_iterate_items(seg, &lv->segments)
+ if (le >= seg->le && le < seg->le + seg->len)
+ return seg;
+
+ return NULL;
+}
+
+struct lv_segment *first_seg(const struct logical_volume *lv)
+{
+ struct lv_segment *seg;
+
+ dm_list_iterate_items(seg, &lv->segments)
+ return seg;
+
+ return NULL;
+}
+
+int vg_remove_mdas(struct volume_group *vg)
+{
+ struct metadata_area *mda;
+
+ /* FIXME Improve recovery situation? */
+ /* Remove each copy of the metadata */
+ dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+ if (mda->ops->vg_remove &&
+ !mda->ops->vg_remove(vg->fid, vg, mda))
+ return_0;
+ }
+
+ return 1;
+}
+
+/*
+ * Determine whether two vgs are compatible for merging.
+ */
+int vgs_are_compatible(struct cmd_context *cmd __attribute__((unused)),
+ struct volume_group *vg_from,
+ struct volume_group *vg_to)
+{
+ struct lv_list *lvl1, *lvl2;
+ struct pv_list *pvl;
+ char *name1, *name2;
+
+ if (lvs_in_vg_activated(vg_from)) {
+ log_error("Logical volumes in \"%s\" must be inactive",
+ vg_from->name);
+ return 0;
+ }
+
+ /* Check compatibility */
+ if (vg_to->extent_size != vg_from->extent_size) {
+ log_error("Extent sizes differ: %d (%s) and %d (%s)",
+ vg_to->extent_size, vg_to->name,
+ vg_from->extent_size, vg_from->name);
+ return 0;
+ }
+
+ if (vg_to->max_pv &&
+ (vg_to->max_pv < vg_to->pv_count + vg_from->pv_count)) {
+ log_error("Maximum number of physical volumes (%d) exceeded "
+ " for \"%s\" and \"%s\"", vg_to->max_pv, vg_to->name,
+ vg_from->name);
+ return 0;
+ }
+
+ if (vg_to->max_lv &&
+ (vg_to->max_lv < vg_visible_lvs(vg_to) + vg_visible_lvs(vg_from))) {
+ log_error("Maximum number of logical volumes (%d) exceeded "
+ " for \"%s\" and \"%s\"", vg_to->max_lv, vg_to->name,
+ vg_from->name);
+ return 0;
+ }
+
+ /* Metadata types must be the same */
+ if (vg_to->fid->fmt != vg_from->fid->fmt) {
+ log_error("Metadata types differ for \"%s\" and \"%s\"",
+ vg_to->name, vg_from->name);
+ return 0;
+ }
+
+ /* Clustering attribute must be the same */
+ if (vg_is_clustered(vg_to) != vg_is_clustered(vg_from)) {
+ log_error("Clustered attribute differs for \"%s\" and \"%s\"",
+ vg_to->name, vg_from->name);
+ return 0;
+ }
+
+ /* Check no conflicts with LV names */
+ dm_list_iterate_items(lvl1, &vg_to->lvs) {
+ name1 = lvl1->lv->name;
+
+ dm_list_iterate_items(lvl2, &vg_from->lvs) {
+ name2 = lvl2->lv->name;
+
+ if (!strcmp(name1, name2)) {
+ log_error("Duplicate logical volume "
+ "name \"%s\" "
+ "in \"%s\" and \"%s\"",
+ name1, vg_to->name, vg_from->name);
+ return 0;
+ }
+ }
+ }
+
+ /* Check no PVs are constructed from either VG */
+ dm_list_iterate_items(pvl, &vg_to->pvs) {
+ if (pv_uses_vg(pvl->pv, vg_from)) {
+ log_error("Physical volume %s might be constructed "
+ "from same volume group %s.",
+ pv_dev_name(pvl->pv), vg_from->name);
+ return 0;
+ }
+ }
+
+ dm_list_iterate_items(pvl, &vg_from->pvs) {
+ if (pv_uses_vg(pvl->pv, vg_to)) {
+ log_error("Physical volume %s might be constructed "
+ "from same volume group %s.",
+ pv_dev_name(pvl->pv), vg_to->name);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+struct _lv_postorder_baton {
+ int (*fn)(struct logical_volume *lv, void *data);
+ void *data;
+};
+
+static int _lv_postorder_visit(struct logical_volume *,
+ int (*fn)(struct logical_volume *lv, void *data),
+ void *data);
+
+static int _lv_postorder_level(struct logical_volume *lv, void *data)
+{
+ struct _lv_postorder_baton *baton = data;
+ if (lv->status & POSTORDER_OPEN_FLAG)
+ return 1; // a data structure loop has closed...
+ lv->status |= POSTORDER_OPEN_FLAG;
+ int r =_lv_postorder_visit(lv, baton->fn, baton->data);
+ lv->status &= ~POSTORDER_OPEN_FLAG;
+ lv->status |= POSTORDER_FLAG;
+ return r;
+};
+
+static int _lv_each_dependency(struct logical_volume *lv,
+ int (*fn)(struct logical_volume *lv, void *data),
+ void *data)
+{
+ int i, s;
+ struct lv_segment *lvseg;
+
+ struct logical_volume *deps[] = {
+ (lv->rdevice && lv != lv->rdevice->lv) ? lv->rdevice->lv : 0,
+ (lv->rdevice && lv != lv->rdevice->slog) ? lv->rdevice->slog : 0,
+ lv->snapshot ? lv->snapshot->origin : 0,
+ lv->snapshot ? lv->snapshot->cow : 0 };
+ for (i = 0; i < sizeof(deps) / sizeof(*deps); ++i) {
+ if (deps[i] && !fn(deps[i], data))
+ return_0;
+ }
+
+ dm_list_iterate_items(lvseg, &lv->segments) {
+ if (lvseg->log_lv && !fn(lvseg->log_lv, data))
+ return_0;
+ if (lvseg->rlog_lv && !fn(lvseg->rlog_lv, data))
+ return_0;
+ for (s = 0; s < lvseg->area_count; ++s) {
+ if (seg_type(lvseg, s) == AREA_LV && !fn(seg_lv(lvseg,s), data))
+ return_0;
+ }
+ }
+ return 1;
+}
+
+static int _lv_postorder_cleanup(struct logical_volume *lv, void *data)
+{
+ if (!(lv->status & POSTORDER_FLAG))
+ return 1;
+ lv->status &= ~POSTORDER_FLAG;
+
+ if (!_lv_each_dependency(lv, _lv_postorder_cleanup, data))
+ return_0;
+ return 1;
+}
+
+static int _lv_postorder_visit(struct logical_volume *lv,
+ int (*fn)(struct logical_volume *lv, void *data),
+ void *data)
+{
+ struct _lv_postorder_baton baton;
+ int r;
+
+ if (lv->status & POSTORDER_FLAG)
+ return 1;
+
+ baton.fn = fn;
+ baton.data = data;
+ r = _lv_each_dependency(lv, _lv_postorder_level, &baton);
+ if (r)
+ r = fn(lv, data);
+
+ return r;
+}
+
+/*
+ * This will walk the LV dependency graph in depth-first order and in the
+ * postorder, call a callback function "fn". The void *data is passed along all
+ * the calls. The callback may return zero to indicate an error and terminate
+ * the depth-first walk. The error is propagated to return value of
+ * _lv_postorder.
+ */
+static int _lv_postorder(struct logical_volume *lv,
+ int (*fn)(struct logical_volume *lv, void *data),
+ void *data)
+{
+ int r;
+ r = _lv_postorder_visit(lv, fn, data);
+ _lv_postorder_cleanup(lv, 0);
+ return r;
+}
+
+struct _lv_mark_if_partial_baton {
+ int partial;
+};
+
+static int _lv_mark_if_partial_collect(struct logical_volume *lv, void *data)
+{
+ struct _lv_mark_if_partial_baton *baton = data;
+ if (lv->status & PARTIAL_LV)
+ baton->partial = 1;
+
+ return 1;
+}
+
+static int _lv_mark_if_partial_single(struct logical_volume *lv, void *data)
+{
+ int s;
+ struct _lv_mark_if_partial_baton baton;
+ struct lv_segment *lvseg;
+
+ dm_list_iterate_items(lvseg, &lv->segments) {
+ for (s = 0; s < lvseg->area_count; ++s) {
+ if (seg_type(lvseg, s) == AREA_PV) {
+ if (is_missing_pv(seg_pv(lvseg, s)))
+ lv->status |= PARTIAL_LV;
+ }
+ }
+ }
+
+ baton.partial = 0;
+ _lv_each_dependency(lv, _lv_mark_if_partial_collect, &baton);
+
+ if (baton.partial)
+ lv->status |= PARTIAL_LV;
+
+ return 1;
+}
+
+static int _lv_mark_if_partial(struct logical_volume *lv)
+{
+ return _lv_postorder(lv, _lv_mark_if_partial_single, NULL);
+}
+
+/*
+ * Mark LVs with missing PVs using PARTIAL_LV status flag. The flag is
+ * propagated transitively, so LVs referencing other LVs are marked
+ * partial as well, if any of their referenced LVs are marked partial.
+ */
+int vg_mark_partial_lvs(struct volume_group *vg)
+{
+ struct logical_volume *lv;
+ struct lv_list *lvl;
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ lv = lvl->lv;
+ if (!_lv_mark_if_partial(lv))
+ return_0;
+ }
+ return 1;
+}
+
+/*
+ * Be sure that all PV devices have cached read ahead in dev-cache
+ * Currently it takes read_ahead from first PV segment only
+ */
+static int _lv_read_ahead_single(struct logical_volume *lv, void *data)
+{
+ struct lv_segment *seg = first_seg(lv);
+ uint32_t seg_read_ahead = 0, *read_ahead = data;
+
+ if (seg && seg->area_count && seg_type(seg, 0) == AREA_PV)
+ dev_get_read_ahead(seg_pv(seg, 0)->dev, &seg_read_ahead);
+
+ if (seg_read_ahead > *read_ahead)
+ *read_ahead = seg_read_ahead;
+
+ return 1;
+}
+
+/*
+ * Calculate readahead for logical volume from underlying PV devices.
+ * If read_ahead is NULL, only ensure that readahead of PVs are preloaded
+ * into PV struct device in dev cache.
+ */
+void lv_calculate_readahead(const struct logical_volume *lv, uint32_t *read_ahead)
+{
+ uint32_t _read_ahead = 0;
+
+ if (lv->read_ahead == DM_READ_AHEAD_AUTO)
+ _lv_postorder((struct logical_volume *)lv, _lv_read_ahead_single, &_read_ahead);
+
+ if (read_ahead) {
+ log_debug("Calculated readahead of LV %s is %u", lv->name, _read_ahead);
+ *read_ahead = _read_ahead;
+ }
+}
+
+/*
+ * Check that an LV and all its PV references are correctly listed in vg->lvs
+ * and vg->pvs, respectively. This only looks at a single LV, but *not* at the
+ * LVs it is using. To do the latter, you should use _lv_postorder with this
+ * function. C.f. vg_validate.
+ */
+static int _lv_validate_references_single(struct logical_volume *lv, void *data)
+{
+ struct volume_group *vg = lv->vg;
+ struct lv_segment *lvseg;
+ struct pv_list *pvl;
+ struct lv_list *lvl;
+ int s;
+ int r = 1;
+ int ok = 0;
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (lvl->lv == lv) {
+ ok = 1;
+ break;
+ }
+ }
+
+ if (!ok) {
+ log_error(INTERNAL_ERROR
+ "Referenced LV %s not listed in VG %s.",
+ lv->name, vg->name);
+ r = 0;
+ }
+
+ dm_list_iterate_items(lvseg, &lv->segments) {
+ for (s = 0; s < lvseg->area_count; ++s) {
+ if (seg_type(lvseg, s) == AREA_PV) {
+ ok = 0;
+ /* look up the reference in vg->pvs */
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (pvl->pv == seg_pv(lvseg, s)) {
+ ok = 1;
+ break;
+ }
+ }
+
+ if (!ok) {
+ log_error(INTERNAL_ERROR
+ "Referenced PV %s not listed in VG %s.",
+ pv_dev_name(seg_pv(lvseg, s)), vg->name);
+ r = 0;
+ }
+ }
+ }
+ }
+
+ return r;
+}
+
+int vg_validate(struct volume_group *vg)
+{
+ struct pv_list *pvl, *pvl2;
+ struct lv_list *lvl, *lvl2;
+ struct lv_segment *seg;
+ char uuid[64] __attribute__((aligned(8)));
+ int r = 1;
+ uint32_t hidden_lv_count = 0, lv_count = 0, lv_visible_count = 0;
+ uint32_t pv_count = 0;
+ uint32_t num_snapshots = 0;
+ uint32_t loop_counter1, loop_counter2;
+
+ if (vg->alloc == ALLOC_CLING_BY_TAGS) {
+ log_error(INTERNAL_ERROR "VG %s allocation policy set to invalid cling_by_tags.",
+ vg->name);
+ r = 0;
+ }
+
+ /* FIXME Also check there's no data/metadata overlap */
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (++pv_count > vg->pv_count) {
+ log_error(INTERNAL_ERROR "PV list corruption detected in VG %s.", vg->name);
+ /* FIXME Dump list structure? */
+ r = 0;
+ }
+ if (pvl->pv->vg != vg) {
+ log_error(INTERNAL_ERROR "VG %s PV list entry points "
+ "to different VG %s", vg->name,
+ pvl->pv->vg ? pvl->pv->vg->name : "NULL");
+ r = 0;
+ }
+ }
+
+ loop_counter1 = loop_counter2 = 0;
+ /* FIXME Use temp hash table instead? */
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (++loop_counter1 > pv_count)
+ break;
+ dm_list_iterate_items(pvl2, &vg->pvs) {
+ if (++loop_counter2 > pv_count)
+ break;
+ if (pvl == pvl2)
+ break;
+ if (id_equal(&pvl->pv->id,
+ &pvl2->pv->id)) {
+ if (!id_write_format(&pvl->pv->id, uuid,
+ sizeof(uuid)))
+ stack;
+ log_error(INTERNAL_ERROR "Duplicate PV id "
+ "%s detected for %s in %s.",
+ uuid, pv_dev_name(pvl->pv),
+ vg->name);
+ r = 0;
+ }
+ }
+
+ if (strcmp(pvl->pv->vg_name, vg->name)) {
+ log_error(INTERNAL_ERROR "VG name for PV %s is corrupted.",
+ pv_dev_name(pvl->pv));
+ r = 0;
+ }
+ }
+
+ if (!check_pv_segments(vg)) {
+ log_error(INTERNAL_ERROR "PV segments corrupted in %s.",
+ vg->name);
+ r = 0;
+ }
+
+ /*
+ * Count all non-snapshot invisible LVs
+ */
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ lv_count++;
+
+ if (lv_is_cow(lvl->lv))
+ num_snapshots++;
+
+ if (lv_is_visible(lvl->lv))
+ lv_visible_count++;
+
+ if (!check_lv_segments(lvl->lv, 0)) {
+ log_error(INTERNAL_ERROR "LV segments corrupted in %s.",
+ lvl->lv->name);
+ r = 0;
+ }
+
+ if (lvl->lv->alloc == ALLOC_CLING_BY_TAGS) {
+ log_error(INTERNAL_ERROR "LV %s allocation policy set to invalid cling_by_tags.",
+ lvl->lv->name);
+ r = 0;
+ }
+
+ if (lvl->lv->status & VISIBLE_LV)
+ continue;
+
+ /* snapshots */
+ if (lv_is_cow(lvl->lv))
+ continue;
+
+ /* virtual origins are always hidden */
+ if (lv_is_origin(lvl->lv) && !lv_is_virtual_origin(lvl->lv))
+ continue;
+
+ /* count other non-snapshot invisible volumes */
+ hidden_lv_count++;
+
+ /*
+ * FIXME: add check for unreferenced invisible LVs
+ * - snapshot cow & origin
+ * - mirror log & images
+ * - mirror conversion volumes (_mimagetmp*)
+ */
+ }
+
+ /*
+ * all volumes = visible LVs + snapshot_cows + invisible LVs
+ */
+ if (lv_count != lv_visible_count + num_snapshots + hidden_lv_count) {
+ log_error(INTERNAL_ERROR "#internal LVs (%u) != #LVs (%"
+ PRIu32 ") + #snapshots (%" PRIu32 ") + #internal LVs (%u) in VG %s",
+ lv_count, lv_visible_count,
+ num_snapshots, hidden_lv_count, vg->name);
+ r = 0;
+ }
+
+ /* Avoid endless loop if lv->segments list is corrupt */
+ if (!r)
+ return r;
+
+ loop_counter1 = loop_counter2 = 0;
+ /* FIXME Use temp hash table instead? */
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (++loop_counter1 > lv_count)
+ break;
+ dm_list_iterate_items(lvl2, &vg->lvs) {
+ if (++loop_counter2 > lv_count)
+ break;
+ if (lvl == lvl2)
+ break;
+ if (!strcmp(lvl->lv->name, lvl2->lv->name)) {
+ log_error(INTERNAL_ERROR "Duplicate LV name "
+ "%s detected in %s.", lvl->lv->name,
+ vg->name);
+ r = 0;
+ }
+ if (id_equal(&lvl->lv->lvid.id[1],
+ &lvl2->lv->lvid.id[1])) {
+ if (!id_write_format(&lvl->lv->lvid.id[1], uuid,
+ sizeof(uuid)))
+ stack;
+ log_error(INTERNAL_ERROR "Duplicate LV id "
+ "%s detected for %s and %s in %s.",
+ uuid, lvl->lv->name, lvl2->lv->name,
+ vg->name);
+ r = 0;
+ }
+ }
+
+ if (!check_lv_segments(lvl->lv, 1)) {
+ log_error(INTERNAL_ERROR "LV segments corrupted in %s.",
+ lvl->lv->name);
+ r = 0;
+ }
+ }
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (!_lv_postorder(lvl->lv, _lv_validate_references_single, NULL))
+ r = 0;
+ }
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (!(lvl->lv->status & PVMOVE))
+ continue;
+ dm_list_iterate_items(seg, &lvl->lv->segments) {
+ if (seg_is_mirrored(seg)) {
+ if (seg->area_count != 2) {
+ log_error(INTERNAL_ERROR
+ "Segment %d in %s is not 2-way.",
+ loop_counter1, lvl->lv->name);
+ r = 0;
+ }
+ } else if (seg->area_count != 1) {
+ log_error(INTERNAL_ERROR
+ "Segment %d in %s has wrong number of areas: %d.",
+ loop_counter1, lvl->lv->name, seg->area_count);
+ r = 0;
+ }
+ }
+ }
+
+ if (!(vg->fid->fmt->features & FMT_UNLIMITED_VOLS) &&
+ (!vg->max_lv || !vg->max_pv)) {
+ log_error(INTERNAL_ERROR "Volume group %s has limited PV/LV count"
+ " but limit is not set.", vg->name);
+ r = 0;
+ }
+
+ if (vg_max_lv_reached(vg))
+ stack;
+
+ return r;
+}
+
+/*
+ * After vg_write() returns success,
+ * caller MUST call either vg_commit() or vg_revert()
+ */
+int vg_write(struct volume_group *vg)
+{
+ struct dm_list *mdah;
+ struct metadata_area *mda;
+
+ if (!vg_validate(vg))
+ return_0;
+
+ if (vg->status & PARTIAL_VG) {
+ log_error("Cannot update partial volume group %s.", vg->name);
+ return 0;
+ }
+
+ if (vg_missing_pv_count(vg) && !vg->cmd->handles_missing_pvs) {
+ log_error("Cannot update volume group %s while physical "
+ "volumes are missing.", vg->name);
+ return 0;
+ }
+
+ if (vg_has_unknown_segments(vg) && !vg->cmd->handles_unknown_segments) {
+ log_error("Cannot update volume group %s with unknown segments in it!",
+ vg->name);
+ return 0;
+ }
+
+ if ((vg->fid->fmt->features & FMT_MDAS) && !_vg_adjust_ignored_mdas(vg))
+ return_0;
+
+ if (!vg_mda_used_count(vg)) {
+ log_error("Aborting vg_write: No metadata areas to write to!");
+ return 0;
+ }
+
+ if (!drop_cached_metadata(vg)) {
+ log_error("Unable to drop cached metadata for VG %s.", vg->name);
+ return 0;
+ }
+
+ vg->seqno++;
+
+ /* Write to each copy of the metadata area */
+ dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+ if (!mda->ops->vg_write) {
+ log_error("Format does not support writing volume"
+ "group metadata areas");
+ /* Revert */
+ dm_list_uniterate(mdah, &vg->fid->metadata_areas_in_use, &mda->list) {
+ mda = dm_list_item(mdah, struct metadata_area);
+
+ if (mda->ops->vg_revert &&
+ !mda->ops->vg_revert(vg->fid, vg, mda)) {
+ stack;
+ }
+ }
+ return 0;
+ }
+ if (!mda->ops->vg_write(vg->fid, vg, mda)) {
+ stack;
+ /* Revert */
+ dm_list_uniterate(mdah, &vg->fid->metadata_areas_in_use, &mda->list) {
+ mda = dm_list_item(mdah, struct metadata_area);
+
+ if (mda->ops->vg_revert &&
+ !mda->ops->vg_revert(vg->fid, vg, mda)) {
+ stack;
+ }
+ }
+ return 0;
+ }
+ }
+
+ /* Now pre-commit each copy of the new metadata */
+ dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+ if (mda->ops->vg_precommit &&
+ !mda->ops->vg_precommit(vg->fid, vg, mda)) {
+ stack;
+ /* Revert */
+ dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+ if (mda->ops->vg_revert &&
+ !mda->ops->vg_revert(vg->fid, vg, mda)) {
+ stack;
+ }
+ }
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int _vg_commit_mdas(struct volume_group *vg)
+{
+ struct metadata_area *mda, *tmda;
+ struct dm_list ignored;
+ int failed = 0;
+ int cache_updated = 0;
+
+ /* Rearrange the metadata_areas_in_use so ignored mdas come first. */
+ dm_list_init(&ignored);
+ dm_list_iterate_items_safe(mda, tmda, &vg->fid->metadata_areas_in_use)
+ if (mda_is_ignored(mda))
+ dm_list_move(&ignored, &mda->list);
+
+ dm_list_iterate_items_safe(mda, tmda, &ignored)
+ dm_list_move(&vg->fid->metadata_areas_in_use, &mda->list);
+
+ /* Commit to each copy of the metadata area */
+ dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+ failed = 0;
+ if (mda->ops->vg_commit &&
+ !mda->ops->vg_commit(vg->fid, vg, mda)) {
+ stack;
+ failed = 1;
+ }
+ /* Update cache first time we succeed */
+ if (!failed && !cache_updated) {
+ lvmcache_update_vg(vg, 0);
+ cache_updated = 1;
+ }
+ }
+ return cache_updated;
+}
+
+/* Commit pending changes */
+int vg_commit(struct volume_group *vg)
+{
+ int cache_updated = 0;
+
+ if (!vgname_is_locked(vg->name)) {
+ log_error(INTERNAL_ERROR "Attempt to write new VG metadata "
+ "without locking %s", vg->name);
+ return cache_updated;
+ }
+
+ cache_updated = _vg_commit_mdas(vg);
+
+ if (cache_updated) {
+ /* Instruct remote nodes to upgrade cached metadata. */
+ remote_commit_cached_metadata(vg);
+ /*
+ * We need to clear old_name after a successful commit.
+ * The volume_group structure could be reused later.
+ */
+ vg->old_name = NULL;
+ }
+
+ /* If update failed, remove any cached precommitted metadata. */
+ if (!cache_updated && !drop_cached_metadata(vg))
+ log_error("Attempt to drop cached metadata failed "
+ "after commit for VG %s.", vg->name);
+
+ /* If at least one mda commit succeeded, it was committed */
+ return cache_updated;
+}
+
+/* Don't commit any pending changes */
+int vg_revert(struct volume_group *vg)
+{
+ struct metadata_area *mda;
+
+ dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+ if (mda->ops->vg_revert &&
+ !mda->ops->vg_revert(vg->fid, vg, mda)) {
+ stack;
+ }
+ }
+
+ if (!drop_cached_metadata(vg))
+ log_error("Attempt to drop cached metadata failed "
+ "after reverted update for VG %s.", vg->name);
+
+ remote_revert_cached_metadata(vg);
+
+ return 1;
+}
+
+/* Make orphan PVs look like a VG */
+static struct volume_group *_vg_read_orphans(struct cmd_context *cmd,
+ int warnings,
+ const char *orphan_vgname)
+{
+ struct lvmcache_vginfo *vginfo;
+ struct lvmcache_info *info;
+ struct pv_list *pvl;
+ struct volume_group *vg;
+ struct physical_volume *pv;
+ struct dm_pool *mem;
+
+ lvmcache_label_scan(cmd, 0);
+
+ if (!(vginfo = vginfo_from_vgname(orphan_vgname, NULL)))
+ return_NULL;
+
+ if (!(mem = dm_pool_create("vg_read orphan", VG_MEMPOOL_CHUNK)))
+ return_NULL;
+
+ if (!(vg = dm_pool_zalloc(mem, sizeof(*vg)))) {
+ log_error("vg allocation failed");
+ goto bad;
+ }
+ dm_list_init(&vg->pvs);
+ dm_list_init(&vg->lvs);
+ dm_list_init(&vg->tags);
+ dm_list_init(&vg->removed_pvs);
+ vg->vgmem = mem;
+ vg->cmd = cmd;
+ if (!(vg->name = dm_pool_strdup(mem, orphan_vgname))) {
+ log_error("vg name allocation failed");
+ goto bad;
+ }
+
+ /* create format instance with appropriate metadata area */
+ if (!(vg->fid = vginfo->fmt->ops->create_instance(vginfo->fmt,
+ orphan_vgname, NULL,
+ NULL))) {
+ log_error("Failed to create format instance");
+ goto bad;
+ }
+
+ dm_list_iterate_items(info, &vginfo->infos) {
+ if (!(pv = _pv_read(cmd, mem, dev_name(info->dev), NULL, NULL, warnings, 0))) {
+ continue;
+ }
+ if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl)))) {
+ log_error("pv_list allocation failed");
+ goto bad;
+ }
+ pvl->pv = pv;
+ add_pvl_to_vgs(vg, pvl);
+ }
+
+ return vg;
+bad:
+ dm_pool_destroy(mem);
+ return NULL;
+}
+
+static int _update_pv_list(struct dm_pool *pvmem, struct dm_list *all_pvs, struct volume_group *vg)
+{
+ struct pv_list *pvl, *pvl2;
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ dm_list_iterate_items(pvl2, all_pvs) {
+ if (pvl->pv->dev == pvl2->pv->dev)
+ goto next_pv;
+ }
+
+ /*
+ * PV is not on list so add it.
+ */
+ if (!(pvl2 = _copy_pvl(pvmem, pvl))) {
+ log_error("pv_list allocation for '%s' failed",
+ pv_dev_name(pvl->pv));
+ return 0;
+ }
+ dm_list_add(all_pvs, &pvl2->list);
+ next_pv:
+ ;
+ }
+
+ return 1;
+}
+
+int vg_missing_pv_count(const struct volume_group *vg)
+{
+ int ret = 0;
+ struct pv_list *pvl;
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (is_missing_pv(pvl->pv))
+ ++ ret;
+ }
+ return ret;
+}
+
+static void check_reappeared_pv(struct volume_group *correct_vg,
+ struct physical_volume *pv)
+{
+ struct pv_list *pvl;
+
+ /*
+ * Skip these checks in case the tool is going to deal with missing
+ * PVs, especially since the resulting messages can be pretty
+ * confusing.
+ */
+ if (correct_vg->cmd->handles_missing_pvs)
+ return;
+
+ dm_list_iterate_items(pvl, &correct_vg->pvs)
+ if (pv->dev == pvl->pv->dev && is_missing_pv(pvl->pv)) {
+ log_warn("Missing device %s reappeared, updating "
+ "metadata for VG %s to version %u.",
+ pv_dev_name(pvl->pv), pv_vg_name(pvl->pv),
+ correct_vg->seqno);
+ if (pvl->pv->pe_alloc_count == 0) {
+ pv->status &= ~MISSING_PV;
+ pvl->pv->status &= ~MISSING_PV;
+ } else
+ log_warn("Device still marked missing because of allocated data "
+ "on it, remove volumes and consider vgreduce --removemissing.");
+ }
+}
+/* Caller sets consistent to 1 if it's safe for vg_read_internal to correct
+ * inconsistent metadata on disk (i.e. the VG write lock is held).
+ * This guarantees only consistent metadata is returned.
+ * If consistent is 0, caller must check whether consistent == 1 on return
+ * and take appropriate action if it isn't (e.g. abort; get write lock
+ * and call vg_read_internal again).
+ *
+ * If precommitted is set, use precommitted metadata if present.
+ *
+ * Either of vgname or vgid may be NULL.
+ */
+static struct volume_group *_vg_read(struct cmd_context *cmd,
+ const char *vgname,
+ const char *vgid,
+ int warnings,
+ int *consistent, unsigned precommitted)
+{
+ struct format_instance *fid;
+ const struct format_type *fmt;
+ struct volume_group *vg, *correct_vg = NULL;
+ struct metadata_area *mda;
+ struct lvmcache_info *info;
+ int inconsistent = 0;
+ int inconsistent_vgid = 0;
+ int inconsistent_pvs = 0;
+ int inconsistent_seqno = 0;
+ int inconsistent_mdas = 0;
+ unsigned use_precommitted = precommitted;
+ unsigned saved_handles_missing_pvs = cmd->handles_missing_pvs;
+ struct dm_list *pvids;
+ struct pv_list *pvl, *pvl2;
+ struct dm_list all_pvs;
+ char uuid[64] __attribute__((aligned(8)));
+
+ if (is_orphan_vg(vgname)) {
+ if (use_precommitted) {
+ log_error(INTERNAL_ERROR "vg_read_internal requires vgname "
+ "with pre-commit.");
+ return NULL;
+ }
+ *consistent = 1;
+ return _vg_read_orphans(cmd, warnings, vgname);
+ }
+
+ /*
+ * If cached metadata was inconsistent and *consistent is set
+ * then repair it now. Otherwise just return it.
+ * Also return if use_precommitted is set due to the FIXME in
+ * the missing PV logic below.
+ */
+ if ((correct_vg = lvmcache_get_vg(vgid, precommitted)) &&
+ (use_precommitted || !*consistent || !(correct_vg->status & INCONSISTENT_VG))) {
+ if (!(correct_vg->status & INCONSISTENT_VG))
+ *consistent = 1;
+ else /* Inconsistent but we can't repair it */
+ correct_vg->status &= ~INCONSISTENT_VG;
+
+ if (vg_missing_pv_count(correct_vg)) {
+ log_verbose("There are %d physical volumes missing.",
+ vg_missing_pv_count(correct_vg));
+ vg_mark_partial_lvs(correct_vg);
+ }
+ return correct_vg;
+ } else {
+ free_vg(correct_vg);
+ correct_vg = NULL;
+ }
+
+ /* Find the vgname in the cache */
+ /* If it's not there we must do full scan to be completely sure */
+ if (!(fmt = fmt_from_vgname(vgname, vgid, 1))) {
+ lvmcache_label_scan(cmd, 0);
+ if (!(fmt = fmt_from_vgname(vgname, vgid, 1))) {
+ /* Independent MDAs aren't supported under low memory */
+ if (!cmd->independent_metadata_areas && memlock())
+ return_NULL;
+ lvmcache_label_scan(cmd, 2);
+ if (!(fmt = fmt_from_vgname(vgname, vgid, 0)))
+ return_NULL;
+ }
+ }
+
+ /* Now determine the correct vgname if none was supplied */
+ if (!vgname && !(vgname = vgname_from_vgid(cmd->mem, vgid)))
+ return_NULL;
+
+ if (use_precommitted && !(fmt->features & FMT_PRECOMMIT))
+ use_precommitted = 0;
+
+ /* create format instance with appropriate metadata area */
+ if (!(fid = fmt->ops->create_instance(fmt, vgname, vgid, NULL))) {
+ log_error("Failed to create format instance");
+ return NULL;
+ }
+
+ /* Store pvids for later so we can check if any are missing */
+ if (!(pvids = lvmcache_get_pvids(cmd, vgname, vgid)))
+ return_NULL;
+
+ /* Ensure contents of all metadata areas match - else do recovery */
+ dm_list_iterate_items(mda, &fid->metadata_areas_in_use) {
+ if ((use_precommitted &&
+ !(vg = mda->ops->vg_read_precommit(fid, vgname, mda))) ||
+ (!use_precommitted &&
+ !(vg = mda->ops->vg_read(fid, vgname, mda)))) {
+ inconsistent = 1;
+ free_vg(vg);
+ continue;
+ }
+ if (!correct_vg) {
+ correct_vg = vg;
+ continue;
+ }
+
+ /* FIXME Also ensure contents same - checksum compare? */
+ if (correct_vg->seqno != vg->seqno) {
+ if (cmd->metadata_read_only)
+ log_very_verbose("Not repairing VG %s metadata seqno (%d != %d) "
+ "as global/metadata_read_only is set.",
+ vgname, vg->seqno, correct_vg->seqno);
+ else {
+ inconsistent = 1;
+ inconsistent_seqno = 1;
+ }
+ if (vg->seqno > correct_vg->seqno) {
+ free_vg(correct_vg);
+ correct_vg = vg;
+ }
+ }
+
+ if (vg != correct_vg)
+ free_vg(vg);
+ }
+
+ /* Ensure every PV in the VG was in the cache */
+ if (correct_vg) {
+ /*
+ * If the VG has PVs without mdas, or ignored mdas, they may
+ * still be orphans in the cache: update the cache state here,
+ * and update the metadata lists in the vg.
+ */
+ if (!inconsistent &&
+ dm_list_size(&correct_vg->pvs) > dm_list_size(pvids)) {
+ dm_list_iterate_items(pvl, &correct_vg->pvs) {
+ if (!pvl->pv->dev) {
+ inconsistent_pvs = 1;
+ break;
+ }
+
+ if (str_list_match_item(pvids, pvl->pv->dev->pvid))
+ continue;
+
+ /*
+ * PV not marked as belonging to this VG in cache.
+ * Check it's an orphan without metadata area
+ * not ignored.
+ */
+ if (!(info = info_from_pvid(pvl->pv->dev->pvid, 1)) ||
+ !info->vginfo || !is_orphan_vg(info->vginfo->vgname)) {
+ inconsistent_pvs = 1;
+ break;
+ }
+ if (dm_list_size(&info->mdas)) {
+ if (!fid_add_mdas(fid, &info->mdas))
+ return_NULL;
+
+ log_debug("Empty mda found for VG %s.", vgname);
+
+ if (inconsistent_mdas)
+ continue;
+
+ /*
+ * If any newly-added mdas are in-use then their
+ * metadata needs updating.
+ */
+ dm_list_iterate_items(mda, &info->mdas)
+ if (!mda_is_ignored(mda)) {
+ inconsistent_mdas = 1;
+ break;
+ }
+ }
+ }
+
+ /* If the check passed, let's update VG and recalculate pvids */
+ if (!inconsistent_pvs) {
+ log_debug("Updating cache for PVs without mdas "
+ "in VG %s.", vgname);
+ /*
+ * If there is no precommitted metadata, committed metadata
+ * is read and stored in the cache even if use_precommitted is set
+ */
+ lvmcache_update_vg(correct_vg, correct_vg->status & PRECOMMITTED);
+
+ if (!(pvids = lvmcache_get_pvids(cmd, vgname, vgid)))
+ return_NULL;
+ }
+ }
+
+ if (dm_list_size(&correct_vg->pvs) !=
+ dm_list_size(pvids) + vg_missing_pv_count(correct_vg)) {
+ log_debug("Cached VG %s had incorrect PV list",
+ vgname);
+
+ if (memlock())
+ inconsistent = 1;
+ else {
+ free_vg(correct_vg);
+ correct_vg = NULL;
+ }
+ } else dm_list_iterate_items(pvl, &correct_vg->pvs) {
+ if (is_missing_pv(pvl->pv))
+ continue;
+ if (!str_list_match_item(pvids, pvl->pv->dev->pvid)) {
+ log_debug("Cached VG %s had incorrect PV list",
+ vgname);
+ free_vg(correct_vg);
+ correct_vg = NULL;
+ break;
+ }
+ }
+
+ if (correct_vg && inconsistent_mdas) {
+ free_vg(correct_vg);
+ correct_vg = NULL;
+ }
+ }
+
+ dm_list_init(&all_pvs);
+
+ /* Failed to find VG where we expected it - full scan and retry */
+ if (!correct_vg) {
+ inconsistent = 0;
+
+ /* Independent MDAs aren't supported under low memory */
+ if (!cmd->independent_metadata_areas && memlock())
+ return_NULL;
+ lvmcache_label_scan(cmd, 2);
+ if (!(fmt = fmt_from_vgname(vgname, vgid, 0)))
+ return_NULL;
+
+ if (precommitted && !(fmt->features & FMT_PRECOMMIT))
+ use_precommitted = 0;
+
+ /* create format instance with appropriate metadata area */
+ if (!(fid = fmt->ops->create_instance(fmt, vgname, vgid, NULL))) {
+ log_error("Failed to create format instance");
+ return NULL;
+ }
+
+ /* Ensure contents of all metadata areas match - else recover */
+ dm_list_iterate_items(mda, &fid->metadata_areas_in_use) {
+ if ((use_precommitted &&
+ !(vg = mda->ops->vg_read_precommit(fid, vgname,
+ mda))) ||
+ (!use_precommitted &&
+ !(vg = mda->ops->vg_read(fid, vgname, mda)))) {
+ inconsistent = 1;
+ continue;
+ }
+ if (!correct_vg) {
+ correct_vg = vg;
+ if (!_update_pv_list(cmd->mem, &all_pvs, correct_vg)) {
+ free_vg(vg);
+ return_NULL;
+ }
+ continue;
+ }
+
+ if (strncmp((char *)vg->id.uuid,
+ (char *)correct_vg->id.uuid, ID_LEN)) {
+ inconsistent = 1;
+ inconsistent_vgid = 1;
+ }
+
+ /* FIXME Also ensure contents same - checksums same? */
+ if (correct_vg->seqno != vg->seqno) {
+ /* Ignore inconsistent seqno if told to skip repair logic */
+ if (cmd->metadata_read_only)
+ log_very_verbose("Not repairing VG %s metadata seqno (%d != %d) "
+ "as global/metadata_read_only is set.",
+ vgname, vg->seqno, correct_vg->seqno);
+ else {
+ inconsistent = 1;
+ inconsistent_seqno = 1;
+ }
+ if (!_update_pv_list(cmd->mem, &all_pvs, vg)) {
+ free_vg(vg);
+ free_vg(correct_vg);
+ return_NULL;
+ }
+ if (vg->seqno > correct_vg->seqno) {
+ free_vg(correct_vg);
+ correct_vg = vg;
+ }
+ }
+
+ if (vg != correct_vg)
+ free_vg(vg);
+ }
+
+ /* Give up looking */
+ if (!correct_vg)
+ return_NULL;
+ }
+
+ /*
+ * If there is no precommitted metadata, committed metadata
+ * is read and stored in the cache even if use_precommitted is set
+ */
+ lvmcache_update_vg(correct_vg, correct_vg->status & PRECOMMITTED &
+ (inconsistent ? INCONSISTENT_VG : 0));
+
+ if (inconsistent) {
+ /* FIXME Test should be if we're *using* precommitted metadata not if we were searching for it */
+ if (use_precommitted) {
+ log_error("Inconsistent pre-commit metadata copies "
+ "for volume group %s", vgname);
+ /* FIXME: during repair, there is inconsistent flag set because some metadata areas
+ * are missing (on missing PVs). Code should create list of missing PVs, compare it
+ * with PV marked missing in metadata and if equals, use it as consistent vg.
+ * For now, return precommited metadata if remainng seq match here to allow
+ * preloading table in suspend call.
+ */
+ if (!inconsistent_seqno) {
+ *consistent = 0;
+ return correct_vg;
+ }
+ free_vg(correct_vg);
+ return NULL;
+ }
+
+ if (!*consistent)
+ return correct_vg;
+
+ /* Don't touch if vgids didn't match */
+ if (inconsistent_vgid) {
+ log_error("Inconsistent metadata UUIDs found for "
+ "volume group %s", vgname);
+ *consistent = 0;
+ return correct_vg;
+ }
+
+ log_warn("WARNING: Inconsistent metadata found for VG %s - updating "
+ "to use version %u", vgname, correct_vg->seqno);
+
+ /*
+ * If PV is marked missing but we found it,
+ * update metadata and remove MISSING flag
+ */
+ dm_list_iterate_items(pvl, &all_pvs)
+ check_reappeared_pv(correct_vg, pvl->pv);
+
+ cmd->handles_missing_pvs = 1;
+ if (!vg_write(correct_vg)) {
+ log_error("Automatic metadata correction failed");
+ free_vg(correct_vg);
+ cmd->handles_missing_pvs = saved_handles_missing_pvs;
+ return NULL;
+ }
+ cmd->handles_missing_pvs = saved_handles_missing_pvs;
+
+ if (!vg_commit(correct_vg)) {
+ log_error("Automatic metadata correction commit "
+ "failed");
+ free_vg(correct_vg);
+ return NULL;
+ }
+
+ dm_list_iterate_items(pvl, &all_pvs) {
+ dm_list_iterate_items(pvl2, &correct_vg->pvs) {
+ if (pvl->pv->dev == pvl2->pv->dev)
+ goto next_pv;
+ }
+ if (!id_write_format(&pvl->pv->id, uuid, sizeof(uuid))) {
+ free_vg(correct_vg);
+ return_NULL;
+ }
+ log_error("Removing PV %s (%s) that no longer belongs to VG %s",
+ pv_dev_name(pvl->pv), uuid, correct_vg->name);
+ if (!pv_write_orphan(cmd, pvl->pv)) {
+ free_vg(correct_vg);
+ return_NULL;
+ }
+
+ /* Refresh metadata after orphan write */
+ drop_cached_metadata(correct_vg);
+ next_pv:
+ ;
+ }
+ }
+
+ if (vg_missing_pv_count(correct_vg)) {
+ log_verbose("There are %d physical volumes missing.",
+ vg_missing_pv_count(correct_vg));
+ vg_mark_partial_lvs(correct_vg);
+ }
+
+ if ((correct_vg->status & PVMOVE) && !pvmove_mode()) {
+ log_error("WARNING: Interrupted pvmove detected in "
+ "volume group %s", correct_vg->name);
+ log_error("Please restore the metadata by running "
+ "vgcfgrestore.");
+ free_vg(correct_vg);
+ return NULL;
+ }
+
+ *consistent = 1;
+ return correct_vg;
+}
+
+struct volume_group *vg_read_internal(struct cmd_context *cmd, const char *vgname,
+ const char *vgid, int warnings, int *consistent)
+{
+ struct volume_group *vg;
+ struct lv_list *lvl;
+
+ if (!(vg = _vg_read(cmd, vgname, vgid, warnings, consistent, 0)))
+ return NULL;
+
+ if (!check_pv_segments(vg)) {
+ log_error(INTERNAL_ERROR "PV segments corrupted in %s.",
+ vg->name);
+ free_vg(vg);
+ return NULL;
+ }
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (!check_lv_segments(lvl->lv, 0)) {
+ log_error(INTERNAL_ERROR "LV segments corrupted in %s.",
+ lvl->lv->name);
+ free_vg(vg);
+ return NULL;
+ }
+ }
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ /*
+ * Checks that cross-reference other LVs.
+ */
+ if (!check_lv_segments(lvl->lv, 1)) {
+ log_error(INTERNAL_ERROR "LV segments corrupted in %s.",
+ lvl->lv->name);
+ free_vg(vg);
+ return NULL;
+ }
+ }
+
+ return vg;
+}
+
+void free_vg(struct volume_group *vg)
+{
+ if (!vg)
+ return;
+
+ if (vg->cmd && vg->vgmem == vg->cmd->mem) {
+ log_error(INTERNAL_ERROR "global memory pool used for VG %s",
+ vg->name);
+ return;
+ }
+
+ dm_pool_destroy(vg->vgmem);
+}
+
+/* This is only called by lv_from_lvid, which is only called from
+ * activate.c so we know the appropriate VG lock is already held and
+ * the vg_read_internal is therefore safe.
+ */
+static struct volume_group *_vg_read_by_vgid(struct cmd_context *cmd,
+ const char *vgid,
+ unsigned precommitted)
+{
+ const char *vgname;
+ struct dm_list *vgnames;
+ struct volume_group *vg;
+ struct lvmcache_vginfo *vginfo;
+ struct str_list *strl;
+ int consistent = 0;
+
+ /* Is corresponding vgname already cached? */
+ if ((vginfo = vginfo_from_vgid(vgid)) &&
+ vginfo->vgname && !is_orphan_vg(vginfo->vgname)) {
+ if ((vg = _vg_read(cmd, NULL, vgid, 1,
+ &consistent, precommitted)) &&
+ !strncmp((char *)vg->id.uuid, vgid, ID_LEN)) {
+ if (!consistent)
+ log_error("Volume group %s metadata is "
+ "inconsistent", vg->name);
+ return vg;
+ }
+ free_vg(vg);
+ }
+
+ /* Mustn't scan if memory locked: ensure cache gets pre-populated! */
+ if (memlock())
+ return_NULL;
+
+ /* FIXME Need a genuine read by ID here - don't vg_read_internal by name! */
+ /* FIXME Disabled vgrenames while active for now because we aren't
+ * allowed to do a full scan here any more. */
+
+ // The slow way - full scan required to cope with vgrename
+ lvmcache_label_scan(cmd, 2);
+ if (!(vgnames = get_vgnames(cmd, 0))) {
+ log_error("vg_read_by_vgid: get_vgnames failed");
+ return NULL;
+ }
+
+ dm_list_iterate_items(strl, vgnames) {
+ vgname = strl->str;
+ if (!vgname)
+ continue; // FIXME Unnecessary?
+ consistent = 0;
+ if ((vg = _vg_read(cmd, vgname, vgid, 1, &consistent,
+ precommitted)) &&
+ !strncmp((char *)vg->id.uuid, vgid, ID_LEN)) {
+ if (!consistent) {
+ log_error("Volume group %s metadata is "
+ "inconsistent", vgname);
+ free_vg(vg);
+ return NULL;
+ }
+ return vg;
+ }
+ free_vg(vg);
+ }
+
+ return NULL;
+}
+
+/* Only called by activate.c */
+struct logical_volume *lv_from_lvid(struct cmd_context *cmd, const char *lvid_s,
+ unsigned precommitted)
+{
+ struct lv_list *lvl;
+ struct volume_group *vg;
+ const union lvid *lvid;
+
+ lvid = (const union lvid *) lvid_s;
+
+ log_very_verbose("Finding volume group for uuid %s", lvid_s);
+ if (!(vg = _vg_read_by_vgid(cmd, (const char *)lvid->id[0].uuid, precommitted))) {
+ log_error("Volume group for uuid not found: %s", lvid_s);
+ return NULL;
+ }
+
+ log_verbose("Found volume group \"%s\"", vg->name);
+ if (vg->status & EXPORTED_VG) {
+ log_error("Volume group \"%s\" is exported", vg->name);
+ goto out;
+ }
+ if (!(lvl = find_lv_in_vg_by_lvid(vg, lvid))) {
+ log_very_verbose("Can't find logical volume id %s", lvid_s);
+ goto out;
+ }
+
+ return lvl->lv;
+out:
+ free_vg(vg);
+ return NULL;
+}
+
+
+const char *find_vgname_from_pvid(struct cmd_context *cmd,
+ const char *pvid)
+{
+ char *vgname;
+ struct lvmcache_info *info;
+
+ vgname = lvmcache_vgname_from_pvid(cmd, pvid);
+
+ if (is_orphan_vg(vgname)) {
+ if (!(info = info_from_pvid(pvid, 0))) {
+ return_NULL;
+ }
+ /*
+ * If an orphan PV has no MDAs, or it has MDAs but the
+ * MDA is ignored, it may appear to be an orphan until
+ * the metadata is read off another PV in the same VG.
+ * Detecting this means checking every VG by scanning
+ * every PV on the system.
+ */
+ if (mdas_empty_or_ignored(&info->mdas)) {
+ if (!scan_vgs_for_pvs(cmd, 1)) {
+ log_error("Rescan for PVs without "
+ "metadata areas failed.");
+ return NULL;
+ }
+ /*
+ * Ask lvmcache again - we may have a non-orphan
+ * name now
+ */
+ vgname = lvmcache_vgname_from_pvid(cmd, pvid);
+ }
+ }
+ return vgname;
+}
+
+
+const char *find_vgname_from_pvname(struct cmd_context *cmd,
+ const char *pvname)
+{
+ const char *pvid;
+
+ pvid = pvid_from_devname(cmd, pvname);
+ if (!pvid)
+ /* Not a PV */
+ return NULL;
+
+ return find_vgname_from_pvid(cmd, pvid);
+}
+
+/**
+ * pv_read - read and return a handle to a physical volume
+ * @cmd: LVM command initiating the pv_read
+ * @pv_name: full device name of the PV, including the path
+ * @mdas: list of metadata areas of the PV
+ * @label_sector: sector number where the PV label is stored on @pv_name
+ * @warnings:
+ *
+ * Returns:
+ * PV handle - valid pv_name and successful read of the PV, or
+ * NULL - invalid parameter or error in reading the PV
+ *
+ * Note:
+ * FIXME - liblvm todo - make into function that returns handle
+ */
+struct physical_volume *pv_read(struct cmd_context *cmd, const char *pv_name,
+ struct dm_list *mdas, uint64_t *label_sector,
+ int warnings, int scan_label_only)
+{
+ return _pv_read(cmd, cmd->mem, pv_name, mdas, label_sector, warnings, scan_label_only);
+}
+
+/* FIXME Use label functions instead of PV functions */
+static struct physical_volume *_pv_read(struct cmd_context *cmd,
+ struct dm_pool *pvmem,
+ const char *pv_name,
+ struct dm_list *mdas,
+ uint64_t *label_sector,
+ int warnings, int scan_label_only)
+{
+ struct physical_volume *pv;
+ struct label *label;
+ struct lvmcache_info *info;
+ struct device *dev;
+
+ if (!(dev = dev_cache_get(pv_name, cmd->filter)))
+ return_NULL;
+
+ if (!(label_read(dev, &label, UINT64_C(0)))) {
+ if (warnings)
+ log_error("No physical volume label read from %s",
+ pv_name);
+ return NULL;
+ }
+
+ info = (struct lvmcache_info *) label->info;
+ if (label_sector && *label_sector)
+ *label_sector = label->sector;
+
+ pv = _alloc_pv(pvmem, dev);
+ if (!pv) {
+ log_error("pv allocation for '%s' failed", pv_name);
+ return NULL;
+ }
+
+ /* FIXME Move more common code up here */
+ if (!(info->fmt->ops->pv_read(info->fmt, pv_name, pv, mdas,
+ scan_label_only))) {
+ log_error("Failed to read existing physical volume '%s'",
+ pv_name);
+ goto bad;
+ }
+
+ if (!pv->size)
+ goto bad;
+
+ if (!alloc_pv_segment_whole_pv(pvmem, pv))
+ goto_bad;
+
+ return pv;
+bad:
+ _free_pv(pvmem, pv);
+ return NULL;
+}
+
+/* May return empty list */
+struct dm_list *get_vgnames(struct cmd_context *cmd, int include_internal)
+{
+ return lvmcache_get_vgnames(cmd, include_internal);
+}
+
+struct dm_list *get_vgids(struct cmd_context *cmd, int include_internal)
+{
+ return lvmcache_get_vgids(cmd, include_internal);
+}
+
+static int _get_pvs(struct cmd_context *cmd, int warnings, struct dm_list **pvslist)
+{
+ struct str_list *strl;
+ struct dm_list * uninitialized_var(results);
+ const char *vgname, *vgid;
+ struct pv_list *pvl, *pvl_copy;
+ struct dm_list *vgids;
+ struct volume_group *vg;
+ int consistent = 0;
+ int old_pvmove;
+
+ lvmcache_label_scan(cmd, 0);
+
+ if (pvslist) {
+ if (!(results = dm_pool_alloc(cmd->mem, sizeof(*results)))) {
+ log_error("PV list allocation failed");
+ return 0;
+ }
+
+ dm_list_init(results);
+ }
+
+ /* Get list of VGs */
+ if (!(vgids = get_vgids(cmd, 1))) {
+ log_error("get_pvs: get_vgids failed");
+ return 0;
+ }
+
+ /* Read every VG to ensure cache consistency */
+ /* Orphan VG is last on list */
+ old_pvmove = pvmove_mode();
+ init_pvmove(1);
+ dm_list_iterate_items(strl, vgids) {
+ vgid = strl->str;
+ if (!vgid)
+ continue; /* FIXME Unnecessary? */
+ consistent = 0;
+ if (!(vgname = vgname_from_vgid(NULL, vgid))) {
+ stack;
+ continue;
+ }
+ if (!(vg = vg_read_internal(cmd, vgname, vgid, warnings, &consistent))) {
+ stack;
+ continue;
+ }
+ if (!consistent)
+ log_warn("WARNING: Volume Group %s is not consistent",
+ vgname);
+
+ /* Move PVs onto results list */
+ if (pvslist)
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (!(pvl_copy = _copy_pvl(cmd->mem, pvl))) {
+ log_error("PV list allocation failed");
+ free_vg(vg);
+ return 0;
+ }
+ dm_list_add(results, &pvl_copy->list);
+ }
+ free_vg(vg);
+ }
+ init_pvmove(old_pvmove);
+
+ if (pvslist)
+ *pvslist = results;
+ else
+ dm_pool_free(cmd->mem, vgids);
+
+ return 1;
+}
+
+struct dm_list *get_pvs(struct cmd_context *cmd)
+{
+ struct dm_list *results;
+
+ if (!_get_pvs(cmd, 1, &results))
+ return NULL;
+
+ return results;
+}
+
+int scan_vgs_for_pvs(struct cmd_context *cmd, int warnings)
+{
+ return _get_pvs(cmd, warnings, NULL);
+}
+
+int pv_write(struct cmd_context *cmd __attribute__((unused)),
+ struct physical_volume *pv,
+ struct dm_list *mdas, int64_t label_sector)
+{
+ if (!pv->fmt->ops->pv_write) {
+ log_error("Format does not support writing physical volumes");
+ return 0;
+ }
+
+ if (!is_orphan_vg(pv->vg_name) || pv->pe_alloc_count) {
+ log_error("Assertion failed: can't _pv_write non-orphan PV "
+ "(in VG %s)", pv->vg_name);
+ return 0;
+ }
+
+ if (!pv->fmt->ops->pv_write(pv->fmt, pv, mdas, label_sector))
+ return_0;
+
+ return 1;
+}
+
+int pv_write_orphan(struct cmd_context *cmd, struct physical_volume *pv)
+{
+ const char *old_vg_name = pv->vg_name;
+
+ pv->vg_name = cmd->fmt->orphan_vg_name;
+ pv->status = ALLOCATABLE_PV;
+ pv->pe_alloc_count = 0;
+
+ if (!dev_get_size(pv->dev, &pv->size)) {
+ log_error("%s: Couldn't get size.", pv_dev_name(pv));
+ return 0;
+ }
+
+ if (!pv_write(cmd, pv, NULL, INT64_C(-1))) {
+ log_error("Failed to clear metadata from physical "
+ "volume \"%s\" after removal from \"%s\"",
+ pv_dev_name(pv), old_vg_name);
+ return 0;
+ }
+
+ return 1;
+}
+
+int is_global_vg(const char *vg_name)
+{
+ return (vg_name && !strcmp(vg_name, VG_GLOBAL)) ? 1 : 0;
+}
+
+/**
+ * is_orphan_vg - Determine whether a vg_name is an orphan
+ * @vg_name: pointer to the vg_name
+ */
+int is_orphan_vg(const char *vg_name)
+{
+ return (vg_name && !strncmp(vg_name, ORPHAN_PREFIX, sizeof(ORPHAN_PREFIX) - 1)) ? 1 : 0;
+}
+
+/*
+ * Returns:
+ * 0 - fail
+ * 1 - success
+ */
+int pv_analyze(struct cmd_context *cmd, const char *pv_name,
+ uint64_t label_sector)
+{
+ struct label *label;
+ struct device *dev;
+ struct metadata_area *mda;
+ struct lvmcache_info *info;
+
+ dev = dev_cache_get(pv_name, cmd->filter);
+ if (!dev) {
+ log_error("Device %s not found (or ignored by filtering).",
+ pv_name);
+ return 0;
+ }
+
+ /*
+ * First, scan for LVM labels.
+ */
+ if (!label_read(dev, &label, label_sector)) {
+ log_error("Could not find LVM label on %s",
+ pv_name);
+ return 0;
+ }
+
+ log_print("Found label on %s, sector %"PRIu64", type=%s",
+ pv_name, label->sector, label->type);
+
+ /*
+ * Next, loop through metadata areas
+ */
+ info = label->info;
+ dm_list_iterate_items(mda, &info->mdas)
+ mda->ops->pv_analyze_mda(info->fmt, mda);
+
+ return 1;
+}
+
+/* FIXME: remove / combine this with locking? */
+int vg_check_write_mode(struct volume_group *vg)
+{
+ if (vg->open_mode != 'w') {
+ log_errno(EPERM, "Attempt to modify a read-only VG");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Performs a set of checks against a VG according to bits set in status
+ * and returns FAILED_* bits for those that aren't acceptable.
+ *
+ * FIXME Remove the unnecessary duplicate definitions and return bits directly.
+ */
+static uint32_t _vg_bad_status_bits(const struct volume_group *vg,
+ uint64_t status)
+{
+ uint32_t failure = 0;
+
+ if ((status & CLUSTERED) &&
+ (vg_is_clustered(vg)) && !locking_is_clustered()) {
+ log_error("Skipping clustered volume group %s", vg->name);
+ /* Return because other flags are considered undefined. */
+ return FAILED_CLUSTERED;
+ }
+
+ if ((status & EXPORTED_VG) &&
+ vg_is_exported(vg)) {
+ log_error("Volume group %s is exported", vg->name);
+ failure |= FAILED_EXPORTED;
+ }
+
+ if ((status & LVM_WRITE) &&
+ !(vg->status & LVM_WRITE)) {
+ log_error("Volume group %s is read-only", vg->name);
+ failure |= FAILED_READ_ONLY;
+ }
+
+ if ((status & RESIZEABLE_VG) &&
+ !vg_is_resizeable(vg)) {
+ log_error("Volume group %s is not resizeable.", vg->name);
+ failure |= FAILED_RESIZEABLE;
+ }
+
+ return failure;
+}
+
+/**
+ * vg_check_status - check volume group status flags and log error
+ * @vg - volume group to check status flags
+ * @status - specific status flags to check (e.g. EXPORTED_VG)
+ */
+int vg_check_status(const struct volume_group *vg, uint64_t status)
+{
+ return !_vg_bad_status_bits(vg, status);
+}
+
+static struct volume_group *_recover_vg(struct cmd_context *cmd,
+ const char *vg_name, const char *vgid)
+{
+ int consistent = 1;
+ struct volume_group *vg;
+
+ unlock_vg(cmd, vg_name);
+
+ dev_close_all();
+
+ if (!lock_vol(cmd, vg_name, LCK_VG_WRITE))
+ return_NULL;
+
+ if (!(vg = vg_read_internal(cmd, vg_name, vgid, 1, &consistent)))
+ return_NULL;
+
+ if (!consistent) {
+ free_vg(vg);
+ return_NULL;
+ }
+
+ return (struct volume_group *)vg;
+}
+
+/*
+ * Consolidated locking, reading, and status flag checking.
+ *
+ * If the metadata is inconsistent, setting READ_ALLOW_INCONSISTENT in
+ * misc_flags will return it with FAILED_INCONSISTENT set instead of
+ * giving you nothing.
+ *
+ * Use vg_read_error(vg) to determine the result. Nonzero means there were
+ * problems reading the volume group.
+ * Zero value means that the VG is open and appropriate locks are held.
+ */
+static struct volume_group *_vg_lock_and_read(struct cmd_context *cmd, const char *vg_name,
+ const char *vgid, uint32_t lock_flags,
+ uint64_t status_flags, uint32_t misc_flags)
+{
+ struct volume_group *vg = NULL;
+ int consistent = 1;
+ int consistent_in;
+ uint32_t failure = 0;
+ int already_locked;
+
+ if (misc_flags & READ_ALLOW_INCONSISTENT || lock_flags != LCK_VG_WRITE)
+ consistent = 0;
+
+ if (!validate_name(vg_name) && !is_orphan_vg(vg_name)) {
+ log_error("Volume group name %s has invalid characters",
+ vg_name);
+ return NULL;
+ }
+
+ already_locked = vgname_is_locked(vg_name);
+
+ if (!already_locked && !(misc_flags & READ_WITHOUT_LOCK) &&
+ !lock_vol(cmd, vg_name, lock_flags)) {
+ log_error("Can't get lock for %s", vg_name);
+ return _vg_make_handle(cmd, vg, FAILED_LOCKING);
+ }
+
+ if (is_orphan_vg(vg_name))
+ status_flags &= ~LVM_WRITE;
+
+ consistent_in = consistent;
+
+ /* If consistent == 1, we get NULL here if correction fails. */
+ if (!(vg = vg_read_internal(cmd, vg_name, vgid, 1, &consistent))) {
+ if (consistent_in && !consistent) {
+ log_error("Volume group \"%s\" inconsistent.", vg_name);
+ failure |= FAILED_INCONSISTENT;
+ goto_bad;
+ }
+
+ log_error("Volume group \"%s\" not found", vg_name);
+
+ failure |= FAILED_NOTFOUND;
+ goto_bad;
+ }
+
+ if (vg_is_clustered(vg) && !locking_is_clustered()) {
+ log_error("Skipping clustered volume group %s", vg->name);
+ failure |= FAILED_CLUSTERED;
+ goto_bad;
+ }
+
+ /* consistent == 0 when VG is not found, but failed == FAILED_NOTFOUND */
+ if (!consistent && !failure) {
+ free_vg(vg);
+ if (!(vg = _recover_vg(cmd, vg_name, vgid))) {
+ log_error("Recovery of volume group \"%s\" failed.",
+ vg_name);
+ failure |= FAILED_INCONSISTENT;
+ goto_bad;
+ }
+ }
+
+ /*
+ * Check that the tool can handle tricky cases -- missing PVs and
+ * unknown segment types.
+ */
+
+ if (!cmd->handles_missing_pvs && vg_missing_pv_count(vg) &&
+ lock_flags == LCK_VG_WRITE) {
+ log_error("Cannot change VG %s while PVs are missing.", vg->name);
+ log_error("Consider vgreduce --removemissing.");
+ failure |= FAILED_INCONSISTENT; /* FIXME new failure code here? */
+ goto_bad;
+ }
+
+ if (!cmd->handles_unknown_segments && vg_has_unknown_segments(vg) &&
+ lock_flags == LCK_VG_WRITE) {
+ log_error("Cannot change VG %s with unknown segments in it!",
+ vg->name);
+ failure |= FAILED_INCONSISTENT; /* FIXME new failure code here? */
+ goto_bad;
+ }
+
+ failure |= _vg_bad_status_bits(vg, status_flags);
+ if (failure)
+ goto_bad;
+
+ return _vg_make_handle(cmd, vg, failure);
+
+bad:
+ if (!already_locked && !(misc_flags & READ_WITHOUT_LOCK))
+ unlock_vg(cmd, vg_name);
+
+ return _vg_make_handle(cmd, vg, failure);
+}
+
+/*
+ * vg_read: High-level volume group metadata read function.
+ *
+ * vg_read_error() must be used on any handle returned to check for errors.
+ *
+ * - metadata inconsistent and automatic correction failed: FAILED_INCONSISTENT
+ * - VG is read-only: FAILED_READ_ONLY
+ * - VG is EXPORTED, unless flags has READ_ALLOW_EXPORTED: FAILED_EXPORTED
+ * - VG is not RESIZEABLE: FAILED_RESIZEABLE
+ * - locking failed: FAILED_LOCKING
+ *
+ * On failures, all locks are released, unless one of the following applies:
+ * - vgname_is_locked(lock_name) is true
+ * FIXME: remove the above 2 conditions if possible and make an error always
+ * release the lock.
+ *
+ * Volume groups are opened read-only unless flags contains READ_FOR_UPDATE.
+ *
+ * Checking for VG existence:
+ *
+ * FIXME: We want vg_read to attempt automatic recovery after acquiring a
+ * temporary write lock: if that fails, we bail out as usual, with failed &
+ * FAILED_INCONSISTENT. If it works, we are good to go. Code that's been in
+ * toollib just set lock_flags to LCK_VG_WRITE and called vg_read_internal with
+ * *consistent = 1.
+ */
+struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name,
+ const char *vgid, uint32_t flags)
+{
+ uint64_t status = UINT64_C(0);
+ uint32_t lock_flags = LCK_VG_READ;
+
+ if (flags & READ_FOR_UPDATE) {
+ status |= EXPORTED_VG | LVM_WRITE;
+ lock_flags = LCK_VG_WRITE;
+ }
+
+ if (flags & READ_ALLOW_EXPORTED)
+ status &= ~EXPORTED_VG;
+
+ return _vg_lock_and_read(cmd, vg_name, vgid, lock_flags, status, flags);
+}
+
+/*
+ * A high-level volume group metadata reading function. Open a volume group for
+ * later update (this means the user code can change the metadata and later
+ * request the new metadata to be written and committed).
+ */
+struct volume_group *vg_read_for_update(struct cmd_context *cmd, const char *vg_name,
+ const char *vgid, uint32_t flags)
+{
+ return vg_read(cmd, vg_name, vgid, flags | READ_FOR_UPDATE);
+}
+
+/*
+ * Test the validity of a VG handle returned by vg_read() or vg_read_for_update().
+ */
+uint32_t vg_read_error(struct volume_group *vg_handle)
+{
+ if (!vg_handle)
+ return FAILED_ALLOCATION;
+
+ return vg_handle->read_status;
+}
+
+/*
+ * Lock a vgname and/or check for existence.
+ * Takes a WRITE lock on the vgname before scanning.
+ * If scanning fails or vgname found, release the lock.
+ * NOTE: If you find the return codes confusing, you might think of this
+ * function as similar to an open() call with O_CREAT and O_EXCL flags
+ * (open returns fail with -EEXIST if file already exists).
+ *
+ * Returns:
+ * FAILED_LOCKING - Cannot lock name
+ * FAILED_EXIST - VG name already exists - cannot reserve
+ * SUCCESS - VG name does not exist in system and WRITE lock held
+ */
+uint32_t vg_lock_newname(struct cmd_context *cmd, const char *vgname)
+{
+ if (!lock_vol(cmd, vgname, LCK_VG_WRITE)) {
+ return FAILED_LOCKING;
+ }
+
+ /* Find the vgname in the cache */
+ /* If it's not there we must do full scan to be completely sure */
+ if (!fmt_from_vgname(vgname, NULL, 1)) {
+ lvmcache_label_scan(cmd, 0);
+ if (!fmt_from_vgname(vgname, NULL, 1)) {
+ /* Independent MDAs aren't supported under low memory */
+ if (!cmd->independent_metadata_areas && memlock()) {
+ /*
+ * FIXME: Disallow calling this function if
+ * memlock() is true.
+ */
+ unlock_vg(cmd, vgname);
+ return FAILED_LOCKING;
+ }
+ lvmcache_label_scan(cmd, 2);
+ if (!fmt_from_vgname(vgname, NULL, 0)) {
+ /* vgname not found after scanning */
+ return SUCCESS;
+ }
+ }
+ }
+
+ /* Found vgname so cannot reserve. */
+ unlock_vg(cmd, vgname);
+ return FAILED_EXIST;
+}
+
+void fid_add_mda(struct format_instance *fid, struct metadata_area *mda)
+{
+ dm_list_add(mda_is_ignored(mda) ? &fid->metadata_areas_ignored :
+ &fid->metadata_areas_in_use, &mda->list);
+}
+
+int fid_add_mdas(struct format_instance *fid, struct dm_list *mdas)
+{
+ struct metadata_area *mda, *mda_new;
+
+ dm_list_iterate_items(mda, mdas) {
+ mda_new = mda_copy(fid->fmt->cmd->mem, mda);
+ if (!mda_new)
+ return_0;
+ fid_add_mda(fid, mda_new);
+ }
+ return 1;
+}
+
+/*
+ * Copy constructor for a metadata_area.
+ */
+struct metadata_area *mda_copy(struct dm_pool *mem,
+ struct metadata_area *mda)
+{
+ struct metadata_area *mda_new;
+
+ if (!(mda_new = dm_pool_alloc(mem, sizeof(*mda_new)))) {
+ log_error("metadata_area allocation failed");
+ return NULL;
+ }
+ memcpy(mda_new, mda, sizeof(*mda));
+ if (mda->ops->mda_metadata_locn_copy && mda->metadata_locn) {
+ mda_new->metadata_locn =
+ mda->ops->mda_metadata_locn_copy(mem, mda->metadata_locn);
+ if (!mda_new->metadata_locn) {
+ dm_pool_free(mem, mda_new);
+ return NULL;
+ }
+ }
+
+ dm_list_init(&mda_new->list);
+
+ return mda_new;
+}
+/*
+ * This function provides a way to answer the question on a format specific
+ * basis - does the format specfic context of these two metadata areas
+ * match?
+ *
+ * A metatdata_area is defined to be independent of the underlying context.
+ * This has the benefit that we can use the same abstraction to read disks
+ * (see _metadata_text_raw_ops) or files (see _metadata_text_file_ops).
+ * However, one downside is there is no format-independent way to determine
+ * whether a given metadata_area is attached to a specific device - in fact,
+ * it may not be attached to a device at all.
+ *
+ * Thus, LVM is structured such that an mda is not a member of struct
+ * physical_volume. The location of the mda depends on whether
+ * the PV is in a volume group. A PV not in a VG has an mda on the
+ * 'info->mda' list in lvmcache, while a PV in a VG has an mda on
+ * the vg->fid->metadata_areas_in_use list. For further details, see _vg_read(),
+ * and the sequence of creating the format_instance with fid->metadata_areas_in_use
+ * list, as well as the construction of the VG, with list of PVs (comes
+ * after the construction of the fid and list of mdas).
+ */
+unsigned mda_locns_match(struct metadata_area *mda1, struct metadata_area *mda2)
+{
+ if (!mda1->ops->mda_locns_match || !mda2->ops->mda_locns_match ||
+ mda1->ops->mda_locns_match != mda2->ops->mda_locns_match)
+ return 0;
+
+ return mda1->ops->mda_locns_match(mda1, mda2);
+}
+
+unsigned mda_is_ignored(struct metadata_area *mda)
+{
+ return (mda->status & MDA_IGNORED);
+}
+
+void mda_set_ignored(struct metadata_area *mda, unsigned mda_ignored)
+{
+ void *locn = mda->metadata_locn;
+ unsigned old_mda_ignored = mda_is_ignored(mda);
+
+ if (mda_ignored && !old_mda_ignored)
+ mda->status |= MDA_IGNORED;
+ else if (!mda_ignored && old_mda_ignored)
+ mda->status &= ~MDA_IGNORED;
+ else
+ return; /* No change */
+
+ log_debug("%s ignored flag for mda %s at offset %" PRIu64 ".",
+ mda_ignored ? "Setting" : "Clearing",
+ mda->ops->mda_metadata_locn_name ? mda->ops->mda_metadata_locn_name(locn) : "",
+ mda->ops->mda_metadata_locn_offset ? mda->ops->mda_metadata_locn_offset(locn) : UINT64_C(0));
+}
+
+int mdas_empty_or_ignored(struct dm_list *mdas)
+{
+ struct metadata_area *mda;
+
+ if (!dm_list_size(mdas))
+ return 1;
+ dm_list_iterate_items(mda, mdas) {
+ if (mda_is_ignored(mda))
+ return 1;
+ }
+ return 0;
+}
+
+int pv_change_metadataignore(struct physical_volume *pv, uint32_t mda_ignored)
+{
+ const char *pv_name = pv_dev_name(pv);
+
+ if (mda_ignored && !pv_mda_used_count(pv)) {
+ log_error("Metadata areas on physical volume \"%s\" already "
+ "ignored.", pv_name);
+ return 0;
+ }
+
+ if (!mda_ignored && (pv_mda_used_count(pv) == pv_mda_count(pv))) {
+ log_error("Metadata areas on physical volume \"%s\" already "
+ "marked as in-use.", pv_name);
+ return 0;
+ }
+
+ if (!pv_mda_count(pv)) {
+ log_error("Physical volume \"%s\" has no metadata "
+ "areas.", pv_name);
+ return 0;
+ }
+
+ log_verbose("Marking metadata areas on physical volume \"%s\" "
+ "as %s.", pv_name, mda_ignored ? "ignored" : "in-use");
+
+ if (!pv_mda_set_ignored(pv, mda_ignored))
+ return_0;
+
+ /*
+ * Update vg_mda_copies based on the mdas in this PV.
+ * This is most likely what the user would expect - if they
+ * specify a specific PV to be ignored/un-ignored, they will
+ * most likely not want LVM to turn around and change the
+ * ignore / un-ignore value when it writes the VG to disk.
+ * This does not guarantee this PV's ignore bits will be
+ * preserved in future operations.
+ */
+ if (!is_orphan(pv) &&
+ vg_mda_copies(pv->vg) != VGMETADATACOPIES_UNMANAGED) {
+ log_warn("WARNING: Changing preferred number of copies of VG %s "
+ "metadata from %"PRIu32" to %"PRIu32, pv_vg_name(pv),
+ vg_mda_copies(pv->vg), vg_mda_used_count(pv->vg));
+ vg_set_mda_copies(pv->vg, vg_mda_used_count(pv->vg));
+ }
+
+ return 1;
+}
+
+char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tags)
+{
+ struct str_list *sl;
+
+ if (!dm_pool_begin_object(mem, 256)) {
+ log_error("dm_pool_begin_object failed");
+ return NULL;
+ }
+
+ dm_list_iterate_items(sl, tags) {
+ if (!dm_pool_grow_object(mem, sl->str, strlen(sl->str)) ||
+ (sl->list.n != tags && !dm_pool_grow_object(mem, ",", 1))) {
+ log_error("dm_pool_grow_object failed");
+ return NULL;
+ }
+ }
+
+ if (!dm_pool_grow_object(mem, "\0", 1)) {
+ log_error("dm_pool_grow_object failed");
+ return NULL;
+ }
+ return dm_pool_end_object(mem);
+}
+
+/**
+ * pv_by_path - Given a device path return a PV handle if it is a PV
+ * @cmd - handle to the LVM command instance
+ * @pv_name - device path to read for the PV
+ *
+ * Returns:
+ * NULL - device path does not contain a valid PV
+ * non-NULL - PV handle corresponding to device path
+ *
+ * FIXME: merge with find_pv_by_name ?
+ */
+struct physical_volume *pv_by_path(struct cmd_context *cmd, const char *pv_name)
+{
+ struct dm_list mdas;
+
+ dm_list_init(&mdas);
+ return _pv_read(cmd, cmd->mem, pv_name, &mdas, NULL, 1, 0);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This is the in core representation of a volume group and its
+ * associated physical and logical volumes.
+ */
+
+#ifndef _LVM_METADATA_H
+#define _LVM_METADATA_H
+
+#include "ctype.h"
+#include "dev-cache.h"
+#include "lvm-string.h"
+#include "metadata-exported.h"
+
+//#define MAX_STRIPES 128U
+//#define SECTOR_SHIFT 9L
+//#define SECTOR_SIZE ( 1L << SECTOR_SHIFT )
+//#define STRIPE_SIZE_MIN ( (unsigned) lvm_getpagesize() >> SECTOR_SHIFT) /* PAGESIZE in sectors */
+//#define STRIPE_SIZE_MAX ( 512L * 1024L >> SECTOR_SHIFT) /* 512 KB in sectors */
+//#define STRIPE_SIZE_LIMIT ((UINT_MAX >> 2) + 1)
+//#define PV_MIN_SIZE ( 512L * 1024L >> SECTOR_SHIFT) /* 512 KB in sectors */
+//#define MAX_RESTRICTED_LVS 255 /* Used by FMT_RESTRICTED_LVIDS */
+#define MIRROR_LOG_OFFSET 2 /* sectors */
+#define VG_MEMPOOL_CHUNK 10240 /* in bytes, hint only */
+
+/*
+ * Ceiling(n / sz)
+ */
+#define dm_div_up(n, sz) (((n) + (sz) - 1) / (sz))
+
+/*
+ * Ceiling(n / size) * size
+ */
+#define dm_round_up(n, sz) (dm_div_up((n), (sz)) * (sz))
+
+
+/* Various flags */
+/* Note that the bits no longer necessarily correspond to LVM1 disk format */
+
+//#define PARTIAL_VG 0x00000001U /* VG */
+//#define EXPORTED_VG 0x00000002U /* VG PV */
+//#define RESIZEABLE_VG 0x00000004U /* VG */
+
+/* May any free extents on this PV be used or must they be left free? */
+//#define ALLOCATABLE_PV 0x00000008U /* PV */
+
+#define SPINDOWN_LV 0x00000010U /* LV */
+#define BADBLOCK_ON 0x00000020U /* LV */
+//#define VISIBLE_LV 0x00000040U /* LV */
+//#define FIXED_MINOR 0x00000080U /* LV */
+/* FIXME Remove when metadata restructuring is completed */
+//#define SNAPSHOT 0x00001000U /* LV - internal use only */
+//#define PVMOVE 0x00002000U /* VG LV SEG */
+//#define LOCKED 0x00004000U /* LV */
+//#define MIRRORED 0x00008000U /* LV - internal use only */
+#define VIRTUAL 0x00010000U /* LV - internal use only */
+//#define MIRROR_LOG 0x00020000U /* LV */
+//#define MIRROR_IMAGE 0x00040000U /* LV */
+//#define MIRROR_NOTSYNCED 0x00080000U /* LV */
+#define ACTIVATE_EXCL 0x00100000U /* LV - internal use only */
+#define PRECOMMITTED 0x00200000U /* VG - internal use only */
+//#define CONVERTING 0x00400000U /* LV */
+
+//#define MISSING_PV 0x00800000U /* PV */
+#define INCONSISTENT_VG 0x00800000U /* VG - internal use only */
+//#define PARTIAL_LV 0x01000000U /* LV - derived flag, not
+// written out in metadata*/
+
+#define POSTORDER_FLAG 0x02000000U /* Not real flags, reserved for */
+#define POSTORDER_OPEN_FLAG 0x04000000U /* temporary use inside vg_read_internal. */
+#define VIRTUAL_ORIGIN 0x08000000U /* LV - internal use only */
+
+//#define LVM_READ 0x00000100U /* LV VG */
+//#define LVM_WRITE 0x00000200U /* LV VG */
+//#define CLUSTERED 0x00000400U /* VG */
+#define SHARED 0x00000800U /* VG */
+
+/* Format features flags */
+//#define FMT_SEGMENTS 0x00000001U /* Arbitrary segment params? */
+//#define FMT_MDAS 0x00000002U /* Proper metadata areas? */
+//#define FMT_TAGS 0x00000004U /* Tagging? */
+//#define FMT_UNLIMITED_VOLS 0x00000008U /* Unlimited PVs/LVs? */
+//#define FMT_RESTRICTED_LVIDS 0x00000010U /* LVID <= 255 */
+//#define FMT_ORPHAN_ALLOCATABLE 0x00000020U /* Orphan PV allocatable? */
+#define FMT_PRECOMMIT 0x00000040U /* Supports pre-commit? */
+//#define FMT_RESIZE_PV 0x00000080U /* Supports pvresize? */
+//#define FMT_UNLIMITED_STRIPESIZE 0x00000100U /* Unlimited stripe size? */
+
+struct metadata_area;
+
+/* Per-format per-metadata area operations */
+struct metadata_area_ops {
+ struct volume_group *(*vg_read) (struct format_instance * fi,
+ const char *vg_name,
+ struct metadata_area * mda);
+ struct volume_group *(*vg_read_precommit) (struct format_instance * fi,
+ const char *vg_name,
+ struct metadata_area * mda);
+ /*
+ * Write out complete VG metadata. You must ensure internal
+ * consistency before calling. eg. PEs can't refer to PVs not
+ * part of the VG.
+ *
+ * It is also the responsibility of the caller to ensure external
+ * consistency, eg by calling pv_write() if removing PVs from
+ * a VG or calling vg_write() a second time if splitting a VG
+ * into two.
+ *
+ * vg_write() should not read or write from any PVs not included
+ * in the volume_group structure it is handed.
+ * (format1 currently breaks this rule.)
+ */
+ int (*vg_write) (struct format_instance * fid, struct volume_group * vg,
+ struct metadata_area * mda);
+ int (*vg_precommit) (struct format_instance * fid,
+ struct volume_group * vg,
+ struct metadata_area * mda);
+ int (*vg_commit) (struct format_instance * fid,
+ struct volume_group * vg, struct metadata_area * mda);
+ int (*vg_revert) (struct format_instance * fid,
+ struct volume_group * vg, struct metadata_area * mda);
+ int (*vg_remove) (struct format_instance * fi, struct volume_group * vg,
+ struct metadata_area * mda);
+
+ /*
+ * Per location copy constructor.
+ */
+ void *(*mda_metadata_locn_copy) (struct dm_pool *mem, void *metadata_locn);
+
+ /*
+ * Per location description for logging.
+ */
+ const char *(*mda_metadata_locn_name) (void *metadata_locn);
+ uint64_t (*mda_metadata_locn_offset) (void *metadata_locn);
+
+ /*
+ * Returns number of free sectors in given metadata area.
+ */
+ uint64_t (*mda_free_sectors) (struct metadata_area *mda);
+
+ /*
+ * Returns number of total sectors in given metadata area.
+ */
+ uint64_t (*mda_total_sectors) (struct metadata_area *mda);
+
+ /*
+ * Check if metadata area belongs to vg
+ */
+ int (*mda_in_vg) (struct format_instance * fi,
+ struct volume_group * vg, struct metadata_area *mda);
+ /*
+ * Analyze a metadata area on a PV.
+ */
+ int (*pv_analyze_mda) (const struct format_type * fmt,
+ struct metadata_area *mda);
+
+ /*
+ * Do these two metadata_area structures match with respect to
+ * their underlying location?
+ */
+ unsigned (*mda_locns_match)(struct metadata_area *mda1,
+ struct metadata_area *mda2);
+};
+
+#define MDA_IGNORED 0x00000001
+
+struct metadata_area {
+ struct dm_list list;
+ struct metadata_area_ops *ops;
+ void *metadata_locn;
+ uint32_t status;
+};
+struct metadata_area *mda_copy(struct dm_pool *mem,
+ struct metadata_area *mda);
+
+unsigned mda_is_ignored(struct metadata_area *mda);
+void mda_set_ignored(struct metadata_area *mda, unsigned ignored);
+unsigned mda_locns_match(struct metadata_area *mda1, struct metadata_area *mda2);
+void fid_add_mda(struct format_instance *fid, struct metadata_area *mda);
+int fid_add_mdas(struct format_instance *fid, struct dm_list *mdas);
+int mdas_empty_or_ignored(struct dm_list *mdas);
+
+#define seg_pvseg(seg, s) (seg)->areas[(s)].u.pv.pvseg
+#define seg_dev(seg, s) (seg)->areas[(s)].u.pv.pvseg->pv->dev
+#define seg_pe(seg, s) (seg)->areas[(s)].u.pv.pvseg->pe
+#define seg_le(seg, s) (seg)->areas[(s)].u.lv.le
+
+struct name_list {
+ struct dm_list list;
+ char *name;
+};
+
+struct mda_list {
+ struct dm_list list;
+ struct device_area mda;
+};
+
+struct peg_list {
+ struct dm_list list;
+ struct pv_segment *peg;
+};
+
+struct seg_list {
+ struct dm_list list;
+ unsigned count;
+ struct lv_segment *seg;
+};
+
+/*
+ * Ownership of objects passes to caller.
+ */
+struct format_handler {
+ /*
+ * Scan any metadata areas that aren't referenced in PV labels
+ */
+ int (*scan) (const struct format_type * fmt, const char *vgname);
+
+ /*
+ * Return PV with given path.
+ */
+ int (*pv_read) (const struct format_type * fmt, const char *pv_name,
+ struct physical_volume * pv, struct dm_list *mdas,
+ int scan_label_only);
+
+ /*
+ * Tweak an already filled out a pv ready for importing into a
+ * vg. eg. pe_count is format specific.
+ */
+ int (*pv_setup) (const struct format_type * fmt,
+ uint64_t pe_start, uint32_t extent_count,
+ uint32_t extent_size, unsigned long data_alignment,
+ unsigned long data_alignment_offset,
+ int pvmetadatacopies, uint64_t pvmetadatasize,
+ unsigned metadataignore, struct dm_list * mdas,
+ struct physical_volume * pv, struct volume_group * vg);
+
+ /*
+ * Write a PV structure to disk. Fails if the PV is in a VG ie
+ * pv->vg_name must be a valid orphan VG name
+ */
+ int (*pv_write) (const struct format_type * fmt,
+ struct physical_volume * pv, struct dm_list * mdas,
+ int64_t label_sector);
+
+ /*
+ * Tweak an already filled out a lv eg, check there
+ * aren't too many extents.
+ */
+ int (*lv_setup) (struct format_instance * fi,
+ struct logical_volume * lv);
+
+ /*
+ * Tweak an already filled out vg. eg, max_pv is format
+ * specific.
+ */
+ int (*vg_setup) (struct format_instance * fi, struct volume_group * vg);
+
+ /*
+ * Check whether particular segment type is supported.
+ */
+ int (*segtype_supported) (struct format_instance *fid,
+ const struct segment_type *segtype);
+
+ /*
+ * Create format instance with a particular metadata area
+ */
+ struct format_instance *(*create_instance) (const struct format_type *
+ fmt, const char *vgname,
+ const char *vgid,
+ void *context);
+
+ /*
+ * Destructor for format instance
+ */
+ void (*destroy_instance) (struct format_instance * fid);
+
+ /*
+ * Destructor for format type
+ */
+ void (*destroy) (struct format_type * fmt);
+};
+
+/*
+ * Utility functions
+ */
+unsigned long set_pe_align(struct physical_volume *pv, unsigned long data_alignment);
+unsigned long set_pe_align_offset(struct physical_volume *pv,
+ unsigned long data_alignment_offset);
+int vg_validate(struct volume_group *vg);
+
+int pv_write_orphan(struct cmd_context *cmd, struct physical_volume *pv);
+
+/* Manipulate PV structures */
+int pv_add(struct volume_group *vg, struct physical_volume *pv);
+int pv_remove(struct volume_group *vg, struct physical_volume *pv);
+struct physical_volume *pv_find(struct volume_group *vg, const char *pv_name);
+
+/* Find a PV within a given VG */
+int get_pv_from_vg_by_id(const struct format_type *fmt, const char *vg_name,
+ const char *vgid, const char *pvid,
+ struct physical_volume *pv);
+
+struct lv_list *find_lv_in_vg_by_lvid(struct volume_group *vg,
+ const union lvid *lvid);
+
+struct lv_list *find_lv_in_lv_list(const struct dm_list *ll,
+ const struct logical_volume *lv);
+
+/* Return the VG that contains a given LV (based on path given in lv_name) */
+/* or environment var */
+struct volume_group *find_vg_with_lv(const char *lv_name);
+
+/* Find LV with given lvid (used during activation) */
+struct logical_volume *lv_from_lvid(struct cmd_context *cmd,
+ const char *lvid_s,
+ unsigned precommitted);
+
+/* FIXME Merge these functions with ones above */
+struct physical_volume *find_pv(struct volume_group *vg, struct device *dev);
+
+struct pv_list *find_pv_in_pv_list(const struct dm_list *pl,
+ const struct physical_volume *pv);
+
+/* Find LV segment containing given LE */
+struct lv_segment *find_seg_by_le(const struct logical_volume *lv, uint32_t le);
+
+/*
+ * Remove a dev_dir if present.
+ */
+const char *strip_dir(const char *vg_name, const char *dir);
+
+struct logical_volume *alloc_lv(struct dm_pool *mem);
+
+/*
+ * Checks that an lv has no gaps or overlapping segments.
+ * Set complete_vg to perform additional VG level checks.
+ */
+int check_lv_segments(struct logical_volume *lv, int complete_vg);
+
+
+/*
+ * Checks that a replicator segment is correct.
+ */
+int check_replicator_segment(const struct lv_segment *replicator_seg);
+
+/*
+ * Sometimes (eg, after an lvextend), it is possible to merge two
+ * adjacent segments into a single segment. This function trys
+ * to merge as many segments as possible.
+ */
+int lv_merge_segments(struct logical_volume *lv);
+
+/*
+ * Ensure there's a segment boundary at a given LE, splitting if necessary
+ */
+int lv_split_segment(struct logical_volume *lv, uint32_t le);
+
+/*
+ * Add/remove upward link from underlying LV to the segment using it
+ * FIXME: ridiculously long name
+ */
+int add_seg_to_segs_using_this_lv(struct logical_volume *lv, struct lv_segment *seg);
+int remove_seg_from_segs_using_this_lv(struct logical_volume *lv, struct lv_segment *seg);
+struct lv_segment *get_only_segment_using_this_lv(struct logical_volume *lv);
+
+/*
+ * Calculate readahead from underlying PV devices
+ */
+void lv_calculate_readahead(const struct logical_volume *lv, uint32_t *read_ahead);
+
+/*
+ * For internal metadata caching.
+ */
+int export_vg_to_buffer(struct volume_group *vg, char **buf);
+struct volume_group *import_vg_from_buffer(const char *buf,
+ struct format_instance *fid);
+
+/*
+ * Mirroring functions
+ */
+
+/*
+ * Given mirror image or mirror log segment, find corresponding mirror segment
+ */
+int fixup_imported_mirrors(struct volume_group *vg);
+
+/*
+ * Begin skeleton for external LVM library
+ */
+struct id pv_id(const struct physical_volume *pv);
+const struct format_type *pv_format_type(const struct physical_volume *pv);
+struct id pv_vgid(const struct physical_volume *pv);
+
+struct physical_volume *pv_by_path(struct cmd_context *cmd, const char *pv_name);
+int add_pv_to_vg(struct volume_group *vg, const char *pv_name,
+ struct physical_volume *pv);
+int vg_mark_partial_lvs(struct volume_group *vg);
+int is_mirror_image_removable(struct logical_volume *mimage_lv, void *baton);
+
+uint64_t find_min_mda_size(struct dm_list *mdas);
+char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tags);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "display.h"
+#include "archiver.h"
+#include "activate.h"
+#include "lv_alloc.h"
+#include "lvm-string.h"
+#include "str_list.h"
+#include "locking.h" /* FIXME Should not be used in this file */
+#include "memlock.h"
+
+#include "defaults.h" /* FIXME: should this be defaults.h? */
+
+/* These are necessary for _write_log_header() */
+#include "xlate.h"
+#define MIRROR_MAGIC 0x4D695272
+#define MIRROR_DISK_VERSION 2
+
+/* These are the flags that represent the mirror failure restoration policies */
+#define MIRROR_REMOVE 0
+#define MIRROR_ALLOCATE 1
+#define MIRROR_ALLOCATE_ANYWHERE 2
+
+/*
+ * Returns true if the lv is temporary mirror layer for resync
+ */
+int is_temporary_mirror_layer(const struct logical_volume *lv)
+{
+ if (lv->status & MIRROR_IMAGE
+ && lv->status & MIRRORED
+ && !(lv->status & LOCKED))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Return a temporary LV for resyncing added mirror image.
+ * Add other mirror legs to lvs list.
+ */
+struct logical_volume *find_temporary_mirror(const struct logical_volume *lv)
+{
+ struct lv_segment *seg;
+
+ if (!(lv->status & MIRRORED))
+ return NULL;
+
+ seg = first_seg(lv);
+
+ /* Temporary mirror is always area_num == 0 */
+ if (seg_type(seg, 0) == AREA_LV &&
+ is_temporary_mirror_layer(seg_lv(seg, 0)))
+ return seg_lv(seg, 0);
+
+ return NULL;
+}
+
+int lv_is_mirrored(const struct logical_volume *lv)
+{
+ if (lv->status & MIRRORED)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * cluster_mirror_is_available
+ *
+ * Check if the proper kernel module and log daemon are running.
+ * Caller should check for 'vg_is_clustered(lv->vg)' before making
+ * this call.
+ *
+ * Returns: 1 if available, 0 otherwise
+ */
+static int cluster_mirror_is_available(struct logical_volume *lv)
+{
+ unsigned attr = 0;
+ struct cmd_context *cmd = lv->vg->cmd;
+ const struct segment_type *segtype;
+
+ if (!(segtype = get_segtype_from_string(cmd, "mirror")))
+ return_0;
+
+ if (!segtype->ops->target_present)
+ return_0;
+
+ if (!segtype->ops->target_present(lv->vg->cmd, NULL, &attr))
+ return_0;
+
+ if (!(attr & MIRROR_LOG_CLUSTERED))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Returns the number of mirrors of the LV
+ */
+uint32_t lv_mirror_count(const struct logical_volume *lv)
+{
+ struct lv_segment *seg;
+ uint32_t s, mirrors;
+
+ if (!(lv->status & MIRRORED))
+ return 1;
+
+ seg = first_seg(lv);
+ mirrors = seg->area_count;
+
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) != AREA_LV)
+ continue;
+ if (is_temporary_mirror_layer(seg_lv(seg, s)))
+ mirrors += lv_mirror_count(seg_lv(seg, s)) - 1;
+ }
+
+ return mirrors;
+}
+
+struct lv_segment *find_mirror_seg(struct lv_segment *seg)
+{
+ struct lv_segment *mirror_seg;
+
+ mirror_seg = get_only_segment_using_this_lv(seg->lv);
+
+ if (!mirror_seg) {
+ log_error("Failed to find mirror_seg for %s", seg->lv->name);
+ return NULL;
+ }
+
+ if (!seg_is_mirrored(mirror_seg)) {
+ log_error("%s on %s is not a mirror segments",
+ mirror_seg->lv->name, seg->lv->name);
+ return NULL;
+ }
+
+ return mirror_seg;
+}
+
+/*
+ * Reduce the region size if necessary to ensure
+ * the volume size is a multiple of the region size.
+ */
+uint32_t adjusted_mirror_region_size(uint32_t extent_size, uint32_t extents,
+ uint32_t region_size)
+{
+ uint64_t region_max;
+
+ region_max = (1 << (ffs((int)extents) - 1)) * (uint64_t) extent_size;
+
+ if (region_max < UINT32_MAX && region_size > region_max) {
+ region_size = (uint32_t) region_max;
+ log_print("Using reduced mirror region size of %" PRIu32
+ " sectors", region_size);
+ }
+
+ return region_size;
+}
+
+/*
+ * shift_mirror_images
+ * @mirrored_seg
+ * @mimage: The position (index) of the image to move to the end
+ *
+ * When dealing with removal of legs, we often move a 'removable leg'
+ * to the back of the 'areas' array. It is critically important not
+ * to simply swap it for the last area in the array. This would have
+ * the affect of reordering the remaining legs - altering position of
+ * the primary. So, we must shuffle all of the areas in the array
+ * to maintain their relative position before moving the 'removable
+ * leg' to the end.
+ *
+ * Short illustration of the problem:
+ * - Mirror consists of legs A, B, C and we want to remove A
+ * - We swap A and C and then remove A, leaving C, B
+ * This scenario is problematic in failure cases where A dies, because
+ * B becomes the primary. If the above happens, we effectively throw
+ * away any changes made between the time of failure and the time of
+ * restructuring the mirror.
+ *
+ * So, any time we want to move areas to the end to be removed, use
+ * this function.
+ */
+int shift_mirror_images(struct lv_segment *mirrored_seg, unsigned mimage)
+{
+ int i;
+ struct lv_segment_area area;
+
+ if (mimage >= mirrored_seg->area_count) {
+ log_error("Invalid index (%u) of mirror image supplied "
+ "to shift_mirror_images()", mimage);
+ return 0;
+ }
+
+ area = mirrored_seg->areas[mimage];
+
+ /* Shift remaining images down to fill the hole */
+ for (i = mimage + 1; i < mirrored_seg->area_count; i++)
+ mirrored_seg->areas[i-1] = mirrored_seg->areas[i];
+
+ /* Place this one at the end */
+ mirrored_seg->areas[i-1] = area;
+
+ return 1;
+}
+
+/*
+ * This function writes a new header to the mirror log header to the lv
+ *
+ * Returns: 1 on success, 0 on failure
+ */
+static int _write_log_header(struct cmd_context *cmd, struct logical_volume *lv)
+{
+ struct device *dev;
+ char *name;
+ struct { /* The mirror log header */
+ uint32_t magic;
+ uint32_t version;
+ uint64_t nr_regions;
+ } log_header;
+
+ log_header.magic = xlate32(MIRROR_MAGIC);
+ log_header.version = xlate32(MIRROR_DISK_VERSION);
+ log_header.nr_regions = xlate64((uint64_t)-1);
+
+ if (!(name = dm_pool_alloc(cmd->mem, PATH_MAX))) {
+ log_error("Name allocation failed - log header not written (%s)",
+ lv->name);
+ return 0;
+ }
+
+ if (dm_snprintf(name, PATH_MAX, "%s%s/%s", cmd->dev_dir,
+ lv->vg->name, lv->name) < 0) {
+ log_error("Name too long - log header not written (%s)", lv->name);
+ return 0;
+ }
+
+ log_verbose("Writing log header to device, %s", lv->name);
+
+ if (!(dev = dev_cache_get(name, NULL))) {
+ log_error("%s: not found: log header not written", name);
+ return 0;
+ }
+
+ if (!dev_open_quiet(dev))
+ return 0;
+
+ if (!dev_write(dev, UINT64_C(0), sizeof(log_header), &log_header)) {
+ log_error("Failed to write log header to %s", name);
+ dev_close_immediate(dev);
+ return 0;
+ }
+
+ dev_close_immediate(dev);
+
+ return 1;
+}
+
+/*
+ * Initialize mirror log contents
+ */
+static int _init_mirror_log(struct cmd_context *cmd,
+ struct logical_volume *log_lv, int in_sync,
+ struct dm_list *tags, int remove_on_failure)
+{
+ struct str_list *sl;
+ struct lvinfo info;
+ uint64_t orig_status = log_lv->status;
+ int was_active = 0;
+
+ if (!activation() && in_sync) {
+ log_error("Aborting. Unable to create in-sync mirror log "
+ "while activation is disabled.");
+ return 0;
+ }
+
+ /* If the LV is active, deactivate it first. */
+ if (lv_info(cmd, log_lv, 0, &info, 0, 0) && info.exists) {
+ (void)deactivate_lv(cmd, log_lv);
+ /*
+ * FIXME: workaround to fail early
+ * Ensure that log is really deactivated because deactivate_lv
+ * on cluster do not fail if there is log_lv with different UUID.
+ */
+ if (lv_info(cmd, log_lv, 0, &info, 0, 0) && info.exists) {
+ log_error("Aborting. Unable to deactivate mirror log.");
+ goto revert_new_lv;
+ }
+ was_active = 1;
+ }
+
+ /* Temporary make it visible for set_lv() */
+ lv_set_visible(log_lv);
+
+ /* Temporary tag mirror log for activation */
+ dm_list_iterate_items(sl, tags)
+ if (!str_list_add(cmd->mem, &log_lv->tags, sl->str)) {
+ log_error("Aborting. Unable to tag mirror log.");
+ goto activate_lv;
+ }
+
+ /* store mirror log on disk(s) */
+ if (!vg_write(log_lv->vg) || !vg_commit(log_lv->vg))
+ goto activate_lv;
+
+ backup(log_lv->vg);
+
+ if (!activate_lv(cmd, log_lv)) {
+ log_error("Aborting. Failed to activate mirror log.");
+ goto revert_new_lv;
+ }
+
+ /* Remove the temporary tags */
+ dm_list_iterate_items(sl, tags)
+ if (!str_list_del(&log_lv->tags, sl->str))
+ log_error("Failed to remove tag %s from mirror log.",
+ sl->str);
+
+ if (activation() && !set_lv(cmd, log_lv, log_lv->size,
+ in_sync ? -1 : 0)) {
+ log_error("Aborting. Failed to wipe mirror log.");
+ goto deactivate_and_revert_new_lv;
+ }
+
+ if (activation() && !_write_log_header(cmd, log_lv)) {
+ log_error("Aborting. Failed to write mirror log header.");
+ goto deactivate_and_revert_new_lv;
+ }
+
+ if (!deactivate_lv(cmd, log_lv)) {
+ log_error("Aborting. Failed to deactivate mirror log. "
+ "Manual intervention required.");
+ return 0;
+ }
+
+ lv_set_hidden(log_lv);
+
+ if (was_active && !activate_lv(cmd, log_lv))
+ return_0;
+
+ return 1;
+
+deactivate_and_revert_new_lv:
+ if (!deactivate_lv(cmd, log_lv)) {
+ log_error("Unable to deactivate mirror log LV. "
+ "Manual intervention required.");
+ return 0;
+ }
+
+revert_new_lv:
+ log_lv->status = orig_status;
+
+ dm_list_iterate_items(sl, tags)
+ if (!str_list_del(&log_lv->tags, sl->str))
+ log_error("Failed to remove tag %s from mirror log.",
+ sl->str);
+
+ if (remove_on_failure && !lv_remove(log_lv)) {
+ log_error("Manual intervention may be required to remove "
+ "abandoned log LV before retrying.");
+ return 0;
+ }
+
+ if (!vg_write(log_lv->vg) || !vg_commit(log_lv->vg))
+ log_error("Manual intervention may be required to "
+ "remove/restore abandoned log LV before retrying.");
+ else
+ backup(log_lv->vg);
+
+activate_lv:
+ if (was_active && !remove_on_failure && !activate_lv(cmd, log_lv))
+ return_0;
+
+ return 0;
+}
+
+/*
+ * Delete independent/orphan LV, it must acquire lock.
+ */
+static int _delete_lv(struct logical_volume *mirror_lv, struct logical_volume *lv)
+{
+ struct cmd_context *cmd = mirror_lv->vg->cmd;
+ struct str_list *sl;
+
+ /* Inherit tags - maybe needed for activation */
+ if (!str_list_match_list(&mirror_lv->tags, &lv->tags, NULL)) {
+ dm_list_iterate_items(sl, &mirror_lv->tags)
+ if (!str_list_add(cmd->mem, &lv->tags, sl->str)) {
+ log_error("Aborting. Unable to tag.");
+ return 0;
+ }
+
+ if (!vg_write(mirror_lv->vg) ||
+ !vg_commit(mirror_lv->vg)) {
+ log_error("Intermediate VG commit for orphan volume failed.");
+ return 0;
+ }
+ }
+
+ if (!activate_lv(cmd, lv))
+ return_0;
+
+ if (!deactivate_lv(cmd, lv))
+ return_0;
+
+ if (!lv_remove(lv))
+ return_0;
+
+ return 1;
+}
+
+static int _merge_mirror_images(struct logical_volume *lv,
+ const struct dm_list *mimages)
+{
+ uint32_t addition = dm_list_size(mimages);
+ struct logical_volume **img_lvs;
+ struct lv_list *lvl;
+ int i = 0;
+
+ if (!addition)
+ return 1;
+
+ if (!(img_lvs = alloca(sizeof(*img_lvs) * addition)))
+ return_0;
+
+ dm_list_iterate_items(lvl, mimages)
+ img_lvs[i++] = lvl->lv;
+
+ return lv_add_mirror_lvs(lv, img_lvs, addition,
+ MIRROR_IMAGE, first_seg(lv)->region_size);
+}
+
+/* Unlink the relationship between the segment and its log_lv */
+struct logical_volume *detach_mirror_log(struct lv_segment *mirrored_seg)
+{
+ struct logical_volume *log_lv;
+
+ if (!mirrored_seg->log_lv)
+ return NULL;
+
+ log_lv = mirrored_seg->log_lv;
+ mirrored_seg->log_lv = NULL;
+ lv_set_visible(log_lv);
+ log_lv->status &= ~MIRROR_LOG;
+ remove_seg_from_segs_using_this_lv(log_lv, mirrored_seg);
+
+ return log_lv;
+}
+
+/* Check if mirror image LV is removable with regard to given removable_pvs */
+int is_mirror_image_removable(struct logical_volume *mimage_lv, void *baton)
+{
+ struct physical_volume *pv;
+ struct lv_segment *seg;
+ int pv_found;
+ struct pv_list *pvl;
+ uint32_t s;
+ struct dm_list *removable_pvs = baton;
+
+ if (!baton || dm_list_empty(removable_pvs))
+ return 1;
+
+ dm_list_iterate_items(seg, &mimage_lv->segments) {
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) != AREA_PV) {
+ /* FIXME Recurse for AREA_LV? */
+ /* Structure of seg_lv is unknown.
+ * Not removing this LV for safety. */
+ return 0;
+ }
+
+ pv = seg_pv(seg, s);
+
+ pv_found = 0;
+ dm_list_iterate_items(pvl, removable_pvs) {
+ if (id_equal(&pv->id, &pvl->pv->id)) {
+ pv_found = 1;
+ break;
+ }
+ if (pvl->pv->dev && pv->dev &&
+ pv->dev->dev == pvl->pv->dev->dev) {
+ pv_found = 1;
+ break;
+ }
+ }
+ if (!pv_found)
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * _move_removable_mimages_to_end
+ *
+ * We always detach mimage LVs from the end of the areas array.
+ * This function will push 'count' mimages to the end of the array
+ * based on if their PVs are removable.
+ *
+ * This is an all or nothing function. Either the user specifies
+ * enough removable PVs to satisfy count, or they don't specify
+ * any removable_pvs at all (in which case all PVs in the mirror
+ * are considered removable).
+ */
+static int _move_removable_mimages_to_end(struct logical_volume *lv,
+ uint32_t count,
+ struct dm_list *removable_pvs)
+{
+ int i;
+ struct logical_volume *sub_lv;
+ struct lv_segment *mirrored_seg = first_seg(lv);
+
+ if (!removable_pvs)
+ return 1;
+
+ for (i = mirrored_seg->area_count - 1; (i >= 0) && count; i--) {
+ sub_lv = seg_lv(mirrored_seg, i);
+
+ if (!is_temporary_mirror_layer(sub_lv) &&
+ is_mirror_image_removable(sub_lv, removable_pvs)) {
+ if (!shift_mirror_images(mirrored_seg, i))
+ return_0;
+ count--;
+ }
+ }
+
+ return !count;
+}
+
+static int _mirrored_lv_in_sync(struct logical_volume *lv)
+{
+ percent_t sync_percent;
+
+ if (!lv_mirror_percent(lv->vg->cmd, lv, 0, &sync_percent,
+ NULL)) {
+ log_error("Unable to determine mirror sync status of %s/%s.",
+ lv->vg->name, lv->name);
+ return 0;
+ }
+
+ return (sync_percent == PERCENT_100) ? 1 : 0;
+}
+
+/*
+ * Split off 'split_count' legs from a mirror
+ *
+ * Returns: 0 on error, 1 on success
+ */
+static int _split_mirror_images(struct logical_volume *lv,
+ const char *split_name,
+ uint32_t split_count,
+ struct dm_list *removable_pvs)
+{
+ uint32_t i;
+ struct logical_volume *sub_lv = NULL;
+ struct logical_volume *new_lv = NULL;
+ struct logical_volume *detached_log_lv = NULL;
+ struct lv_segment *mirrored_seg = first_seg(lv);
+ struct dm_list split_images;
+ struct lv_list *lvl;
+
+ if (!(lv->status & MIRRORED)) {
+ log_error("Unable to split non-mirrored LV, %s",
+ lv->name);
+ return 0;
+ }
+
+ if (!split_count) {
+ log_error("split_count is zero!");
+ return 0;
+ }
+
+ log_verbose("Detaching %d images from mirror, %s",
+ split_count, lv->name);
+
+ if (!_move_removable_mimages_to_end(lv, split_count, removable_pvs)) {
+ /*
+ * FIXME: Allow incomplete specification of removable PVs?
+ *
+ * I am forcing the user to either specify no
+ * removable PVs or all of them. Should we allow
+ * them to just specify some - making us pick the rest?
+ */
+ log_error("Insufficient removable PVs given"
+ " to satisfy request");
+ return 0;
+ }
+
+ dm_list_init(&split_images);
+ for (i = 0; i < split_count; i++) {
+ mirrored_seg->area_count--;
+ sub_lv = seg_lv(mirrored_seg, mirrored_seg->area_count);
+
+ sub_lv->status &= ~MIRROR_IMAGE;
+ lv_set_visible(sub_lv);
+ release_lv_segment_area(mirrored_seg, mirrored_seg->area_count,
+ mirrored_seg->area_len);
+
+ log_very_verbose("%s assigned to be split", sub_lv->name);
+
+ if (!new_lv) {
+ new_lv = sub_lv;
+ new_lv->name = dm_pool_strdup(lv->vg->cmd->mem,
+ split_name);
+ if (!new_lv->name) {
+ log_error("Unable to rename newly split LV");
+ return 0;
+ }
+ } else {
+ lvl = dm_pool_alloc(lv->vg->cmd->mem, sizeof(*lvl));
+ if (!lvl) {
+ log_error("lv_list alloc failed");
+ return 0;
+ }
+ lvl->lv = sub_lv;
+ dm_list_add(&split_images, &lvl->list);
+ }
+ }
+
+ if (!dm_list_empty(&split_images)) {
+ size_t len = strlen(new_lv->name) + 32;
+ char *layer_name, format[len];
+
+ if (!insert_layer_for_lv(lv->vg->cmd, new_lv,
+ 0, "_mimage_%d")) {
+ log_error("Failed to build new mirror, %s",
+ new_lv->name);
+ return 0;
+ }
+
+ first_seg(new_lv)->region_size = mirrored_seg->region_size;
+
+ dm_list_iterate_items(lvl, &split_images) {
+ sub_lv = lvl->lv;
+
+ dm_snprintf(format, len, "%s_mimage_%%d",
+ new_lv->name);
+
+ layer_name = dm_pool_alloc(lv->vg->cmd->mem, len);
+ if (!layer_name) {
+ log_error("Unable to allocate memory");
+ return 0;
+ }
+ if (!generate_lv_name(lv->vg, format, layer_name, len)||
+ sscanf(layer_name, format, &i) != 1) {
+ log_error("Failed to generate new image names");
+ return 0;
+ }
+ sub_lv->name = layer_name;
+ }
+
+ if (!_merge_mirror_images(new_lv, &split_images)) {
+ log_error("Failed to group split "
+ "images into new mirror");
+ return 0;
+ }
+
+ /*
+ * We don't allow splitting a mirror that is not in-sync,
+ * so we can bring the newly split mirror up without a
+ * resync. (It will be a 'core' log mirror after all.)
+ */
+ init_mirror_in_sync(1);
+ }
+
+ sub_lv = NULL;
+
+ /*
+ * If no more mirrors, remove mirror layer.
+ * The sub_lv is removed entirely later - leaving
+ * only the top-level (now linear) LV.
+ */
+ if (mirrored_seg->area_count == 1) {
+ sub_lv = seg_lv(mirrored_seg, 0);
+ sub_lv->status &= ~MIRROR_IMAGE;
+ lv_set_visible(sub_lv);
+ detached_log_lv = detach_mirror_log(mirrored_seg);
+ if (!remove_layer_from_lv(lv, sub_lv))
+ return_0;
+ lv->status &= ~MIRRORED;
+ lv->status &= ~MIRROR_NOTSYNCED;
+ }
+
+ if (!vg_write(mirrored_seg->lv->vg)) {
+ log_error("Intermediate VG metadata write failed.");
+ return 0;
+ }
+
+ /*
+ * Suspend the original device and all its sub devices
+ */
+ if (!suspend_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv)) {
+ log_error("Failed to lock %s", mirrored_seg->lv->name);
+ vg_revert(mirrored_seg->lv->vg);
+ return 0;
+ }
+
+ if (!vg_commit(mirrored_seg->lv->vg)) {
+ resume_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv);
+ return 0;
+ }
+
+ /* Bring newly split-off LV into existence */
+ if (!activate_lv(lv->vg->cmd, new_lv)) {
+ log_error("Failed to activate newly split LV, %s",
+ new_lv->name);
+ return 0;
+ }
+
+ /* Resume altered original LV */
+ log_very_verbose("Updating \"%s\" in kernel", mirrored_seg->lv->name);
+ if (!resume_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv)) {
+ log_error("Problem reactivating %s", mirrored_seg->lv->name);
+ return 0;
+ }
+
+ if (sub_lv && !_delete_lv(lv, sub_lv))
+ return_0;
+
+ if (detached_log_lv && !_delete_lv(lv, detached_log_lv))
+ return_0;
+
+ log_very_verbose("%" PRIu32 " image(s) detached from %s",
+ split_count, lv->name);
+
+ return 1;
+}
+
+/*
+ * Remove num_removed images from mirrored_seg
+ *
+ * Arguments:
+ * num_removed: the requested (maximum) number of mirrors to be removed
+ * removable_pvs: if not NULL and list not empty, only mirrors using PVs
+ * in this list will be removed
+ * remove_log: if non-zero, log_lv will be removed
+ * (even if it's 0, log_lv will be removed if there is no
+ * mirror remaining after the removal)
+ * collapse: if non-zero, instead of removing, remove the temporary
+ * mirror layer and merge mirrors to the original LV.
+ * removable_pvs should be NULL and num_removed should be
+ * seg->area_count - 1.
+ * removed: if non NULL, the number of removed mirror images is set
+ * as a result
+ *
+ * If collapse is non-zero, <removed> is guaranteed to be equal to num_removed.
+ *
+ * Return values:
+ * Failure (0) means something unexpected has happend and
+ * the caller should abort.
+ * Even if no mirror was removed (e.g. no LV matches to 'removable_pvs'),
+ * returns success (1).
+ */
+static int _remove_mirror_images(struct logical_volume *lv,
+ uint32_t num_removed,
+ int (*is_removable)(struct logical_volume *, void *),
+ void *removable_baton,
+ unsigned remove_log, unsigned collapse,
+ uint32_t *removed)
+{
+ uint32_t m;
+ int32_t s;
+ struct logical_volume *sub_lv;
+ struct logical_volume *detached_log_lv = NULL;
+ struct logical_volume *temp_layer_lv = NULL;
+ struct lv_segment *mirrored_seg = first_seg(lv);
+ uint32_t old_area_count = mirrored_seg->area_count;
+ uint32_t new_area_count = mirrored_seg->area_count;
+ struct lv_list *lvl;
+ struct dm_list tmp_orphan_lvs;
+
+ if (removed)
+ *removed = 0;
+
+ log_very_verbose("Reducing mirror set from %" PRIu32 " to %"
+ PRIu32 " image(s)%s.",
+ old_area_count, old_area_count - num_removed,
+ remove_log ? " and no log volume" : "");
+
+ if (collapse && (old_area_count - num_removed != 1)) {
+ log_error("Incompatible parameters to _remove_mirror_images");
+ return 0;
+ }
+
+ /* Move removable_pvs to end of array */
+ for (s = mirrored_seg->area_count - 1;
+ s >= 0 && old_area_count - new_area_count < num_removed;
+ s--) {
+ sub_lv = seg_lv(mirrored_seg, s);
+ if (!is_temporary_mirror_layer(sub_lv) &&
+ is_removable(sub_lv, removable_baton)) {
+ /*
+ * Check if the user is trying to pull the
+ * primary mirror image when the mirror is
+ * not in-sync.
+ */
+ if ((s == 0) && !_mirrored_lv_in_sync(lv) &&
+ !(lv->status & PARTIAL_LV)) {
+ log_error("Unable to remove primary mirror image while mirror is not in-sync");
+ return_0;
+ }
+ if (!shift_mirror_images(mirrored_seg, s))
+ return_0;
+ new_area_count--;
+ }
+ }
+
+ /*
+ * If removable_pvs were specified, then they have been shifted
+ * to the end to ensure they are removed. The remaining balance
+ * of images left to remove will be taken from the unspecified.
+ */
+ new_area_count = old_area_count - num_removed;
+
+ if (num_removed && old_area_count == new_area_count)
+ return 1;
+
+ /* Remove mimage LVs from the segment */
+ dm_list_init(&tmp_orphan_lvs);
+ for (m = new_area_count; m < mirrored_seg->area_count; m++) {
+ seg_lv(mirrored_seg, m)->status &= ~MIRROR_IMAGE;
+ lv_set_visible(seg_lv(mirrored_seg, m));
+ if (!(lvl = dm_pool_alloc(lv->vg->cmd->mem, sizeof(*lvl)))) {
+ log_error("lv_list alloc failed");
+ return 0;
+ }
+ lvl->lv = seg_lv(mirrored_seg, m);
+ dm_list_add(&tmp_orphan_lvs, &lvl->list);
+ release_lv_segment_area(mirrored_seg, m, mirrored_seg->area_len);
+ }
+ mirrored_seg->area_count = new_area_count;
+
+ /* If no more mirrors, remove mirror layer */
+ /* As an exceptional case, if the lv is temporary layer,
+ * leave the LV as mirrored and let the lvconvert completion
+ * to remove the layer. */
+ if (new_area_count == 1 && !is_temporary_mirror_layer(lv)) {
+ temp_layer_lv = seg_lv(mirrored_seg, 0);
+ temp_layer_lv->status &= ~MIRROR_IMAGE;
+ lv_set_visible(temp_layer_lv);
+ detached_log_lv = detach_mirror_log(mirrored_seg);
+ if (!remove_layer_from_lv(lv, temp_layer_lv))
+ return_0;
+ lv->status &= ~MIRRORED;
+ lv->status &= ~MIRROR_NOTSYNCED;
+ if (collapse && !_merge_mirror_images(lv, &tmp_orphan_lvs)) {
+ log_error("Failed to add mirror images");
+ return 0;
+ }
+ mirrored_seg = first_seg(lv);
+ if (remove_log && !detached_log_lv)
+ detached_log_lv = detach_mirror_log(mirrored_seg);
+ } else if (new_area_count == 0) {
+ log_very_verbose("All mimages of %s are gone", lv->name);
+
+ /* All mirror images are gone.
+ * It can happen for vgreduce --removemissing. */
+ detached_log_lv = detach_mirror_log(mirrored_seg);
+ lv->status &= ~MIRRORED;
+ lv->status &= ~MIRROR_NOTSYNCED;
+ if (!replace_lv_with_error_segment(lv))
+ return_0;
+ } else if (remove_log)
+ detached_log_lv = detach_mirror_log(mirrored_seg);
+
+ /*
+ * The log may be removed due to repair. If the log
+ * happens to be a mirrored log, then there is a special
+ * case we need to consider. One of the images of a
+ * mirrored log can fail followed shortly afterwards by
+ * a failure of the second. This means that the top-level
+ * mirror is waiting for writes to the log to finish, but
+ * they never will unless the mirrored log can be repaired
+ * or replaced with an error target. Since both the devices
+ * have failed, we must replace with error target - it is
+ * the only way to release the pending writes.
+ */
+ if (detached_log_lv && lv_is_mirrored(detached_log_lv) &&
+ (detached_log_lv->status & PARTIAL_LV)) {
+ struct lv_segment *seg = first_seg(detached_log_lv);
+
+ log_very_verbose("%s being removed due to failures",
+ detached_log_lv->name);
+
+ /*
+ * We are going to replace the mirror with an
+ * error segment, but before we do, we must remember
+ * all of the LVs that must be deleted later (i.e.
+ * the sub-lv's)
+ */
+ for (m = 0; m < seg->area_count; m++) {
+ seg_lv(seg, m)->status &= ~MIRROR_IMAGE;
+ lv_set_visible(seg_lv(seg, m));
+ if (!(lvl = dm_pool_alloc(lv->vg->cmd->mem,
+ sizeof(*lvl)))) {
+ log_error("dm_pool_alloc failed");
+ return 0;
+ }
+ lvl->lv = seg_lv(seg, m);
+ dm_list_add(&tmp_orphan_lvs, &lvl->list);
+ }
+
+ if (!replace_lv_with_error_segment(detached_log_lv)) {
+ log_error("Failed error target substitution for %s",
+ detached_log_lv->name);
+ return 0;
+ }
+
+ if (!vg_write(detached_log_lv->vg)) {
+ log_error("intermediate VG write failed.");
+ return 0;
+ }
+
+ if (!suspend_lv(detached_log_lv->vg->cmd,
+ detached_log_lv)) {
+ log_error("Failed to suspend %s",
+ detached_log_lv->name);
+ return 0;
+ }
+
+ if (!vg_commit(detached_log_lv->vg)) {
+ if (!resume_lv(detached_log_lv->vg->cmd,
+ detached_log_lv))
+ stack;
+ return_0;
+ }
+
+ if (!resume_lv(detached_log_lv->vg->cmd, detached_log_lv)) {
+ log_error("Failed to resume %s",
+ detached_log_lv->name);
+ return_0;
+ }
+ }
+
+ /*
+ * To successfully remove these unwanted LVs we need to
+ * remove the LVs from the mirror set, commit that metadata
+ * then deactivate and remove them fully.
+ */
+
+ if (!vg_write(mirrored_seg->lv->vg)) {
+ log_error("intermediate VG write failed.");
+ return 0;
+ }
+
+ if (!suspend_lv_origin(mirrored_seg->lv->vg->cmd, mirrored_seg->lv)) {
+ log_error("Failed to lock %s", mirrored_seg->lv->name);
+ vg_revert(mirrored_seg->lv->vg);
+ return 0;
+ }
+
+ /* FIXME: second suspend should not be needed
+ * Explicitly suspend temporary LV
+ * This balance memlock_inc() calls with memlock_dec() in resume
+ * (both localy and in cluster) and also properly propagates precommited
+ * metadata into dm table on other nodes.
+ * (visible flag set causes the suspend is not properly propagated?)
+ */
+ if (temp_layer_lv && !suspend_lv(temp_layer_lv->vg->cmd, temp_layer_lv))
+ log_error("Problem suspending temporary LV %s", temp_layer_lv->name);
+
+ if (!vg_commit(mirrored_seg->lv->vg)) {
+ if (!resume_lv(mirrored_seg->lv->vg->cmd, mirrored_seg->lv))
+ stack;
+ return_0;
+ }
+
+ log_very_verbose("Updating \"%s\" in kernel", mirrored_seg->lv->name);
+
+ /*
+ * Avoid having same mirror target loaded twice simultaneously by first
+ * resuming the removed LV which now contains an error segment.
+ * As it's now detached from mirrored_seg->lv we must resume it
+ * explicitly.
+ */
+ if (temp_layer_lv && !resume_lv(temp_layer_lv->vg->cmd, temp_layer_lv)) {
+ log_error("Problem resuming temporary LV, %s", temp_layer_lv->name);
+ return 0;
+ }
+
+ if (!resume_lv_origin(mirrored_seg->lv->vg->cmd, mirrored_seg->lv)) {
+ log_error("Problem reactivating %s", mirrored_seg->lv->name);
+ return 0;
+ }
+
+ /* Save or delete the 'orphan' LVs */
+ if (!collapse) {
+ dm_list_iterate_items(lvl, &tmp_orphan_lvs)
+ if (!_delete_lv(lv, lvl->lv))
+ return_0;
+ }
+
+ if (temp_layer_lv && !_delete_lv(lv, temp_layer_lv))
+ return_0;
+
+ if (detached_log_lv && !_delete_lv(lv, detached_log_lv))
+ return_0;
+
+ /* Mirror with only 1 area is 'in sync'. */
+ if (new_area_count == 1 && is_temporary_mirror_layer(lv)) {
+ if (first_seg(lv)->log_lv &&
+ !_init_mirror_log(lv->vg->cmd, first_seg(lv)->log_lv,
+ 1, &lv->tags, 0)) {
+ /* As a result, unnecessary sync may run after
+ * collapsing. But safe.*/
+ log_error("Failed to initialize log device");
+ return_0;
+ }
+ }
+
+ if (removed)
+ *removed = old_area_count - new_area_count;
+
+ log_very_verbose("%" PRIu32 " image(s) removed from %s",
+ old_area_count - num_removed, lv->name);
+
+ return 1;
+}
+
+/*
+ * Remove the number of mirror images from the LV
+ */
+int remove_mirror_images(struct logical_volume *lv, uint32_t num_mirrors,
+ int (*is_removable)(struct logical_volume *, void *),
+ void *removable_baton, unsigned remove_log)
+{
+ uint32_t num_removed, removed_once, r;
+ uint32_t existing_mirrors = lv_mirror_count(lv);
+ struct logical_volume *next_lv = lv;
+
+ num_removed = existing_mirrors - num_mirrors;
+
+ /* num_removed can be 0 if the function is called just to remove log */
+ do {
+ if (num_removed < first_seg(next_lv)->area_count)
+ removed_once = num_removed;
+ else
+ removed_once = first_seg(next_lv)->area_count - 1;
+
+ if (!_remove_mirror_images(next_lv, removed_once,
+ is_removable, removable_baton,
+ remove_log, 0, &r))
+ return_0;
+
+ if (r < removed_once) {
+ /* Some mirrors are removed from the temporary mirror,
+ * but the temporary layer still exists.
+ * Down the stack and retry for remainder. */
+ next_lv = find_temporary_mirror(next_lv);
+ }
+
+ num_removed -= r;
+ } while (next_lv && num_removed);
+
+ if (num_removed) {
+ if (num_removed == existing_mirrors - num_mirrors)
+ log_error("No mirror images found using specified PVs.");
+ else {
+ log_error("%u images are removed out of requested %u.",
+ existing_mirrors - lv_mirror_count(lv),
+ existing_mirrors - num_mirrors);
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _no_removable_images(struct logical_volume *lv __attribute__((unused)),
+ void *baton __attribute__((unused))) {
+ return 0;
+}
+
+/*
+ * Collapsing temporary mirror layers.
+ *
+ * When mirrors are added to already-mirrored LV, a temporary mirror layer
+ * is inserted at the top of the stack to reduce resync work.
+ * The function will remove the intermediate layer and collapse the stack
+ * as far as mirrors are in-sync.
+ *
+ * The function is destructive: to remove intermediate mirror layers,
+ * VG metadata commits and suspend/resume are necessary.
+ */
+int collapse_mirrored_lv(struct logical_volume *lv)
+{
+ struct logical_volume *tmp_lv;
+ struct lv_segment *mirror_seg;
+
+ while ((tmp_lv = find_temporary_mirror(lv))) {
+ mirror_seg = find_mirror_seg(first_seg(tmp_lv));
+ if (!mirror_seg) {
+ log_error("Failed to find mirrored LV for %s",
+ tmp_lv->name);
+ return 0;
+ }
+
+ if (!_mirrored_lv_in_sync(mirror_seg->lv)) {
+ log_verbose("Not collapsing %s: out-of-sync",
+ mirror_seg->lv->name);
+ return 1;
+ }
+
+ if (!_remove_mirror_images(mirror_seg->lv,
+ mirror_seg->area_count - 1,
+ _no_removable_images, NULL, 0, 1, NULL)) {
+ log_error("Failed to release mirror images");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int get_mirror_fault_policy(struct cmd_context *cmd __attribute__((unused)),
+ int log_policy)
+{
+ const char *policy;
+
+ if (log_policy)
+ policy = find_config_str(NULL, "activation/mirror_log_fault_policy",
+ DEFAULT_MIRROR_LOG_FAULT_POLICY);
+ else {
+ policy = find_config_str(NULL, "activation/mirror_image_fault_policy",
+ NULL);
+ if (!policy)
+ policy = find_config_str(NULL, "activation/mirror_device_fault_policy",
+ DEFAULT_MIRROR_IMAGE_FAULT_POLICY);
+ }
+
+ if (!strcmp(policy, "remove"))
+ return MIRROR_REMOVE;
+ else if (!strcmp(policy, "allocate"))
+ return MIRROR_ALLOCATE;
+ else if (!strcmp(policy, "allocate_anywhere"))
+ return MIRROR_ALLOCATE_ANYWHERE;
+
+ if (log_policy)
+ log_error("Bad activation/mirror_log_fault_policy");
+ else
+ log_error("Bad activation/mirror_device_fault_policy");
+
+ return MIRROR_REMOVE;
+}
+
+static int get_mirror_log_fault_policy(struct cmd_context *cmd)
+{
+ return get_mirror_fault_policy(cmd, 1);
+}
+
+static int get_mirror_device_fault_policy(struct cmd_context *cmd)
+{
+ return get_mirror_fault_policy(cmd, 0);
+}
+
+/*
+ * replace_mirror_images
+ * @mirrored_seg: segment (which may be linear now) to restore
+ * @num_mirrors: number of copies we should end up with
+ * @replace_log: replace log if not present
+ * @in_sync: was the original mirror in-sync?
+ *
+ * in_sync will be set to 0 if new mirror devices are being added
+ * In other words, it is only useful if the log (and only the log)
+ * is being restored.
+ *
+ * Returns: 0 on failure, 1 on reconfig, -1 if no reconfig done
+ */
+static int replace_mirror_images(struct lv_segment *mirrored_seg,
+ uint32_t num_mirrors,
+ int log_policy, int in_sync)
+{
+ int r = -1;
+ struct logical_volume *lv = mirrored_seg->lv;
+
+ /* FIXME: Use lvconvert rather than duplicating its code */
+
+ if (mirrored_seg->area_count < num_mirrors) {
+ log_warn("WARNING: Failed to replace mirror device in %s/%s",
+ mirrored_seg->lv->vg->name, mirrored_seg->lv->name);
+
+ if ((mirrored_seg->area_count > 1) && !mirrored_seg->log_lv)
+ log_warn("WARNING: Use 'lvconvert -m %d %s/%s --corelog' to replace failed devices",
+ num_mirrors - 1, lv->vg->name, lv->name);
+ else
+ log_warn("WARNING: Use 'lvconvert -m %d %s/%s' to replace failed devices",
+ num_mirrors - 1, lv->vg->name, lv->name);
+ r = 0;
+
+ /* REMEMBER/FIXME: set in_sync to 0 if a new mirror device was added */
+ in_sync = 0;
+ }
+
+ /*
+ * FIXME: right now, we ignore the allocation policy specified to
+ * allocate the new log.
+ */
+ if ((mirrored_seg->area_count > 1) && !mirrored_seg->log_lv &&
+ (log_policy != MIRROR_REMOVE)) {
+ log_warn("WARNING: Failed to replace mirror log device in %s/%s",
+ lv->vg->name, lv->name);
+
+ log_warn("WARNING: Use 'lvconvert -m %d %s/%s' to replace failed devices",
+ mirrored_seg->area_count - 1 , lv->vg->name, lv->name);
+ r = 0;
+ }
+
+ return r;
+}
+
+int reconfigure_mirror_images(struct lv_segment *mirrored_seg, uint32_t num_mirrors,
+ struct dm_list *removable_pvs, unsigned remove_log)
+{
+ int r;
+ int in_sync;
+ int log_policy, dev_policy;
+ uint32_t old_num_mirrors = mirrored_seg->area_count;
+ int had_log = (mirrored_seg->log_lv) ? 1 : 0;
+
+ /* was the mirror in-sync before problems? */
+ in_sync = _mirrored_lv_in_sync(mirrored_seg->lv);
+
+ /*
+ * While we are only removing devices, we can have sync set.
+ * Setting this is only useful if we are moving to core log
+ * otherwise the disk log will contain the sync information
+ */
+ init_mirror_in_sync(in_sync);
+
+ r = _remove_mirror_images(mirrored_seg->lv, old_num_mirrors - num_mirrors,
+ is_mirror_image_removable, removable_pvs,
+ remove_log, 0, NULL);
+ if (!r)
+ /* Unable to remove bad devices */
+ return 0;
+
+ log_warn("WARNING: Bad device removed from mirror volume, %s/%s",
+ mirrored_seg->lv->vg->name, mirrored_seg->lv->name);
+
+ log_policy = get_mirror_log_fault_policy(mirrored_seg->lv->vg->cmd);
+ dev_policy = get_mirror_device_fault_policy(mirrored_seg->lv->vg->cmd);
+
+ r = replace_mirror_images(mirrored_seg,
+ (dev_policy != MIRROR_REMOVE) ?
+ old_num_mirrors : num_mirrors,
+ log_policy, in_sync);
+
+ if (!r)
+ /* Failed to replace device(s) */
+ log_warn("WARNING: Unable to find substitute device for mirror volume, %s/%s",
+ mirrored_seg->lv->vg->name, mirrored_seg->lv->name);
+ else if (r > 0)
+ /* Success in replacing device(s) */
+ log_warn("WARNING: Mirror volume, %s/%s restored - substitute for failed device found.",
+ mirrored_seg->lv->vg->name, mirrored_seg->lv->name);
+ else
+ /* Bad device removed, but not replaced because of policy */
+ if (mirrored_seg->area_count == 1) {
+ log_warn("WARNING: Mirror volume, %s/%s converted to linear due to device failure.",
+ mirrored_seg->lv->vg->name, mirrored_seg->lv->name);
+ } else if (had_log && !mirrored_seg->log_lv) {
+ log_warn("WARNING: Mirror volume, %s/%s disk log removed due to device failure.",
+ mirrored_seg->lv->vg->name, mirrored_seg->lv->name);
+ }
+ /*
+ * If we made it here, we at least removed the bad device.
+ * Consider this success.
+ */
+ return 1;
+}
+
+static int _create_mimage_lvs(struct alloc_handle *ah,
+ uint32_t num_mirrors,
+ uint32_t stripes,
+ uint32_t stripe_size,
+ struct logical_volume *lv,
+ struct logical_volume **img_lvs,
+ int log)
+{
+ uint32_t m;
+ char *img_name;
+ size_t len;
+
+ len = strlen(lv->name) + 32;
+ if (!(img_name = alloca(len))) {
+ log_error("img_name allocation failed. "
+ "Remove new LV and retry.");
+ return 0;
+ }
+
+ if (dm_snprintf(img_name, len, "%s_mimage_%%d", lv->name) < 0) {
+ log_error("img_name allocation failed. "
+ "Remove new LV and retry.");
+ return 0;
+ }
+
+ for (m = 0; m < num_mirrors; m++) {
+ if (!(img_lvs[m] = lv_create_empty(img_name,
+ NULL, LVM_READ | LVM_WRITE,
+ ALLOC_INHERIT, lv->vg))) {
+ log_error("Aborting. Failed to create mirror image LV. "
+ "Remove new LV and retry.");
+ return 0;
+ }
+
+ if (log) {
+ if (!lv_add_log_segment(ah, m * stripes + 1, img_lvs[m], 0)) {
+ log_error("Aborting. Failed to add mirror image segment "
+ "to %s. Remove new LV and retry.",
+ img_lvs[m]->name);
+ return 0;
+ }
+ } else {
+ if (!lv_add_segment(ah, m * stripes, stripes, img_lvs[m],
+ get_segtype_from_string(lv->vg->cmd,
+ "striped"),
+ stripe_size, 0, 0)) {
+ log_error("Aborting. Failed to add mirror image segment "
+ "to %s. Remove new LV and retry.",
+ img_lvs[m]->name);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Remove mirrors from each segment.
+ * 'new_mirrors' is the number of mirrors after the removal. '0' for linear.
+ * If 'status_mask' is non-zero, the removal happens only when all segments
+ * has the status bits on.
+ */
+int remove_mirrors_from_segments(struct logical_volume *lv,
+ uint32_t new_mirrors, uint64_t status_mask)
+{
+ struct lv_segment *seg;
+ uint32_t s;
+
+ /* Check the segment params are compatible */
+ dm_list_iterate_items(seg, &lv->segments) {
+ if (!seg_is_mirrored(seg)) {
+ log_error("Segment is not mirrored: %s:%" PRIu32,
+ lv->name, seg->le);
+ return 0;
+ } if ((seg->status & status_mask) != status_mask) {
+ log_error("Segment status does not match: %s:%" PRIu32
+ " status:0x%" PRIx64 "/0x%" PRIx64, lv->name, seg->le,
+ seg->status, status_mask);
+ return 0;
+ }
+ }
+
+ /* Convert the segments */
+ dm_list_iterate_items(seg, &lv->segments) {
+ if (!new_mirrors && seg->extents_copied == seg->area_len) {
+ if (!move_lv_segment_area(seg, 0, seg, 1))
+ return_0;
+ }
+
+ for (s = new_mirrors + 1; s < seg->area_count; s++)
+ release_lv_segment_area(seg, s, seg->area_len);
+
+ seg->area_count = new_mirrors + 1;
+
+ if (!new_mirrors)
+ seg->segtype = get_segtype_from_string(lv->vg->cmd,
+ "striped");
+ }
+
+ return 1;
+}
+
+const char *get_pvmove_pvname_from_lv_mirr(struct logical_volume *lv_mirr)
+{
+ struct lv_segment *seg;
+
+ dm_list_iterate_items(seg, &lv_mirr->segments) {
+ if (!seg_is_mirrored(seg))
+ continue;
+ if (seg_type(seg, 0) != AREA_PV)
+ continue;
+ return dev_name(seg_dev(seg, 0));
+ }
+
+ return NULL;
+}
+
+const char *get_pvmove_pvname_from_lv(struct logical_volume *lv)
+{
+ struct lv_segment *seg;
+ uint32_t s;
+
+ dm_list_iterate_items(seg, &lv->segments) {
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) != AREA_LV)
+ continue;
+ return get_pvmove_pvname_from_lv_mirr(seg_lv(seg, s));
+ }
+ }
+
+ return NULL;
+}
+
+struct logical_volume *find_pvmove_lv(struct volume_group *vg,
+ struct device *dev,
+ uint32_t lv_type)
+{
+ struct lv_list *lvl;
+ struct logical_volume *lv;
+ struct lv_segment *seg;
+
+ /* Loop through all LVs */
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ lv = lvl->lv;
+
+ if (!(lv->status & lv_type))
+ continue;
+
+ /* Check segment origins point to pvname */
+ dm_list_iterate_items(seg, &lv->segments) {
+ if (seg_type(seg, 0) != AREA_PV)
+ continue;
+ if (seg_dev(seg, 0) != dev)
+ continue;
+ return lv;
+ }
+ }
+
+ return NULL;
+}
+
+struct logical_volume *find_pvmove_lv_from_pvname(struct cmd_context *cmd,
+ struct volume_group *vg,
+ const char *name,
+ const char *uuid __attribute__((unused)),
+ uint32_t lv_type)
+{
+ struct physical_volume *pv;
+
+ if (!(pv = find_pv_by_name(cmd, name)))
+ return_NULL;
+
+ return find_pvmove_lv(vg, pv->dev, lv_type);
+}
+
+struct dm_list *lvs_using_lv(struct cmd_context *cmd, struct volume_group *vg,
+ struct logical_volume *lv)
+{
+ struct dm_list *lvs;
+ struct logical_volume *lv1;
+ struct lv_list *lvl, *lvl1;
+ struct lv_segment *seg;
+ uint32_t s;
+
+ if (!(lvs = dm_pool_alloc(cmd->mem, sizeof(*lvs)))) {
+ log_error("lvs list alloc failed");
+ return NULL;
+ }
+
+ dm_list_init(lvs);
+
+ /* Loop through all LVs except the one supplied */
+ dm_list_iterate_items(lvl1, &vg->lvs) {
+ lv1 = lvl1->lv;
+ if (lv1 == lv)
+ continue;
+
+ /* Find whether any segment points at the supplied LV */
+ dm_list_iterate_items(seg, &lv1->segments) {
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) != AREA_LV ||
+ seg_lv(seg, s) != lv)
+ continue;
+ if (!(lvl = dm_pool_alloc(cmd->mem, sizeof(*lvl)))) {
+ log_error("lv_list alloc failed");
+ return NULL;
+ }
+ lvl->lv = lv1;
+ dm_list_add(lvs, &lvl->list);
+ goto next_lv;
+ }
+ }
+ next_lv:
+ ;
+ }
+
+ return lvs;
+}
+
+percent_t copy_percent(struct logical_volume *lv_mirr)
+{
+ uint32_t numerator = 0u, denominator = 0u;
+ struct lv_segment *seg;
+
+ dm_list_iterate_items(seg, &lv_mirr->segments) {
+ denominator += seg->area_len;
+
+ if (seg_is_mirrored(seg) && seg->area_count > 1)
+ numerator += seg->extents_copied;
+ else
+ numerator += seg->area_len;
+ }
+
+ return denominator ? make_percent( numerator, denominator ) : 100.0;
+}
+
+/*
+ * Fixup mirror pointers after single-pass segment import
+ */
+int fixup_imported_mirrors(struct volume_group *vg)
+{
+ struct lv_list *lvl;
+ struct lv_segment *seg;
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ dm_list_iterate_items(seg, &lvl->lv->segments) {
+ if (seg->segtype !=
+ get_segtype_from_string(vg->cmd, "mirror"))
+ continue;
+
+ if (seg->log_lv && !add_seg_to_segs_using_this_lv(seg->log_lv, seg))
+ return_0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Add mirrors to "linear" or "mirror" segments
+ */
+int add_mirrors_to_segments(struct cmd_context *cmd, struct logical_volume *lv,
+ uint32_t mirrors, uint32_t region_size,
+ struct dm_list *allocatable_pvs, alloc_policy_t alloc)
+{
+ struct alloc_handle *ah;
+ const struct segment_type *segtype;
+ struct dm_list *parallel_areas;
+ uint32_t adjusted_region_size;
+ int r = 1;
+
+ if (!(parallel_areas = build_parallel_areas_from_lv(cmd, lv, 1)))
+ return_0;
+
+ if (!(segtype = get_segtype_from_string(cmd, "mirror")))
+ return_0;
+
+ adjusted_region_size = adjusted_mirror_region_size(lv->vg->extent_size,
+ lv->le_count,
+ region_size);
+
+ if (!(ah = allocate_extents(lv->vg, NULL, segtype, 1, mirrors, 0, 0,
+ lv->le_count, allocatable_pvs, alloc,
+ parallel_areas))) {
+ log_error("Unable to allocate mirror extents for %s.", lv->name);
+ return 0;
+ }
+
+ if (!lv_add_mirror_areas(ah, lv, 0, adjusted_region_size)) {
+ log_error("Failed to add mirror areas to %s", lv->name);
+ r = 0;
+ }
+
+ alloc_destroy(ah);
+ return r;
+}
+
+/*
+ * Convert mirror log
+ *
+ * FIXME: Can't handle segment-by-segment mirror (like pvmove)
+ */
+int remove_mirror_log(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ struct dm_list *removable_pvs,
+ int force)
+{
+ percent_t sync_percent;
+ struct lvinfo info;
+ struct volume_group *vg = lv->vg;
+
+ /* Unimplemented features */
+ if (dm_list_size(&lv->segments) != 1) {
+ log_error("Multiple-segment mirror is not supported");
+ return 0;
+ }
+
+ /* Had disk log, switch to core. */
+ if (lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) {
+ if (!lv_mirror_percent(cmd, lv, 0, &sync_percent,
+ NULL)) {
+ log_error("Unable to determine mirror sync status.");
+ return 0;
+ }
+ } else if (vg_is_clustered(vg)) {
+ log_error("Unable to convert the log of an inactive "
+ "cluster mirror, %s", lv->name);
+ return 0;
+ } else if (force || yes_no_prompt("Full resync required to convert "
+ "inactive mirror %s to core log. "
+ "Proceed? [y/n]: ", lv->name) == 'y')
+ sync_percent = 0;
+ else
+ return 0;
+
+ if (sync_percent == PERCENT_100)
+ init_mirror_in_sync(1);
+ else {
+ /* A full resync will take place */
+ lv->status &= ~MIRROR_NOTSYNCED;
+ init_mirror_in_sync(0);
+ }
+
+ if (!remove_mirror_images(lv, lv_mirror_count(lv),
+ is_mirror_image_removable, removable_pvs, 1U))
+ return_0;
+
+ return 1;
+}
+
+static struct logical_volume *_create_mirror_log(struct logical_volume *lv,
+ struct alloc_handle *ah,
+ alloc_policy_t alloc,
+ const char *lv_name,
+ const char *suffix)
+{
+ struct logical_volume *log_lv;
+ char *log_name;
+ size_t len;
+
+ len = strlen(lv_name) + 32;
+ if (!(log_name = alloca(len))) {
+ log_error("log_name allocation failed.");
+ return NULL;
+ }
+
+ if (dm_snprintf(log_name, len, "%s%s", lv_name, suffix) < 0) {
+ log_error("log_name allocation failed.");
+ return NULL;
+ }
+
+ if (!(log_lv = lv_create_empty(log_name, NULL,
+ VISIBLE_LV | LVM_READ | LVM_WRITE,
+ alloc, lv->vg)))
+ return_NULL;
+
+ if (!lv_add_log_segment(ah, 0, log_lv, MIRROR_LOG))
+ return_NULL;
+
+ return log_lv;
+}
+
+/*
+ * Returns: 1 on success, 0 on error
+ */
+static int _form_mirror(struct cmd_context *cmd, struct alloc_handle *ah,
+ struct logical_volume *lv,
+ uint32_t mirrors, uint32_t stripes,
+ uint32_t stripe_size, uint32_t region_size, int log)
+{
+ struct logical_volume **img_lvs;
+
+ /*
+ * insert a mirror layer
+ */
+ if (dm_list_size(&lv->segments) != 1 ||
+ seg_type(first_seg(lv), 0) != AREA_LV)
+ if (!insert_layer_for_lv(cmd, lv, 0, "_mimage_%d"))
+ return 0;
+
+ /*
+ * create mirror image LVs
+ */
+ if (!(img_lvs = alloca(sizeof(*img_lvs) * mirrors))) {
+ log_error("img_lvs allocation failed. "
+ "Remove new LV and retry.");
+ return 0;
+ }
+
+ if (!_create_mimage_lvs(ah, mirrors, stripes, stripe_size, lv, img_lvs, log))
+ return 0;
+
+ if (!lv_add_mirror_lvs(lv, img_lvs, mirrors,
+ MIRROR_IMAGE | (lv->status & LOCKED),
+ region_size)) {
+ log_error("Aborting. Failed to add mirror segment. "
+ "Remove new LV and retry.");
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct logical_volume *_set_up_mirror_log(struct cmd_context *cmd,
+ struct alloc_handle *ah,
+ struct logical_volume *lv,
+ uint32_t log_count,
+ uint32_t region_size,
+ alloc_policy_t alloc,
+ int in_sync)
+{
+ struct logical_volume *log_lv;
+ const char *suffix, *c;
+ char *lv_name;
+ size_t len;
+ struct lv_segment *seg;
+
+ init_mirror_in_sync(in_sync);
+
+ /* Mirror log name is lv_name + suffix, determined as the following:
+ * 1. suffix is:
+ * o "_mlog" for the original mirror LV.
+ * o "_mlogtmp_%d" for temporary mirror LV,
+ * 2. lv_name is:
+ * o lv->name, if the log is temporary
+ * o otherwise, the top-level LV name
+ */
+ seg = first_seg(lv);
+ if (seg_type(seg, 0) == AREA_LV &&
+ strstr(seg_lv(seg, 0)->name, MIRROR_SYNC_LAYER)) {
+ lv_name = lv->name;
+ suffix = "_mlogtmp_%d";
+ } else if ((c = strstr(lv->name, MIRROR_SYNC_LAYER))) {
+ len = c - lv->name + 1;
+ if (!(lv_name = alloca(len)) ||
+ !dm_snprintf(lv_name, len, "%s", lv->name)) {
+ log_error("mirror log name allocation failed");
+ return 0;
+ }
+ suffix = "_mlog";
+ } else {
+ lv_name = lv->name;
+ suffix = "_mlog";
+ }
+
+ if (!(log_lv = _create_mirror_log(lv, ah, alloc,
+ (const char *) lv_name, suffix))) {
+ log_error("Failed to create mirror log.");
+ return NULL;
+ }
+
+ if ((log_count > 1) &&
+ !_form_mirror(cmd, ah, log_lv, log_count-1, 1, 0, region_size, 1)) {
+ log_error("Failed to form mirrored log.");
+ return NULL;
+ }
+
+ if (!_init_mirror_log(cmd, log_lv, in_sync, &lv->tags, 1)) {
+ log_error("Failed to initialise mirror log.");
+ return NULL;
+ }
+
+ return log_lv;
+}
+
+int attach_mirror_log(struct lv_segment *seg, struct logical_volume *log_lv)
+{
+ seg->log_lv = log_lv;
+ log_lv->status |= MIRROR_LOG;
+ lv_set_hidden(log_lv);
+ return add_seg_to_segs_using_this_lv(log_lv, seg);
+}
+
+int add_mirror_log(struct cmd_context *cmd, struct logical_volume *lv,
+ uint32_t log_count, uint32_t region_size,
+ struct dm_list *allocatable_pvs, alloc_policy_t alloc)
+{
+ struct alloc_handle *ah;
+ const struct segment_type *segtype;
+ struct dm_list *parallel_areas;
+ percent_t sync_percent;
+ int in_sync;
+ struct logical_volume *log_lv;
+ struct lvinfo info;
+ int r = 0;
+
+ if (dm_list_size(&lv->segments) != 1) {
+ log_error("Multiple-segment mirror is not supported");
+ return 0;
+ }
+
+ /*
+ * We are unable to convert the log of inactive cluster mirrors
+ * due to the inability to detect whether the mirror is active
+ * on remote nodes (even though it is inactive on this node)
+ */
+ if (vg_is_clustered(lv->vg) &&
+ !(lv_info(cmd, lv, 0, &info, 0, 0) && info.exists)) {
+ log_error("Unable to convert the log of inactive "
+ "cluster mirror %s", lv->name);
+ return 0;
+ }
+
+ if (!(parallel_areas = build_parallel_areas_from_lv(cmd, lv, 0)))
+ return_0;
+
+ if (!(segtype = get_segtype_from_string(cmd, "mirror")))
+ return_0;
+
+ if (activation() && segtype->ops->target_present &&
+ !segtype->ops->target_present(cmd, NULL, NULL)) {
+ log_error("%s: Required device-mapper target(s) not "
+ "detected in your kernel", segtype->name);
+ return 0;
+ }
+
+ /* allocate destination extents */
+ ah = allocate_extents(lv->vg, NULL, segtype,
+ 0, 0, log_count, region_size, 0,
+ allocatable_pvs, alloc, parallel_areas);
+ if (!ah) {
+ log_error("Unable to allocate extents for mirror log.");
+ return 0;
+ }
+
+ /* check sync status */
+ if (lv_mirror_percent(cmd, lv, 0, &sync_percent, NULL) &&
+ (sync_percent == PERCENT_100))
+ in_sync = 1;
+ else
+ in_sync = 0;
+
+ if (!(log_lv = _set_up_mirror_log(cmd, ah, lv, log_count,
+ region_size, alloc, in_sync)))
+ goto_out;
+
+ if (!attach_mirror_log(first_seg(lv), log_lv))
+ goto_out;
+
+ r = 1;
+out:
+ alloc_destroy(ah);
+ return r;
+}
+
+/*
+ * Convert "linear" LV to "mirror".
+ */
+int add_mirror_images(struct cmd_context *cmd, struct logical_volume *lv,
+ uint32_t mirrors, uint32_t stripes,
+ uint32_t stripe_size, uint32_t region_size,
+ struct dm_list *allocatable_pvs, alloc_policy_t alloc,
+ uint32_t log_count)
+{
+ struct alloc_handle *ah;
+ const struct segment_type *segtype;
+ struct dm_list *parallel_areas;
+ struct logical_volume *log_lv = NULL;
+
+ /*
+ * allocate destination extents
+ */
+
+ if (!(parallel_areas = build_parallel_areas_from_lv(cmd, lv, 0)))
+ return_0;
+
+ if (!(segtype = get_segtype_from_string(cmd, "mirror")))
+ return_0;
+
+ ah = allocate_extents(lv->vg, NULL, segtype,
+ stripes, mirrors, log_count, region_size, lv->le_count,
+ allocatable_pvs, alloc, parallel_areas);
+ if (!ah) {
+ log_error("Unable to allocate extents for mirror(s).");
+ return 0;
+ }
+
+ /*
+ * create and initialize mirror log
+ */
+ if (log_count &&
+ !(log_lv = _set_up_mirror_log(cmd, ah, lv, log_count,
+ (region_size > lv->vg->extent_size) ?
+ lv->vg->extent_size : region_size,
+ alloc, mirror_in_sync()))) {
+ stack;
+ goto out_remove_images;
+ }
+
+ /* The log initialization involves vg metadata commit.
+ So from here on, if failure occurs, the log must be explicitly
+ removed and the updated vg metadata should be committed. */
+
+ if (!_form_mirror(cmd, ah, lv, mirrors, stripes, stripe_size, region_size, 0))
+ goto out_remove_log;
+
+ if (log_count && !attach_mirror_log(first_seg(lv), log_lv))
+ stack;
+
+ alloc_destroy(ah);
+ return 1;
+
+ out_remove_log:
+ if (log_lv) {
+ if (!lv_remove(log_lv) ||
+ !vg_write(log_lv->vg) ||
+ !vg_commit(log_lv->vg))
+ log_error("Manual intervention may be required to remove "
+ "abandoned log LV before retrying.");
+ else
+ backup(log_lv->vg);
+ }
+ out_remove_images:
+ alloc_destroy(ah);
+ return 0;
+}
+
+/*
+ * Generic interface for adding mirror and/or mirror log.
+ * 'mirror' is the number of mirrors to be added.
+ * 'pvs' is either allocatable pvs.
+ */
+int lv_add_mirrors(struct cmd_context *cmd, struct logical_volume *lv,
+ uint32_t mirrors, uint32_t stripes, uint32_t stripe_size,
+ uint32_t region_size, uint32_t log_count,
+ struct dm_list *pvs, alloc_policy_t alloc, uint32_t flags)
+{
+ if (!mirrors && !log_count) {
+ log_error("No conversion is requested");
+ return 0;
+ }
+
+ if (vg_is_clustered(lv->vg)) {
+ if (!(lv->status & ACTIVATE_EXCL) &&
+ !cluster_mirror_is_available(lv)) {
+ log_error("Shared cluster mirrors are not available.");
+ return 0;
+ }
+
+ /*
+ * No mirrored logs for cluster mirrors until
+ * log daemon is multi-threaded.
+ */
+ if (log_count > 1) {
+ log_error("Log type, \"mirrored\", is unavailable to cluster mirrors");
+ return 0;
+ }
+ }
+
+ /* For corelog mirror, activation code depends on
+ * the global mirror_in_sync status. As we are adding
+ * a new mirror, it should be set as 'out-of-sync'
+ * so that the sync starts. */
+ /* However, MIRROR_SKIP_INIT_SYNC even overrides it. */
+ if (flags & MIRROR_SKIP_INIT_SYNC)
+ init_mirror_in_sync(1);
+ else if (!log_count)
+ init_mirror_in_sync(0);
+
+ if (flags & MIRROR_BY_SEG) {
+ if (log_count) {
+ log_error("Persistent log is not supported on "
+ "segment-by-segment mirroring");
+ return 0;
+ }
+ if (stripes > 1) {
+ log_error("Striped-mirroring is not supported on "
+ "segment-by-segment mirroring");
+ return 0;
+ }
+
+ return add_mirrors_to_segments(cmd, lv, mirrors,
+ region_size, pvs, alloc);
+ } else if (flags & MIRROR_BY_LV) {
+ if (!mirrors)
+ return add_mirror_log(cmd, lv, log_count,
+ region_size, pvs, alloc);
+ return add_mirror_images(cmd, lv, mirrors,
+ stripes, stripe_size, region_size,
+ pvs, alloc, log_count);
+ }
+
+ log_error("Unsupported mirror conversion type");
+ return 0;
+}
+
+int lv_split_mirror_images(struct logical_volume *lv, const char *split_name,
+ uint32_t split_count, struct dm_list *removable_pvs)
+{
+ int r;
+
+ if (find_lv_in_vg(lv->vg, split_name)) {
+ log_error("Logical Volume \"%s\" already exists in "
+ "volume group \"%s\"", split_name, lv->vg->name);
+ return 0;
+ }
+
+ /* Can't split a mirror that is not in-sync... unless force? */
+ if (!_mirrored_lv_in_sync(lv)) {
+ log_error("Unable to split mirror that is not in-sync.");
+ return_0;
+ }
+
+ /*
+ * FIXME: Generate default name when not supplied.
+ *
+ * If we were going to generate a default name, we would
+ * do it here. Better to wait for a decision on the form
+ * of the default name when '--track_deltas' (the ability
+ * to merge a split leg back in and only copy the changes)
+ * is being implemented. For now, we force the user to
+ * come up with a name for their LV.
+ */
+ r = _split_mirror_images(lv, split_name, split_count, removable_pvs);
+ if (!r)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Generic interface for removing mirror and/or mirror log.
+ * 'mirror' is the number of mirrors to be removed.
+ * 'pvs' is removable pvs.
+ */
+int lv_remove_mirrors(struct cmd_context *cmd __attribute__((unused)),
+ struct logical_volume *lv,
+ uint32_t mirrors, uint32_t log_count,
+ int (*is_removable)(struct logical_volume *, void *),
+ void *removable_baton,
+ uint64_t status_mask)
+{
+ uint32_t new_mirrors;
+ struct lv_segment *seg;
+
+ if (!mirrors && !log_count) {
+ log_error("No conversion is requested");
+ return 0;
+ }
+
+ seg = first_seg(lv);
+ if (!seg_is_mirrored(seg)) {
+ log_error("Not a mirror segment");
+ return 0;
+ }
+
+ if (lv_mirror_count(lv) <= mirrors) {
+ log_error("Removing more than existing: %d <= %d",
+ seg->area_count, mirrors);
+ return 0;
+ }
+ new_mirrors = lv_mirror_count(lv) - mirrors - 1;
+
+ /* MIRROR_BY_LV */
+ if (seg_type(seg, 0) == AREA_LV &&
+ seg_lv(seg, 0)->status & MIRROR_IMAGE)
+ return remove_mirror_images(lv, new_mirrors + 1,
+ is_removable, removable_baton,
+ log_count ? 1U : 0);
+
+ /* MIRROR_BY_SEG */
+ if (log_count) {
+ log_error("Persistent log is not supported on "
+ "segment-by-segment mirroring");
+ return 0;
+ }
+ return remove_mirrors_from_segments(lv, new_mirrors, status_mask);
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "lvmcache.h"
+
+/*
+ * FIXME: Check for valid handle before dereferencing field or log error?
+ */
+#define pv_field(handle, field) ((handle)->field)
+
+char *pv_fmt_dup(const struct physical_volume *pv)
+{
+ if (!pv->fmt)
+ return NULL;
+ return dm_pool_strdup(pv->vg->vgmem, pv->fmt->name);
+}
+
+char *pv_name_dup(const struct physical_volume *pv)
+{
+ return dm_pool_strdup(pv->vg->vgmem, dev_name(pv->dev));
+}
+
+/*
+ * Gets/Sets for external LVM library
+ */
+struct id pv_id(const struct physical_volume *pv)
+{
+ return pv_field(pv, id);
+}
+
+char *pv_uuid_dup(const struct physical_volume *pv)
+{
+ return id_format_and_copy(pv->vg->vgmem, &pv->id);
+}
+
+char *pv_tags_dup(const struct physical_volume *pv)
+{
+ return tags_format_and_copy(pv->vg->vgmem, &pv->tags);
+}
+
+const struct format_type *pv_format_type(const struct physical_volume *pv)
+{
+ return pv_field(pv, fmt);
+}
+
+struct id pv_vgid(const struct physical_volume *pv)
+{
+ return pv_field(pv, vgid);
+}
+
+struct device *pv_dev(const struct physical_volume *pv)
+{
+ return pv_field(pv, dev);
+}
+
+const char *pv_vg_name(const struct physical_volume *pv)
+{
+ return pv_field(pv, vg_name);
+}
+
+const char *pv_dev_name(const struct physical_volume *pv)
+{
+ return dev_name(pv_dev(pv));
+}
+
+uint64_t pv_size(const struct physical_volume *pv)
+{
+ return pv_field(pv, size);
+}
+
+uint64_t pv_dev_size(const struct physical_volume *pv)
+{
+ uint64_t size;
+
+ if (!dev_get_size(pv->dev, &size))
+ size = 0;
+ return size;
+}
+
+uint64_t pv_size_field(const struct physical_volume *pv)
+{
+ uint64_t size;
+
+ if (!pv->pe_count)
+ size = pv->size;
+ else
+ size = (uint64_t) pv->pe_count * pv->pe_size;
+ return size;
+}
+
+uint64_t pv_free(const struct physical_volume *pv)
+{
+ uint64_t freespace;
+
+ if (!pv->pe_count)
+ freespace = pv->size;
+ else
+ freespace = (uint64_t)
+ (pv->pe_count - pv->pe_alloc_count) * pv->pe_size;
+ return freespace;
+}
+
+uint64_t pv_status(const struct physical_volume *pv)
+{
+ return pv_field(pv, status);
+}
+
+uint32_t pv_pe_size(const struct physical_volume *pv)
+{
+ return pv_field(pv, pe_size);
+}
+
+uint64_t pv_pe_start(const struct physical_volume *pv)
+{
+ return pv_field(pv, pe_start);
+}
+
+uint32_t pv_pe_count(const struct physical_volume *pv)
+{
+ return pv_field(pv, pe_count);
+}
+
+uint32_t pv_pe_alloc_count(const struct physical_volume *pv)
+{
+ return pv_field(pv, pe_alloc_count);
+}
+
+uint32_t pv_mda_count(const struct physical_volume *pv)
+{
+ struct lvmcache_info *info;
+
+ info = info_from_pvid((const char *)&pv->id.uuid, 0);
+ return info ? dm_list_size(&info->mdas) : UINT64_C(0);
+}
+
+uint32_t pv_mda_used_count(const struct physical_volume *pv)
+{
+ struct lvmcache_info *info;
+ struct metadata_area *mda;
+ uint32_t used_count=0;
+
+ info = info_from_pvid((const char *)&pv->id.uuid, 0);
+ if (!info)
+ return 0;
+ dm_list_iterate_items(mda, &info->mdas) {
+ if (!mda_is_ignored(mda))
+ used_count++;
+ }
+ return used_count;
+}
+
+/**
+ * is_orphan - Determine whether a pv is an orphan based on its vg_name
+ * @pv: handle to the physical volume
+ */
+int is_orphan(const struct physical_volume *pv)
+{
+ return is_orphan_vg(pv_field(pv, vg_name));
+}
+
+/**
+ * is_pv - Determine whether a pv is a real pv or dummy one
+ * @pv: handle to device
+ */
+int is_pv(const struct physical_volume *pv)
+{
+ return (pv_field(pv, vg_name) ? 1 : 0);
+}
+
+int is_missing_pv(const struct physical_volume *pv)
+{
+ return pv_field(pv, status) & MISSING_PV ? 1 : 0;
+}
+
+char *pv_attr_dup(struct dm_pool *mem, const struct physical_volume *pv)
+{
+ char *repstr;
+
+ if (!(repstr = dm_pool_zalloc(mem, 3))) {
+ log_error("dm_pool_alloc failed");
+ return NULL;
+ }
+
+ repstr[0] = (pv->status & ALLOCATABLE_PV) ? 'a' : '-';
+ repstr[1] = (pv->status & EXPORTED_VG) ? 'x' : '-';
+ return repstr;
+}
+
+uint64_t pv_mda_size(const struct physical_volume *pv)
+{
+ struct lvmcache_info *info;
+ uint64_t min_mda_size = 0;
+ const char *pvid = (const char *)(&pv->id.uuid);
+
+ /* PVs could have 2 mdas of different sizes (rounding effect) */
+ if ((info = info_from_pvid(pvid, 0)))
+ min_mda_size = find_min_mda_size(&info->mdas);
+ return min_mda_size;
+}
+
+uint64_t pv_mda_free(const struct physical_volume *pv)
+{
+ struct lvmcache_info *info;
+ uint64_t freespace = UINT64_MAX, mda_free;
+ const char *pvid = (const char *)&pv->id.uuid;
+ struct metadata_area *mda;
+
+ if ((info = info_from_pvid(pvid, 0)))
+ dm_list_iterate_items(mda, &info->mdas) {
+ if (!mda->ops->mda_free_sectors)
+ continue;
+ mda_free = mda->ops->mda_free_sectors(mda);
+ if (mda_free < freespace)
+ freespace = mda_free;
+ }
+
+ if (freespace == UINT64_MAX)
+ freespace = UINT64_C(0);
+ return freespace;
+}
+
+uint64_t pv_used(const struct physical_volume *pv)
+{
+ uint64_t used;
+
+ if (!pv->pe_count)
+ used = 0LL;
+ else
+ used = (uint64_t) pv->pe_alloc_count * pv->pe_size;
+ return used;
+}
+
+unsigned pv_mda_set_ignored(const struct physical_volume *pv, unsigned mda_ignored)
+{
+ struct lvmcache_info *info;
+ struct metadata_area *mda, *vg_mda, *tmda;
+ struct dm_list *vg_mdas_in_use, *vg_mdas_ignored;
+
+ if (!(info = info_from_pvid((const char *)&pv->id.uuid, 0)))
+ return_0;
+
+ if (is_orphan(pv)) {
+ dm_list_iterate_items(mda, &info->mdas)
+ mda_set_ignored(mda, mda_ignored);
+ return 1;
+ }
+
+ /*
+ * Do not allow disabling of the the last PV in a VG.
+ */
+ if (pv_mda_used_count(pv) == vg_mda_used_count(pv->vg)) {
+ log_error("Cannot disable all metadata areas in volume group %s.",
+ pv->vg->name);
+ return 0;
+ }
+
+ /*
+ * Non-orphan case is more complex.
+ * If the PV's mdas are ignored, and we wish to un-ignore,
+ * we clear the bit and move them from the ignored mda list to the
+ * in_use list, ensuring the new state will get written to disk
+ * in the vg_write() path.
+ * If the PV's mdas are not ignored, and we are setting
+ * them to ignored, we set the bit but leave them on the in_use
+ * list, ensuring the new state will get written to disk in the
+ * vg_write() path.
+ */
+ vg_mdas_in_use = &pv->vg->fid->metadata_areas_in_use;
+ vg_mdas_ignored = &pv->vg->fid->metadata_areas_ignored;
+
+ dm_list_iterate_items(mda, &info->mdas) {
+ if (mda_is_ignored(mda) && !mda_ignored)
+ /* Changing an ignored mda to one in_use requires moving it */
+ dm_list_iterate_items_safe(vg_mda, tmda, vg_mdas_ignored)
+ if (mda_locns_match(mda, vg_mda)) {
+ mda_set_ignored(vg_mda, mda_ignored);
+ dm_list_move(vg_mdas_in_use, &vg_mda->list);
+ }
+
+ dm_list_iterate_items_safe(vg_mda, tmda, vg_mdas_in_use)
+ if (mda_locns_match(mda, vg_mda))
+ /* Don't move mda: needs writing to disk. */
+ mda_set_ignored(vg_mda, mda_ignored);
+
+ mda_set_ignored(mda, mda_ignored);
+ }
+
+ return 1;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _LVM_PV_H
+#define _LVM_PV_H
+
+struct id;
+struct device;
+struct format_type;
+struct volume_group;
+
+struct physical_volume {
+ struct id id;
+ struct device *dev;
+ const struct format_type *fmt;
+
+ /*
+ * vg_name and vgid are used before the parent VG struct exists.
+ * FIXME: Investigate removal/substitution with 'vg' fields.
+ */
+ const char *vg_name;
+ struct id vgid;
+
+ /*
+ * 'vg' is set and maintained when the PV belongs to a 'pvs'
+ * list in a parent VG struct.
+ */
+ struct volume_group *vg;
+
+ uint64_t status;
+ uint64_t size;
+
+ /* physical extents */
+ uint32_t pe_size;
+ uint64_t pe_start;
+ uint32_t pe_count;
+ uint32_t pe_alloc_count;
+ unsigned long pe_align;
+ unsigned long pe_align_offset;
+
+ struct dm_list segments; /* Ordered pv_segments covering complete PV */
+ struct dm_list tags;
+};
+
+char *pv_fmt_dup(const struct physical_volume *pv);
+char *pv_name_dup(const struct physical_volume *pv);
+struct device *pv_dev(const struct physical_volume *pv);
+const char *pv_vg_name(const struct physical_volume *pv);
+char *pv_attr_dup(struct dm_pool *mem, const struct physical_volume *pv);
+const char *pv_dev_name(const struct physical_volume *pv);
+char *pv_uuid_dup(const struct physical_volume *pv);
+char *pv_tags_dup(const struct physical_volume *pv);
+uint64_t pv_size(const struct physical_volume *pv);
+uint64_t pv_size_field(const struct physical_volume *pv);
+uint64_t pv_dev_size(const struct physical_volume *pv);
+uint64_t pv_free(const struct physical_volume *pv);
+uint64_t pv_status(const struct physical_volume *pv);
+uint32_t pv_pe_size(const struct physical_volume *pv);
+uint64_t pv_pe_start(const struct physical_volume *pv);
+uint32_t pv_pe_count(const struct physical_volume *pv);
+uint32_t pv_pe_alloc_count(const struct physical_volume *pv);
+uint64_t pv_mda_size(const struct physical_volume *pv);
+uint64_t pv_mda_free(const struct physical_volume *pv);
+uint64_t pv_used(const struct physical_volume *pv);
+uint32_t pv_mda_count(const struct physical_volume *pv);
+uint32_t pv_mda_used_count(const struct physical_volume *pv);
+unsigned pv_mda_set_ignored(const struct physical_volume *pv, unsigned ignored);
+int is_orphan(const struct physical_volume *pv);
+int is_missing_pv(const struct physical_volume *pv);
+int is_pv(const struct physical_volume *pv);
+
+#endif /* _LVM_PV_H */
--- /dev/null
+/*
+ * Copyright (C) 2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_PV_ALLOC_H
+
+int alloc_pv_segment_whole_pv(struct dm_pool *mem, struct physical_volume *pv);
+int peg_dup(struct dm_pool *mem, struct dm_list *peg_new, struct dm_list *peg_old);
+struct pv_segment *assign_peg_to_lvseg(struct physical_volume *pv, uint32_t pe,
+ uint32_t area_len,
+ struct lv_segment *seg,
+ uint32_t area_num);
+int pv_split_segment(struct dm_pool *mem,
+ struct physical_volume *pv, uint32_t pe,
+ struct pv_segment **pvseg_allocated);
+int release_pv_segment(struct pv_segment *peg, uint32_t area_reduction);
+int check_pv_segments(struct volume_group *vg);
+void merge_pv_segments(struct pv_segment *peg1, struct pv_segment *peg2);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2003 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "pv_alloc.h"
+#include "toolcontext.h"
+#include "archiver.h"
+#include "locking.h"
+#include "lvmcache.h"
+
+static struct pv_segment *_alloc_pv_segment(struct dm_pool *mem,
+ struct physical_volume *pv,
+ uint32_t pe, uint32_t len,
+ struct lv_segment *lvseg,
+ uint32_t lv_area)
+{
+ struct pv_segment *peg;
+
+ if (!(peg = dm_pool_zalloc(mem, sizeof(*peg)))) {
+ log_error("pv_segment allocation failed");
+ return NULL;
+ }
+
+ peg->pv = pv;
+ peg->pe = pe;
+ peg->len = len;
+ peg->lvseg = lvseg;
+ peg->lv_area = lv_area;
+
+ dm_list_init(&peg->list);
+
+ return peg;
+}
+
+int alloc_pv_segment_whole_pv(struct dm_pool *mem, struct physical_volume *pv)
+{
+ struct pv_segment *peg;
+
+ if (!pv->pe_count)
+ return 1;
+
+ /* FIXME Cope with holes in PVs */
+ if (!(peg = _alloc_pv_segment(mem, pv, 0, pv->pe_count, NULL, 0)))
+ return_0;
+
+ dm_list_add(&pv->segments, &peg->list);
+
+ return 1;
+}
+
+int peg_dup(struct dm_pool *mem, struct dm_list *peg_new, struct dm_list *peg_old)
+{
+ struct pv_segment *peg, *pego;
+
+ dm_list_init(peg_new);
+
+ dm_list_iterate_items(pego, peg_old) {
+ if (!(peg = _alloc_pv_segment(mem, pego->pv, pego->pe,
+ pego->len, pego->lvseg,
+ pego->lv_area)))
+ return_0;
+ dm_list_add(peg_new, &peg->list);
+ }
+
+ return 1;
+}
+
+/* Find segment at a given physical extent in a PV */
+static struct pv_segment *find_peg_by_pe(const struct physical_volume *pv,
+ uint32_t pe)
+{
+ struct pv_segment *pvseg;
+
+ /* search backwards to optimise mostly used last segment split */
+ dm_list_iterate_back_items(pvseg, &pv->segments)
+ if (pe >= pvseg->pe && pe < pvseg->pe + pvseg->len)
+ return pvseg;
+
+ return NULL;
+}
+
+/*
+ * Split peg at given extent.
+ * Second part is always not allocated to a LV and returned.
+ */
+static struct pv_segment *_pv_split_segment(struct dm_pool *mem,
+ struct physical_volume *pv,
+ struct pv_segment *peg,
+ uint32_t pe)
+{
+ struct pv_segment *peg_new;
+
+ if (!(peg_new = _alloc_pv_segment(mem, peg->pv, pe,
+ peg->len + peg->pe - pe,
+ NULL, 0)))
+ return_NULL;
+
+ peg->len = peg->len - peg_new->len;
+
+ dm_list_add_h(&peg->list, &peg_new->list);
+
+ if (peg->lvseg) {
+ peg->pv->pe_alloc_count -= peg_new->len;
+ peg->lvseg->lv->vg->free_count += peg_new->len;
+ }
+
+ return peg_new;
+}
+
+/*
+ * Ensure there is a PV segment boundary at the given extent.
+ */
+int pv_split_segment(struct dm_pool *mem,
+ struct physical_volume *pv, uint32_t pe,
+ struct pv_segment **pvseg_allocated)
+{
+ struct pv_segment *pvseg, *pvseg_new = NULL;
+
+ if (pe == pv->pe_count)
+ goto out;
+
+ if (!(pvseg = find_peg_by_pe(pv, pe))) {
+ log_error("Segment with extent %" PRIu32 " in PV %s not found",
+ pe, pv_dev_name(pv));
+ return 0;
+ }
+
+ /* This is a peg start already */
+ if (pe == pvseg->pe) {
+ pvseg_new = pvseg;
+ goto out;
+ }
+
+ if (!(pvseg_new = _pv_split_segment(mem, pv, pvseg, pe)))
+ return_0;
+out:
+ if (pvseg_allocated)
+ *pvseg_allocated = pvseg_new;
+
+ return 1;
+}
+
+static struct pv_segment null_pv_segment = {
+ .pv = NULL,
+ .pe = 0,
+};
+
+struct pv_segment *assign_peg_to_lvseg(struct physical_volume *pv,
+ uint32_t pe, uint32_t area_len,
+ struct lv_segment *seg,
+ uint32_t area_num)
+{
+ struct pv_segment *peg = NULL;
+
+ /* Missing format1 PV */
+ if (!pv)
+ return &null_pv_segment;
+
+ if (!pv_split_segment(seg->lv->vg->vgmem, pv, pe, &peg) ||
+ !pv_split_segment(seg->lv->vg->vgmem, pv, pe + area_len, NULL))
+ return_NULL;
+
+ if (!peg) {
+ log_error("Missing PV segment on %s at %u.",
+ pv_dev_name(pv), pe);
+ return NULL;
+ }
+
+ peg->lvseg = seg;
+ peg->lv_area = area_num;
+
+ peg->pv->pe_alloc_count += area_len;
+ peg->lvseg->lv->vg->free_count -= area_len;
+
+ return peg;
+}
+
+int release_pv_segment(struct pv_segment *peg, uint32_t area_reduction)
+{
+ if (!peg->lvseg) {
+ log_error("release_pv_segment with unallocated segment: "
+ "%s PE %" PRIu32, pv_dev_name(peg->pv), peg->pe);
+ return 0;
+ }
+
+ if (peg->lvseg->area_len == area_reduction) {
+ peg->pv->pe_alloc_count -= area_reduction;
+ peg->lvseg->lv->vg->free_count += area_reduction;
+
+ peg->lvseg = NULL;
+ peg->lv_area = 0;
+
+ /* FIXME merge free space */
+
+ return 1;
+ }
+
+ if (!pv_split_segment(peg->lvseg->lv->vg->vgmem,
+ peg->pv, peg->pe + peg->lvseg->area_len -
+ area_reduction, NULL))
+ return_0;
+
+ return 1;
+}
+
+/*
+ * Only for use by lv_segment merging routines.
+ */
+void merge_pv_segments(struct pv_segment *peg1, struct pv_segment *peg2)
+{
+ peg1->len += peg2->len;
+
+ dm_list_del(&peg2->list);
+}
+
+/*
+ * Calculate the overlap, in extents, between a struct pv_segment and
+ * a struct pe_range.
+ */
+static uint32_t _overlap_pe(const struct pv_segment *pvseg,
+ const struct pe_range *per)
+{
+ uint32_t start;
+ uint32_t end;
+
+ start = max(pvseg->pe, per->start);
+ end = min(pvseg->pe + pvseg->len, per->start + per->count);
+ if (end < start)
+ return 0;
+ else
+ return end - start;
+}
+
+/*
+ * Returns: number of free PEs in a struct pv_list
+ */
+uint32_t pv_list_extents_free(const struct dm_list *pvh)
+{
+ struct pv_list *pvl;
+ struct pe_range *per;
+ uint32_t extents = 0;
+ struct pv_segment *pvseg;
+
+ dm_list_iterate_items(pvl, pvh) {
+ dm_list_iterate_items(per, pvl->pe_ranges) {
+ dm_list_iterate_items(pvseg, &pvl->pv->segments) {
+ if (!pvseg_is_allocated(pvseg))
+ extents += _overlap_pe(pvseg, per);
+ }
+ }
+ }
+
+ return extents;
+}
+
+/*
+ * Check all pv_segments in VG for consistency
+ */
+int check_pv_segments(struct volume_group *vg)
+{
+ struct physical_volume *pv;
+ struct pv_list *pvl;
+ struct pv_segment *peg;
+ unsigned s, segno;
+ uint32_t start_pe, alloced;
+ uint32_t pv_count = 0, free_count = 0, extent_count = 0;
+ int ret = 1;
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ pv = pvl->pv;
+ segno = 0;
+ start_pe = 0;
+ alloced = 0;
+ pv_count++;
+
+ dm_list_iterate_items(peg, &pv->segments) {
+ s = peg->lv_area;
+
+ /* FIXME Remove this next line eventually */
+ log_debug("%s %u: %6u %6u: %s(%u:%u)",
+ pv_dev_name(pv), segno++, peg->pe, peg->len,
+ peg->lvseg ? peg->lvseg->lv->name : "NULL",
+ peg->lvseg ? peg->lvseg->le : 0, s);
+ /* FIXME Add details here on failure instead */
+ if (start_pe != peg->pe) {
+ log_error("Gap in pvsegs: %u, %u",
+ start_pe, peg->pe);
+ ret = 0;
+ }
+ if (peg->lvseg) {
+ if (seg_type(peg->lvseg, s) != AREA_PV) {
+ log_error("Wrong lvseg area type");
+ ret = 0;
+ }
+ if (seg_pvseg(peg->lvseg, s) != peg) {
+ log_error("Inconsistent pvseg pointers");
+ ret = 0;
+ }
+ if (peg->lvseg->area_len != peg->len) {
+ log_error("Inconsistent length: %u %u",
+ peg->len,
+ peg->lvseg->area_len);
+ ret = 0;
+ }
+ alloced += peg->len;
+ }
+ start_pe += peg->len;
+ }
+
+ if (start_pe != pv->pe_count) {
+ log_error("PV segment pe_count mismatch: %u != %u",
+ start_pe, pv->pe_count);
+ ret = 0;
+ }
+
+ if (alloced != pv->pe_alloc_count) {
+ log_error("PV segment pe_alloc_count mismatch: "
+ "%u != %u", alloced, pv->pe_alloc_count);
+ ret = 0;
+ }
+
+ extent_count += start_pe;
+ free_count += (start_pe - alloced);
+ }
+
+ if (pv_count != vg->pv_count) {
+ log_error("PV segment VG pv_count mismatch: %u != %u",
+ pv_count, vg->pv_count);
+ ret = 0;
+ }
+
+ if (free_count != vg->free_count) {
+ log_error("PV segment VG free_count mismatch: %u != %u",
+ free_count, vg->free_count);
+ ret = 0;
+ }
+
+ if (extent_count != vg->extent_count) {
+ log_error("PV segment VG extent_count mismatch: %u != %u",
+ extent_count, vg->extent_count);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int _reduce_pv(struct physical_volume *pv, struct volume_group *vg, uint32_t new_pe_count)
+{
+ struct pv_segment *peg, *pegt;
+ uint32_t old_pe_count = pv->pe_count;
+
+ if (new_pe_count < pv->pe_alloc_count) {
+ log_error("%s: cannot resize to %" PRIu32 " extents "
+ "as %" PRIu32 " are allocated.",
+ pv_dev_name(pv), new_pe_count,
+ pv->pe_alloc_count);
+ return 0;
+ }
+
+ /* Check PEs to be removed are not already allocated */
+ dm_list_iterate_items(peg, &pv->segments) {
+ if (peg->pe + peg->len <= new_pe_count)
+ continue;
+
+ if (peg->lvseg) {
+ log_error("%s: cannot resize to %" PRIu32 " extents as "
+ "later ones are allocated.",
+ pv_dev_name(pv), new_pe_count);
+ return 0;
+ }
+ }
+
+ if (!pv_split_segment(vg->vgmem, pv, new_pe_count, NULL))
+ return_0;
+
+ dm_list_iterate_items_safe(peg, pegt, &pv->segments) {
+ if (peg->pe + peg->len > new_pe_count)
+ dm_list_del(&peg->list);
+ }
+
+ pv->pe_count = new_pe_count;
+
+ vg->extent_count -= (old_pe_count - new_pe_count);
+ vg->free_count -= (old_pe_count - new_pe_count);
+
+ return 1;
+}
+
+static int _extend_pv(struct physical_volume *pv, struct volume_group *vg,
+ uint32_t new_pe_count)
+{
+ struct pv_segment *peg;
+ uint32_t old_pe_count = pv->pe_count;
+
+ if ((uint64_t) new_pe_count * pv->pe_size > pv->size ) {
+ log_error("%s: cannot resize to %" PRIu32 " extents as there "
+ "is only room for %" PRIu64 ".", pv_dev_name(pv),
+ new_pe_count, pv->size / pv->pe_size);
+ return 0;
+ }
+
+ peg = _alloc_pv_segment(pv->fmt->cmd->mem, pv,
+ old_pe_count,
+ new_pe_count - old_pe_count,
+ NULL, 0);
+ dm_list_add(&pv->segments, &peg->list);
+
+ pv->pe_count = new_pe_count;
+
+ vg->extent_count += (new_pe_count - old_pe_count);
+ vg->free_count += (new_pe_count - old_pe_count);
+
+ return 1;
+}
+
+/*
+ * Resize a PV in a VG, adding or removing segments as needed.
+ * New size must fit within pv->size.
+ */
+int pv_resize(struct physical_volume *pv,
+ struct volume_group *vg,
+ uint32_t new_pe_count)
+{
+ if ((new_pe_count == pv->pe_count)) {
+ log_verbose("No change to size of physical volume %s.",
+ pv_dev_name(pv));
+ return 1;
+ }
+
+ log_verbose("Resizing physical volume %s from %" PRIu32
+ " to %" PRIu32 " extents.",
+ pv_dev_name(pv), pv->pe_count, new_pe_count);
+
+ if (new_pe_count > pv->pe_count)
+ return _extend_pv(pv, vg, new_pe_count);
+ else
+ return _reduce_pv(pv, vg, new_pe_count);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "pv_map.h"
+#include "pv_alloc.h"
+
+#include <assert.h>
+
+/*
+ * Areas are maintained in size order, largest first.
+ *
+ * FIXME Cope with overlap.
+ */
+static void _insert_area(struct dm_list *head, struct pv_area *a, unsigned reduced)
+{
+ struct pv_area *pva;
+ uint32_t count = reduced ? a->unreserved : a->count;
+
+ dm_list_iterate_items(pva, head)
+ if (count > pva->count)
+ break;
+
+ dm_list_add(&pva->list, &a->list);
+ a->map->pe_count += a->count;
+}
+
+static void _remove_area(struct pv_area *a)
+{
+ dm_list_del(&a->list);
+ a->map->pe_count -= a->count;
+}
+
+static int _create_single_area(struct dm_pool *mem, struct pv_map *pvm,
+ uint32_t start, uint32_t length)
+{
+ struct pv_area *pva;
+
+ if (!(pva = dm_pool_zalloc(mem, sizeof(*pva))))
+ return_0;
+
+ log_debug("Allowing allocation on %s start PE %" PRIu32 " length %"
+ PRIu32, pv_dev_name(pvm->pv), start, length);
+ pva->map = pvm;
+ pva->start = start;
+ pva->count = length;
+ pva->unreserved = pva->count;
+ _insert_area(&pvm->areas, pva, 0);
+
+ return 1;
+}
+
+static int _create_alloc_areas_for_pv(struct dm_pool *mem, struct pv_map *pvm,
+ uint32_t start, uint32_t count)
+{
+ struct pv_segment *peg;
+ uint32_t pe, end, area_len;
+
+ /* Only select extents from start to end inclusive */
+ end = start + count - 1;
+ if (end > pvm->pv->pe_count - 1)
+ end = pvm->pv->pe_count - 1;
+
+ pe = start;
+
+ /* Walk through complete ordered list of device segments */
+ dm_list_iterate_items(peg, &pvm->pv->segments) {
+ /* pe holds the next extent we want to check */
+
+ /* Beyond the range we're interested in? */
+ if (pe > end)
+ break;
+
+ /* Skip if we haven't reached the first seg we want yet */
+ if (pe > peg->pe + peg->len - 1)
+ continue;
+
+ /* Free? */
+ if (peg->lvseg)
+ goto next;
+
+ /* How much of this peg do we need? */
+ area_len = (end >= peg->pe + peg->len - 1) ?
+ peg->len - (pe - peg->pe) : end - pe + 1;
+
+ if (!_create_single_area(mem, pvm, pe, area_len))
+ return_0;
+
+ next:
+ pe = peg->pe + peg->len;
+ }
+
+ return 1;
+}
+
+static int _create_all_areas_for_pv(struct dm_pool *mem, struct pv_map *pvm,
+ struct dm_list *pe_ranges)
+{
+ struct pe_range *aa;
+
+ if (!pe_ranges) {
+ /* Use whole PV */
+ if (!_create_alloc_areas_for_pv(mem, pvm, UINT32_C(0),
+ pvm->pv->pe_count))
+ return_0;
+
+ return 1;
+ }
+
+ dm_list_iterate_items(aa, pe_ranges) {
+ if (!_create_alloc_areas_for_pv(mem, pvm, aa->start,
+ aa->count))
+ return_0;
+ }
+
+ return 1;
+}
+
+static int _create_maps(struct dm_pool *mem, struct dm_list *pvs, struct dm_list *pvms)
+{
+ struct pv_map *pvm, *pvm2;
+ struct pv_list *pvl;
+
+ dm_list_iterate_items(pvl, pvs) {
+ if (!(pvl->pv->status & ALLOCATABLE_PV))
+ continue;
+ if (is_missing_pv(pvl->pv))
+ continue;
+ assert(pvl->pv->dev);
+
+ pvm = NULL;
+
+ dm_list_iterate_items(pvm2, pvms)
+ if (pvm2->pv->dev == pvl->pv->dev) {
+ pvm = pvm2;
+ break;
+ }
+
+ if (!pvm) {
+ if (!(pvm = dm_pool_zalloc(mem, sizeof(*pvm))))
+ return_0;
+
+ pvm->pv = pvl->pv;
+ dm_list_init(&pvm->areas);
+ dm_list_add(pvms, &pvm->list);
+ }
+
+ if (!_create_all_areas_for_pv(mem, pvm, pvl->pe_ranges))
+ return_0;
+ }
+
+ return 1;
+}
+
+/*
+ * Create list of PV areas available for this particular allocation
+ */
+struct dm_list *create_pv_maps(struct dm_pool *mem, struct volume_group *vg,
+ struct dm_list *allocatable_pvs)
+{
+ struct dm_list *pvms;
+
+ if (!(pvms = dm_pool_zalloc(mem, sizeof(*pvms)))) {
+ log_error("create_pv_maps alloc failed");
+ return NULL;
+ }
+
+ dm_list_init(pvms);
+
+ if (!_create_maps(mem, allocatable_pvs, pvms)) {
+ log_error("Couldn't create physical volume maps in %s",
+ vg->name);
+ dm_pool_free(mem, pvms);
+ return NULL;
+ }
+
+ return pvms;
+}
+
+void consume_pv_area(struct pv_area *pva, uint32_t to_go)
+{
+ _remove_area(pva);
+
+ assert(to_go <= pva->count);
+
+ if (to_go < pva->count) {
+ /* split the area */
+ pva->start += to_go;
+ pva->count -= to_go;
+ pva->unreserved = pva->count;
+ _insert_area(&pva->map->areas, pva, 0);
+ }
+}
+
+/*
+ * Remove an area from list and reinsert it based on its new smaller size
+ * after a provisional allocation.
+ */
+void reinsert_reduced_pv_area(struct pv_area *pva)
+{
+ _remove_area(pva);
+ _insert_area(&pva->map->areas, pva, 1);
+}
+
+uint32_t pv_maps_size(struct dm_list *pvms)
+{
+ struct pv_map *pvm;
+ uint32_t pe_count = 0;
+
+ dm_list_iterate_items(pvm, pvms)
+ pe_count += pvm->pe_count;
+
+ return pe_count;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_PV_MAP_H
+#define _LVM_PV_MAP_H
+
+#include "metadata.h"
+
+/*
+ * The in core rep. only stores a mapping from
+ * logical extents to physical extents against an
+ * lv. Sometimes, when allocating a new lv for
+ * instance, it is useful to have the inverse
+ * mapping available.
+ */
+
+struct pv_area {
+ struct pv_map *map;
+ uint32_t start;
+ uint32_t count;
+
+ /* Number of extents unreserved during ALLOC_ANYWHERE allocation. */
+ uint32_t unreserved;
+
+ struct dm_list list; /* pv_map.areas */
+};
+
+/*
+ * When building up a potential group of "parallel" extent ranges during
+ * an allocation attempt, track the maximum number of extents that may
+ * need to be used as a particular parallel area. Several of these
+ * structs may reference the same pv_area, but 'used' may differ between
+ * them. The sum of all the 'used' variables referring to the same
+ * pv_area may not exceed that area's count, so we cannot allocate the
+ * same extents twice.
+ */
+struct pv_area_used {
+ struct pv_area *pva;
+ uint32_t used;
+};
+
+struct pv_map {
+ struct physical_volume *pv;
+ struct dm_list areas; /* struct pv_areas */
+ uint32_t pe_count; /* Total number of PEs */
+
+ struct dm_list list;
+};
+
+/*
+ * Find intersection between available_pvs and free space in VG
+ */
+struct dm_list *create_pv_maps(struct dm_pool *mem, struct volume_group *vg,
+ struct dm_list *allocatable_pvs);
+
+void consume_pv_area(struct pv_area *area, uint32_t to_go);
+void reinsert_reduced_pv_area(struct pv_area *pva);
+
+uint32_t pv_maps_size(struct dm_list *pvms);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2009-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "locking.h"
+#include "metadata.h"
+#include "segtype.h"
+#include "toolcontext.h"
+
+/* Add lv as replicator_dev device */
+int replicator_dev_add_rimage(struct replicator_device *rdev,
+ struct logical_volume *lv)
+{
+ if (!lv || !rdev)
+ return_0;
+
+ if (lv_is_rimage(lv)) {
+ log_error("Logical volume %s is already part of other "
+ "replicator.", lv->name);
+ return 0;
+ }
+
+ if (rdev->lv) {
+ log_error("Logical volume %s can not be attached to an "
+ "already defined replicator device", lv->name);
+ return 0;
+ }
+
+ lv_set_hidden(lv);
+ lv->rdevice = rdev;
+ rdev->lv = lv;
+
+ return add_seg_to_segs_using_this_lv(lv, rdev->replicator_dev);
+}
+
+/* Remove lv from replicator_dev device */
+struct logical_volume *replicator_dev_remove_rimage(struct replicator_device *rdev)
+{
+ struct logical_volume *lv;
+
+ if (!rdev || !rdev->lv)
+ return_NULL;
+
+ lv = rdev->lv;
+ if (!remove_seg_from_segs_using_this_lv(lv, rdev->replicator_dev))
+ return_NULL;
+
+ /* FIXME: - check for site references */
+ rdev->lv = NULL;
+ lv->rdevice = NULL;
+ lv_set_visible(lv);
+
+ return lv;
+}
+
+int replicator_dev_add_slog(struct replicator_device *rdev,
+ struct logical_volume *slog)
+{
+ if (!slog || !rdev)
+ return_0;
+
+ if (rdev->slog) {
+ log_error("Replicator device in site %s already has sync log.",
+ rdev->rsite->name);
+ return 0;
+ }
+
+ if (slog->rdevice) {
+ log_error("Sync log %s is already used by replicator %s.",
+ slog->name, slog->rdevice->rsite->replicator->name);
+ return 0;
+ }
+
+ lv_set_hidden(slog);
+ slog->rdevice = rdev;
+ rdev->slog = slog;
+
+ return add_seg_to_segs_using_this_lv(slog, rdev->replicator_dev);
+}
+
+struct logical_volume *replicator_dev_remove_slog(struct replicator_device *rdev)
+{
+ struct logical_volume *lv;
+
+ if (!rdev)
+ return_NULL;
+
+ lv = rdev->slog;
+ if (!lv) {
+ log_error("Replicator device in site %s does not have sync log.",
+ rdev->rsite->name);
+ return NULL;
+ }
+
+ if (!remove_seg_from_segs_using_this_lv(lv, rdev->replicator_dev))
+ return_NULL;
+
+ rdev->slog = NULL;
+ lv->rdevice = NULL;
+ lv_set_visible(lv);
+
+ return lv;
+}
+
+int replicator_add_replicator_dev(struct logical_volume *replicator_lv,
+ struct lv_segment *replicator_dev_seg)
+{
+ if (!replicator_lv)
+ return_0;
+
+ if (!(replicator_lv->status & REPLICATOR)) {
+ dm_list_init(&replicator_lv->rsites);
+ lv_set_hidden(replicator_lv);
+ replicator_lv->status |= REPLICATOR;
+ }
+
+ if (!replicator_dev_seg)
+ return 1;
+
+ if (replicator_dev_seg->replicator) {
+ log_error("Replicator device %s is already part of replicator.",
+ replicator_dev_seg->lv->name);
+ return 0;
+ }
+
+ replicator_dev_seg->replicator = replicator_lv;
+
+ return add_seg_to_segs_using_this_lv(replicator_lv, replicator_dev_seg);
+}
+
+/**
+ * Returns rimage ?? lv upon succeful detach of device
+ * entire LV entry should be removed by this crootall ??
+ */
+struct logical_volume *replicator_remove_replicator_dev(struct lv_segment *replicator_dev_seg)
+{
+ struct logical_volume *lv = NULL;
+
+ log_error("FIXME: not implemented.");
+#if 0
+ /* FIXME: - this is going to be complex.... */
+ if (!replicator_dev_seg)
+ return_NULL;
+
+ /* if slog or rimage - exit */
+
+ if (!remove_seg_from_segs_using_this_lv(lv, replicator_seg))
+ return_NULL;
+
+ replicator_seg->rlog_lv = NULL;
+ lv->status &= ~REPLICATOR_LOG;
+ lv_set_visible(lv);
+#endif
+
+ return lv;
+}
+
+int replicator_add_rlog(struct lv_segment *replicator_seg,
+ struct logical_volume *rlog_lv)
+{
+ if (!rlog_lv)
+ return_0;
+
+ if (rlog_lv->status & REPLICATOR_LOG) {
+ log_error("Rlog device %s is already used.", rlog_lv->name);
+ return 0;
+ }
+
+ lv_set_hidden(rlog_lv);
+ rlog_lv->status |= REPLICATOR_LOG;
+ replicator_seg->rlog_lv = rlog_lv;
+
+ return add_seg_to_segs_using_this_lv(rlog_lv, replicator_seg);
+}
+
+struct logical_volume *replicator_remove_rlog(struct lv_segment *replicator_seg)
+{
+ struct logical_volume *lv;
+
+ if (!replicator_seg)
+ return_0;
+
+ if (!(lv = replicator_seg->rlog_lv)) {
+ log_error("Replog segment %s does not have rlog.",
+ replicator_seg->lv->name);
+ return NULL;
+ }
+
+ if (!remove_seg_from_segs_using_this_lv(lv, replicator_seg))
+ return_NULL;
+
+ replicator_seg->rlog_lv = NULL;
+ lv->status &= ~REPLICATOR_LOG;
+ lv_set_visible(lv);
+
+ return lv;
+}
+
+
+#if 0
+/*
+ * Create new LV to pretend the original LV
+ * this target will have a 'replicator' segment
+ */
+int lv_add_replicator(struct logical_volume *origin, const char *rep_suffix)
+{
+ struct logical_volume *rep_lv;
+ char *name;
+ size_t slen;
+
+ if (!(name = strstr(origin->name, rep_suffix))) {
+ log_error("Failed to find replicator suffix %s in LV name %s",
+ rep_suffix, origin->name);
+ return 0;
+ }
+ slen = (size_t)(name - origin->name);
+ name = alloca(slen + 1);
+ memcpy(name, origin->name, slen);
+ name[slen] = 0;
+
+ if ((rep_lv = find_lv(origin->vg, name))) {
+ rep_lv->status |= VIRTUAL;
+ return 1;
+ }
+
+ if (!(rep_lv = lv_create_empty(name, &origin->lvid,
+ LVM_READ | LVM_WRITE | VISIBLE_LV,
+ ALLOC_INHERIT, origin->vg)))
+ return_0;
+
+ if (!lv_add_virtual_segment(rep_lv, 0, origin->le_count,
+ get_segtype_from_string(origin->vg->cmd,
+ "error")))
+ return_0;
+
+ rep_lv->status |= VIRTUAL;
+ return 1;
+}
+
+int lv_remove_replicator(struct logical_volume *lv)
+{
+ return 1;
+}
+#endif
+
+/*
+ * Check all replicator structures:
+ * only non-clustered VG for Replicator
+ * only one segment in replicator LV
+ * site has correct combination of operation_mode parameters
+ * site and related devices have correct index numbers
+ * duplicate site names, site indexes, device names, device indexes
+ */
+int check_replicator_segment(const struct lv_segment *rseg)
+{
+ struct replicator_site *rsite, *rsiteb;
+ struct replicator_device *rdev, *rdevb;
+ struct logical_volume *lv = rseg->lv;
+ int r = 1;
+
+ if (vg_is_clustered(lv->vg)) {
+ log_error("Volume Group %s of replicator %s is clustered",
+ lv->vg->name, lv->name);
+ return 0;
+ }
+
+ if (dm_list_size(&lv->segments) != 1) {
+ log_error("Replicator %s segment size %d != 1",
+ lv->name, dm_list_size(&lv->segments));
+ return 0;
+ }
+
+ dm_list_iterate_items(rsite, &lv->rsites) {
+ if (rsite->op_mode == DM_REPLICATOR_SYNC) {
+ if (rsite->fall_behind_timeout) {
+ log_error("Defined fall_behind_timeout="
+ "%d for sync replicator %s/%s.",
+ rsite->fall_behind_timeout, lv->name,
+ rsite->name);
+ r = 0;
+ }
+ if (rsite->fall_behind_ios) {
+ log_error("Defined fall_behind_ios="
+ "%d for sync replicator %s/%s.",
+ rsite->fall_behind_ios, lv->name, rsite->name);
+ r = 0;
+ }
+ if (rsite->fall_behind_data) {
+ log_error("Defined fall_behind_data="
+ "%" PRIu64 " for sync replicator %s/%s.",
+ rsite->fall_behind_data, lv->name, rsite->name);
+ r = 0;
+ }
+ } else {
+ if (rsite->fall_behind_timeout && rsite->fall_behind_ios) {
+ log_error("Defined fall_behind_timeout and"
+ " fall_behind_ios for async replicator %s/%s.",
+ lv->name, rsite->name);
+ r = 0;
+ }
+ if (rsite->fall_behind_timeout && rsite->fall_behind_data) {
+ log_error("Defined fall_behind_timeout and"
+ " fall_behind_data for async replicator %s/%s.",
+ lv->name, rsite->name);
+ r = 0;
+ }
+ if (rsite->fall_behind_ios && rsite->fall_behind_data) {
+ log_error("Defined fall_behind_ios and"
+ " fall_behind_data for async replicator %s/%s.",
+ lv->name, rsite->name);
+ r = 0;
+ }
+ if (!rsite->fall_behind_ios &&
+ !rsite->fall_behind_data &&
+ !rsite->fall_behind_timeout) {
+ log_error("fall_behind_timeout,"
+ " fall_behind_ios and fall_behind_data are"
+ " undefined for async replicator %s/%s.",
+ lv->name, rsite->name);
+ r = 0;
+ }
+ }
+ dm_list_iterate_items(rsiteb, &lv->rsites) {
+ if (rsite == rsiteb)
+ break;
+ if (strcasecmp(rsite->name, rsiteb->name) == 0) {
+ log_error("Duplicate site name "
+ "%s detected for replicator %s.",
+ rsite->name, lv->name);
+ r = 0;
+ }
+ if ((rsite->vg_name && rsiteb->vg_name &&
+ strcasecmp(rsite->vg_name, rsiteb->vg_name) == 0) ||
+ (!rsite->vg_name && !rsiteb->vg_name)) {
+ log_error("Duplicate VG name "
+ "%s detected for replicator %s.",
+ (rsite->vg_name) ? rsite->vg_name : "<local>",
+ lv->name);
+ r = 0;
+ }
+ if (rsite->site_index == rsiteb->site_index) {
+ log_error("Duplicate site index %d detected "
+ "for replicator site %s/%s.",
+ rsite->site_index, lv->name,
+ rsite->name);
+ r = 0;
+ }
+ if (rsite->site_index > rseg->rsite_index_highest) {
+ log_error("Site index %d > %d (too high) "
+ "for replicator site %s/%s.",
+ rsite->site_index,
+ rseg->rsite_index_highest,
+ lv->name, rsite->name);
+ r = 0;
+ }
+ }
+
+ dm_list_iterate_items(rdev, &rsite->rdevices) {
+ dm_list_iterate_items(rdevb, &rsite->rdevices) {
+ if (rdev == rdevb)
+ break;
+ if (rdev->slog && (rdev->slog == rdevb->slog)) {
+ log_error("Duplicate sync log %s "
+ "detected for replicator %s.",
+ rdev->slog->name, lv->name);
+ r = 0;
+ }
+ if (strcasecmp(rdev->name, rdevb->name) == 0) {
+ log_error("Duplicate device name %s "
+ "detected for replicator %s.",
+ rdev->name, lv->name);
+ r = 0;
+ }
+ if (rdev->device_index == rdevb->device_index) {
+ log_error("Duplicate device index %"
+ PRId64 " detected for "
+ "replicator site %s/%s.",
+ rdev->device_index,
+ lv->name, rsite->name);
+ r = 0;
+ }
+ if (rdev->device_index > rseg->rdevice_index_highest) {
+ log_error("Device index %" PRIu64
+ " > %" PRIu64 " (too high) "
+ "for replicator site %s/%s.",
+ rdev->device_index,
+ rseg->rdevice_index_highest,
+ lv->name, rsite->name);
+ r = 0;
+ }
+ }
+ }
+ }
+
+ return r;
+}
+
+/**
+ * Is this segment part of active replicator
+ */
+int lv_is_active_replicator_dev(const struct logical_volume *lv)
+{
+ return ((lv->status & REPLICATOR) &&
+ lv->rdevice &&
+ lv->rdevice->rsite &&
+ lv->rdevice->rsite->state == REPLICATOR_STATE_ACTIVE);
+}
+
+/**
+ * Is this LV replicator control device
+ */
+int lv_is_replicator(const struct logical_volume *lv)
+{
+ return ((lv->status & REPLICATOR) &&
+ !dm_list_empty(&lv->segments) &&
+ seg_is_replicator(first_seg(lv)));
+}
+
+/**
+ * Is this LV replicator device
+ */
+int lv_is_replicator_dev(const struct logical_volume *lv)
+{
+ return ((lv->status & REPLICATOR) &&
+ !dm_list_empty(&lv->segments) &&
+ seg_is_replicator_dev(first_seg(lv)));
+}
+
+/**
+ * Is this LV replicated origin lv
+ */
+int lv_is_rimage(const struct logical_volume *lv)
+{
+ return (lv->rdevice && lv->rdevice->lv == lv);
+}
+
+/**
+ * Is this LV rlog
+ */
+int lv_is_rlog(const struct logical_volume *lv)
+{
+ return (lv->status & REPLICATOR_LOG);
+}
+
+/**
+ * Is this LV sync log
+ */
+int lv_is_slog(const struct logical_volume *lv)
+{
+ return (lv->rdevice && lv->rdevice->slog == lv);
+}
+
+/**
+ * Returns first replicator-dev in site in case the LV is replicator-dev,
+ * NULL otherwise
+ */
+struct logical_volume *first_replicator_dev(const struct logical_volume *lv)
+{
+ struct replicator_device *rdev;
+ struct replicator_site *rsite;
+
+ if (lv_is_replicator_dev(lv))
+ dm_list_iterate_items(rsite, &first_seg(lv)->replicator->rsites) {
+ dm_list_iterate_items(rdev, &rsite->rdevices)
+ return rdev->replicator_dev->lv;
+ break;
+ }
+
+ return NULL;
+}
+
+/**
+ * Add VG open parameters to sorted cmd_vg list.
+ *
+ * Maintain the alphabeticaly ordered list, avoid duplications.
+ *
+ * \return Returns newly created or already present cmd_vg entry,
+ * or NULL in error case.
+ */
+struct cmd_vg *cmd_vg_add(struct dm_pool *mem, struct dm_list *cmd_vgs,
+ const char *vg_name, const char *vgid,
+ uint32_t flags)
+{
+ struct cmd_vg *cvl, *ins;
+
+ if (!vg_name && !vgid) {
+ log_error("Either vg_name or vgid must be set.");
+ return NULL;
+ }
+
+ /* Is it already in the list ? */
+ if ((cvl = cmd_vg_lookup(cmd_vgs, vg_name, vgid)))
+ return cvl;
+
+ if (!(cvl = dm_pool_zalloc(mem, sizeof(*cvl)))) {
+ log_error("Allocation of cmd_vg failed.");
+ return NULL;
+ }
+
+ if (vg_name && !(cvl->vg_name = dm_pool_strdup(mem, vg_name))) {
+ dm_pool_free(mem, cvl);
+ log_error("Allocation of vg_name failed.");
+ return NULL;
+ }
+
+ if (vgid && !(cvl->vgid = dm_pool_strdup(mem, vgid))) {
+ dm_pool_free(mem, cvl);
+ log_error("Allocation of vgid failed.");
+ return NULL;
+ }
+
+ cvl->flags = flags;
+
+ if (vg_name)
+ dm_list_iterate_items(ins, cmd_vgs)
+ if (strcmp(vg_name, ins->vg_name) < 0) {
+ cmd_vgs = &ins->list; /* new position */
+ break;
+ }
+
+ dm_list_add(cmd_vgs, &cvl->list);
+
+ return cvl;
+}
+
+/**
+ * Find cmd_vg with given vg_name in cmd_vgs list.
+ *
+ * \param cmd_vgs List of cmd_vg entries.
+ *
+ * \param vg_name Name of VG to be found.
+
+ * \param vgid UUID of VG to be found.
+ *
+ * \return Returns cmd_vg entry if vg_name or vgid is found,
+ * NULL otherwise.
+ */
+struct cmd_vg *cmd_vg_lookup(struct dm_list *cmd_vgs,
+ const char *vg_name, const char *vgid)
+{
+ struct cmd_vg *cvl;
+
+ dm_list_iterate_items(cvl, cmd_vgs)
+ if ((vgid && cvl->vgid && !strcmp(vgid, cvl->vgid)) ||
+ (vg_name && cvl->vg_name && !strcmp(vg_name, cvl->vg_name)))
+ return cvl;
+ return NULL;
+}
+
+/**
+ * Read and lock multiple VGs stored in cmd_vgs list alphabeticaly.
+ * On the success list head pointer is set to VGs' cmd_vgs.
+ * (supports FAILED_INCONSISTENT)
+ *
+ * \param cmd_vg Contains list of cmd_vg entries.
+ *
+ * \return Returns 1 if all VG in cmd_vgs list are correctly
+ * openned and locked, 0 otherwise.
+ */
+int cmd_vg_read(struct cmd_context *cmd, struct dm_list *cmd_vgs)
+{
+ struct cmd_vg *cvl;
+
+ /* Iterate through alphabeticaly ordered cmd_vg list */
+ dm_list_iterate_items(cvl, cmd_vgs) {
+ cvl->vg = vg_read(cmd, cvl->vg_name, cvl->vgid, cvl->flags);
+ if (vg_read_error(cvl->vg)) {
+ log_debug("Failed to vg_read %s", cvl->vg_name);
+ return 0;
+ }
+ cvl->vg->cmd_vgs = cmd_vgs; /* Make it usable in VG */
+ }
+
+ return 1;
+}
+
+/**
+ * Release opened and locked VGs from list.
+ *
+ * \param cmd_vgs Contains list of cmd_vg entries.
+ */
+void free_cmd_vgs(struct dm_list *cmd_vgs)
+{
+ struct cmd_vg *cvl;
+
+ /* Backward iterate cmd_vg list */
+ dm_list_iterate_back_items(cvl, cmd_vgs) {
+ if (vg_read_error(cvl->vg))
+ free_vg(cvl->vg);
+ else
+ unlock_and_free_vg(cvl->vg->cmd, cvl->vg, cvl->vg_name);
+ cvl->vg = NULL;
+ }
+}
+
+/**
+ * Find all needed remote VGs for processing given LV.
+ * Missing VGs are added to VG's cmd_vg list and flag cmd_missing_vgs is set.
+ */
+int find_replicator_vgs(struct logical_volume *lv)
+{
+ struct replicator_site *rsite;
+ int ret = 1;
+
+ if (!lv_is_replicator_dev(lv))
+ return 1;
+
+ dm_list_iterate_items(rsite, &first_seg(lv)->replicator->rsites) {
+ if (!rsite->vg_name || !lv->vg->cmd_vgs ||
+ cmd_vg_lookup(lv->vg->cmd_vgs, rsite->vg_name, NULL))
+ continue;
+ ret = 0;
+ /* Using cmd memory pool for cmd_vg list allocation */
+ if (!cmd_vg_add(lv->vg->cmd->mem, lv->vg->cmd_vgs,
+ rsite->vg_name, NULL, 0)) {
+ lv->vg->cmd_missing_vgs = 0; /* do not retry */
+ stack;
+ break;
+ }
+
+ log_debug("VG: %s added as missing.", rsite->vg_name);
+ lv->vg->cmd_missing_vgs++;
+ }
+
+ return ret;
+}
+
+/**
+ * Read all remote VGs from lv's replicator sites.
+ * Function is used in activation context and needs all VGs already locked.
+ */
+int lv_read_replicator_vgs(struct logical_volume *lv)
+{
+ struct replicator_device *rdev;
+ struct replicator_site *rsite;
+ struct volume_group *vg;
+
+ if (!lv_is_replicator_dev(lv))
+ return 1;
+
+ dm_list_iterate_items(rsite, &first_seg(lv)->replicator->rsites) {
+ if (!rsite->vg_name)
+ continue;
+ vg = vg_read(lv->vg->cmd, rsite->vg_name, 0, 0); // READ_WITHOUT_LOCK
+ if (vg_read_error(vg)) {
+ log_error("Unable to read volume group %s",
+ rsite->vg_name);
+ goto bad;
+ }
+ rsite->vg = vg;
+ /* FIXME: handling missing LVs needs to be better */
+ dm_list_iterate_items(rdev, &rsite->rdevices)
+ if (!(rdev->lv = find_lv(vg, rdev->name))) {
+ log_error("Unable to find %s in volume group %s",
+ rdev->name, rsite->vg_name);
+ goto bad;
+ }
+ }
+
+ return 1;
+bad:
+ lv_release_replicator_vgs(lv);
+ return 0;
+}
+
+/**
+ * Release all VG resources taken by lv's replicator sites.
+ * Function is used in activation context and needs all VGs already locked.
+ */
+void lv_release_replicator_vgs(struct logical_volume *lv)
+{
+ struct replicator_site *rsite;
+
+ if (!lv_is_replicator_dev(lv))
+ return;
+
+ dm_list_iterate_back_items(rsite, &first_seg(lv)->replicator->rsites)
+ if (rsite->vg_name && rsite->vg) {
+ free_vg(rsite->vg);
+ rsite->vg = NULL;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "segtype.h"
+
+struct segment_type *get_segtype_from_string(struct cmd_context *cmd,
+ const char *str)
+{
+ struct segment_type *segtype;
+
+ dm_list_iterate_items(segtype, &cmd->segtypes) {
+ if (!strcmp(segtype->name, str))
+ return segtype;
+ }
+
+ if (!(segtype = init_unknown_segtype(cmd, str)))
+ return_NULL;
+
+ segtype->library = NULL;
+ dm_list_add(&cmd->segtypes, &segtype->list);
+ log_warn("WARNING: Unrecognised segment type %s", str);
+ return segtype;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _SEGTYPES_H
+#define _SEGTYPES_H
+
+#include "metadata-exported.h"
+
+struct segtype_handler;
+struct cmd_context;
+struct config_tree;
+struct lv_segment;
+struct formatter;
+struct config_node;
+struct dev_manager;
+
+/* Feature flags */
+#define SEG_CAN_SPLIT 0x00000001U
+#define SEG_AREAS_STRIPED 0x00000002U
+#define SEG_AREAS_MIRRORED 0x00000004U
+#define SEG_SNAPSHOT 0x00000008U
+#define SEG_FORMAT1_SUPPORT 0x00000010U
+#define SEG_VIRTUAL 0x00000020U
+#define SEG_CANNOT_BE_ZEROED 0x00000040U
+#define SEG_MONITORED 0x00000080U
+#define SEG_REPLICATOR 0x00000100U
+#define SEG_REPLICATOR_DEV 0x00000200U
+#define SEG_UNKNOWN 0x80000000U
+
+#define seg_is_mirrored(seg) ((seg)->segtype->flags & SEG_AREAS_MIRRORED ? 1 : 0)
+#define seg_is_replicator(seg) ((seg)->segtype->flags & SEG_REPLICATOR ? 1 : 0)
+#define seg_is_replicator_dev(seg) ((seg)->segtype->flags & SEG_REPLICATOR_DEV ? 1 : 0)
+#define seg_is_striped(seg) ((seg)->segtype->flags & SEG_AREAS_STRIPED ? 1 : 0)
+#define seg_is_snapshot(seg) ((seg)->segtype->flags & SEG_SNAPSHOT ? 1 : 0)
+#define seg_is_virtual(seg) ((seg)->segtype->flags & SEG_VIRTUAL ? 1 : 0)
+#define seg_can_split(seg) ((seg)->segtype->flags & SEG_CAN_SPLIT ? 1 : 0)
+#define seg_cannot_be_zeroed(seg) ((seg)->segtype->flags & SEG_CANNOT_BE_ZEROED ? 1 : 0)
+#define seg_monitored(seg) ((seg)->segtype->flags & SEG_MONITORED ? 1 : 0)
+#define seg_unknown(seg) ((seg)->segtype->flags & SEG_UNKNOWN ? 1 : 0)
+
+#define segtype_is_striped(segtype) ((segtype)->flags & SEG_AREAS_STRIPED ? 1 : 0)
+#define segtype_is_mirrored(segtype) ((segtype)->flags & SEG_AREAS_MIRRORED ? 1 : 0)
+#define segtype_is_virtual(segtype) ((segtype)->flags & SEG_VIRTUAL ? 1 : 0)
+
+struct segment_type {
+ struct dm_list list; /* Internal */
+ struct cmd_context *cmd; /* lvm_register_segtype() sets this. */
+ uint32_t flags;
+ struct segtype_handler *ops;
+ const char *name;
+ void *library; /* lvm_register_segtype() sets this. */
+ void *private; /* For the segtype handler to use. */
+};
+
+struct segtype_handler {
+ const char *(*name) (const struct lv_segment * seg);
+ const char *(*target_name) (const struct lv_segment * seg);
+ void (*display) (const struct lv_segment * seg);
+ int (*text_export) (const struct lv_segment * seg,
+ struct formatter * f);
+ int (*text_import_area_count) (const struct config_node * sn,
+ uint32_t *area_count);
+ int (*text_import) (struct lv_segment * seg,
+ const struct config_node * sn,
+ struct dm_hash_table * pv_hash);
+ int (*merge_segments) (struct lv_segment * seg1,
+ struct lv_segment * seg2);
+ int (*add_target_line) (struct dev_manager *dm, struct dm_pool *mem,
+ struct cmd_context *cmd, void **target_state,
+ struct lv_segment *seg,
+ struct dm_tree_node *node, uint64_t len,
+ uint32_t *pvmove_mirror_count);
+ int (*target_status_compatible) (const char *type);
+ int (*check_transient_status) (struct lv_segment *seg, char *params);
+ int (*target_percent) (void **target_state,
+ percent_t *percent,
+ struct dm_pool * mem,
+ struct cmd_context *cmd,
+ struct lv_segment *seg, char *params,
+ uint64_t *total_numerator,
+ uint64_t *total_denominator);
+ int (*target_present) (struct cmd_context *cmd,
+ const struct lv_segment *seg,
+ unsigned *attributes);
+ int (*modules_needed) (struct dm_pool *mem,
+ const struct lv_segment *seg,
+ struct dm_list *modules);
+ void (*destroy) (struct segment_type * segtype);
+ int (*target_monitored) (struct lv_segment *seg, int *pending);
+ int (*target_monitor_events) (struct lv_segment *seg, int events);
+ int (*target_unmonitor_events) (struct lv_segment *seg, int events);
+};
+
+struct segment_type *get_segtype_from_string(struct cmd_context *cmd,
+ const char *str);
+
+struct segtype_library;
+int lvm_register_segtype(struct segtype_library *seglib,
+ struct segment_type *segtype);
+
+struct segment_type *init_striped_segtype(struct cmd_context *cmd);
+struct segment_type *init_zero_segtype(struct cmd_context *cmd);
+struct segment_type *init_error_segtype(struct cmd_context *cmd);
+struct segment_type *init_free_segtype(struct cmd_context *cmd);
+struct segment_type *init_unknown_segtype(struct cmd_context *cmd, const char *name);
+
+#ifdef REPLICATOR_INTERNAL
+int init_replicator_segtype(struct segtype_library *seglib);
+#endif
+
+#ifdef SNAPSHOT_INTERNAL
+struct segment_type *init_snapshot_segtype(struct cmd_context *cmd);
+#endif
+
+#ifdef MIRRORED_INTERNAL
+struct segment_type *init_mirrored_segtype(struct cmd_context *cmd);
+#endif
+
+#ifdef CRYPT_INTERNAL
+struct segment_type *init_crypt_segtype(struct cmd_context *cmd);
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "locking.h"
+#include "toolcontext.h"
+#include "lv_alloc.h"
+#include "activate.h"
+
+int lv_is_origin(const struct logical_volume *lv)
+{
+ return lv->origin_count ? 1 : 0;
+}
+
+int lv_is_cow(const struct logical_volume *lv)
+{
+ return (!lv_is_origin(lv) && lv->snapshot) ? 1 : 0;
+}
+
+int lv_is_visible(const struct logical_volume *lv)
+{
+ if (lv->status & SNAPSHOT)
+ return 0;
+
+ if (lv_is_cow(lv)) {
+ if (lv_is_virtual_origin(origin_from_cow(lv)))
+ return 1;
+
+ if (lv_is_merging_cow(lv))
+ return 0;
+
+ return lv_is_visible(origin_from_cow(lv));
+ }
+
+ return lv->status & VISIBLE_LV ? 1 : 0;
+}
+
+int lv_is_virtual_origin(const struct logical_volume *lv)
+{
+ return (lv->status & VIRTUAL_ORIGIN) ? 1 : 0;
+}
+
+int lv_is_merging_origin(const struct logical_volume *origin)
+{
+ return (origin->status & MERGING) ? 1 : 0;
+}
+
+struct lv_segment *find_merging_cow(const struct logical_volume *origin)
+{
+ if (!lv_is_merging_origin(origin))
+ return NULL;
+
+ return find_cow(origin);
+}
+
+int lv_is_merging_cow(const struct logical_volume *snapshot)
+{
+ /* checks lv_segment's status to see if cow is merging */
+ return (find_cow(snapshot)->status & MERGING) ? 1 : 0;
+}
+
+/* Given a cow LV, return the snapshot lv_segment that uses it */
+struct lv_segment *find_cow(const struct logical_volume *lv)
+{
+ return lv->snapshot;
+}
+
+/* Given a cow LV, return its origin */
+struct logical_volume *origin_from_cow(const struct logical_volume *lv)
+{
+ return lv->snapshot->origin;
+}
+
+void init_snapshot_seg(struct lv_segment *seg, struct logical_volume *origin,
+ struct logical_volume *cow, uint32_t chunk_size, int merge)
+{
+ seg->chunk_size = chunk_size;
+ seg->origin = origin;
+ seg->cow = cow;
+
+ lv_set_hidden(cow);
+
+ cow->snapshot = seg;
+
+ origin->origin_count++;
+
+ /* FIXME Assumes an invisible origin belongs to a sparse device */
+ if (!lv_is_visible(origin))
+ origin->status |= VIRTUAL_ORIGIN;
+
+ seg->lv->status |= (SNAPSHOT | VIRTUAL);
+ if (merge)
+ init_snapshot_merge(seg, origin);
+
+ dm_list_add(&origin->snapshot_segs, &seg->origin_list);
+}
+
+void init_snapshot_merge(struct lv_segment *cow_seg,
+ struct logical_volume *origin)
+{
+ /*
+ * Even though lv_is_visible(cow_seg->lv) returns 0,
+ * the cow_seg->lv (name: snapshotX) is _not_ hidden;
+ * this is part of the lvm2 snapshot fiction. Must
+ * clear VISIBLE_LV directly (lv_set_visible can't)
+ * - cow_seg->lv->status is used to control whether 'lv'
+ * (with user provided snapshot LV name) is visible
+ * - this also enables vg_validate() to succeed with
+ * merge metadata (cow_seg->lv is now "internal")
+ */
+ cow_seg->lv->status &= ~VISIBLE_LV;
+ cow_seg->status |= MERGING;
+ origin->snapshot = cow_seg;
+ origin->status |= MERGING;
+}
+
+void clear_snapshot_merge(struct logical_volume *origin)
+{
+ /* clear merge attributes */
+ origin->snapshot->status &= ~MERGING;
+ origin->snapshot = NULL;
+ origin->status &= ~MERGING;
+}
+
+int vg_add_snapshot(struct logical_volume *origin,
+ struct logical_volume *cow, union lvid *lvid,
+ uint32_t extent_count, uint32_t chunk_size)
+{
+ struct logical_volume *snap;
+ struct lv_segment *seg;
+
+ /*
+ * Is the cow device already being used ?
+ */
+ if (lv_is_cow(cow)) {
+ log_error("'%s' is already in use as a snapshot.", cow->name);
+ return 0;
+ }
+
+ if (cow == origin) {
+ log_error("Snapshot and origin LVs must differ.");
+ return 0;
+ }
+
+ if (!(snap = lv_create_empty("snapshot%d",
+ lvid, LVM_READ | LVM_WRITE | VISIBLE_LV,
+ ALLOC_INHERIT, origin->vg)))
+ return_0;
+
+ snap->le_count = extent_count;
+
+ if (!(seg = alloc_snapshot_seg(snap, 0, 0)))
+ return_0;
+
+ init_snapshot_seg(seg, origin, cow, chunk_size, 0);
+
+ return 1;
+}
+
+int vg_remove_snapshot(struct logical_volume *cow)
+{
+ int preload_origin = 0;
+ struct logical_volume *origin = origin_from_cow(cow);
+
+ dm_list_del(&cow->snapshot->origin_list);
+ origin->origin_count--;
+
+ if (find_merging_cow(origin) == find_cow(cow)) {
+ clear_snapshot_merge(origin);
+ /*
+ * preload origin IFF "snapshot-merge" target is active
+ * - IMPORTANT: avoids preload if onactivate merge is pending
+ */
+ if (lv_has_target_type(origin->vg->cmd->mem, origin, NULL,
+ "snapshot-merge")) {
+ /*
+ * preload origin to:
+ * - allow proper release of -cow
+ * - avoid allocations with other devices suspended
+ * when transitioning from "snapshot-merge" to
+ * "snapshot-origin after a merge completes.
+ */
+ preload_origin = 1;
+ }
+ }
+
+ if (!lv_remove(cow->snapshot->lv)) {
+ log_error("Failed to remove internal snapshot LV %s",
+ cow->snapshot->lv->name);
+ return 0;
+ }
+
+ cow->snapshot = NULL;
+ lv_set_visible(cow);
+
+ if (preload_origin) {
+ if (!vg_write(origin->vg))
+ return_0;
+ if (!suspend_lv(origin->vg->cmd, origin)) {
+ log_error("Failed to refresh %s without snapshot.",
+ origin->name);
+ return 0;
+ }
+ if (!vg_commit(origin->vg))
+ return_0;
+ if (!resume_lv(origin->vg->cmd, origin)) {
+ log_error("Failed to resume %s.", origin->name);
+ return 0;
+ }
+ }
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "display.h"
+#include "activate.h"
+
+char *vg_fmt_dup(const struct volume_group *vg)
+{
+ if (!vg->fid || !vg->fid->fmt)
+ return NULL;
+ return dm_pool_strdup(vg->vgmem, vg->fid->fmt->name);
+}
+
+char *vg_name_dup(const struct volume_group *vg)
+{
+ return dm_pool_strdup(vg->vgmem, vg->name);
+}
+
+char *vg_system_id_dup(const struct volume_group *vg)
+{
+ return dm_pool_strdup(vg->vgmem, vg->system_id);
+}
+
+char *vg_uuid_dup(const struct volume_group *vg)
+{
+ return id_format_and_copy(vg->vgmem, &vg->id);
+}
+
+char *vg_tags_dup(const struct volume_group *vg)
+{
+ return tags_format_and_copy(vg->vgmem, &vg->tags);
+}
+
+uint32_t vg_seqno(const struct volume_group *vg)
+{
+ return vg->seqno;
+}
+
+uint64_t vg_status(const struct volume_group *vg)
+{
+ return vg->status;
+}
+
+uint64_t vg_size(const struct volume_group *vg)
+{
+ return (uint64_t) vg->extent_count * vg->extent_size;
+}
+
+uint64_t vg_free(const struct volume_group *vg)
+{
+ return (uint64_t) vg->free_count * vg->extent_size;
+}
+
+uint64_t vg_extent_size(const struct volume_group *vg)
+{
+ return (uint64_t) vg->extent_size;
+}
+
+uint64_t vg_extent_count(const struct volume_group *vg)
+{
+ return (uint64_t) vg->extent_count;
+}
+
+uint64_t vg_free_count(const struct volume_group *vg)
+{
+ return (uint64_t) vg->free_count;
+}
+
+uint64_t vg_pv_count(const struct volume_group *vg)
+{
+ return (uint64_t) vg->pv_count;
+}
+
+uint64_t vg_max_pv(const struct volume_group *vg)
+{
+ return (uint64_t) vg->max_pv;
+}
+
+uint64_t vg_max_lv(const struct volume_group *vg)
+{
+ return (uint64_t) vg->max_lv;
+}
+
+unsigned snapshot_count(const struct volume_group *vg)
+{
+ struct lv_list *lvl;
+ unsigned num_snapshots = 0;
+
+ dm_list_iterate_items(lvl, &vg->lvs)
+ if (lv_is_cow(lvl->lv))
+ num_snapshots++;
+
+ return num_snapshots;
+}
+
+unsigned vg_visible_lvs(const struct volume_group *vg)
+{
+ struct lv_list *lvl;
+ unsigned lv_count = 0;
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (lv_is_visible(lvl->lv))
+ lv_count++;
+ }
+
+ return lv_count;
+}
+
+uint32_t vg_mda_count(const struct volume_group *vg)
+{
+ return dm_list_size(&vg->fid->metadata_areas_in_use) +
+ dm_list_size(&vg->fid->metadata_areas_ignored);
+}
+
+uint32_t vg_mda_used_count(const struct volume_group *vg)
+{
+ uint32_t used_count = 0;
+ struct metadata_area *mda;
+
+ /*
+ * Ignored mdas could be on either list - the reason being the state
+ * may have changed from ignored to un-ignored and we need to write
+ * the state to disk.
+ */
+ dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use)
+ if (!mda_is_ignored(mda))
+ used_count++;
+
+ return used_count;
+}
+
+uint32_t vg_mda_copies(const struct volume_group *vg)
+{
+ return vg->mda_copies;
+}
+
+uint64_t vg_mda_size(const struct volume_group *vg)
+{
+ return find_min_mda_size(&vg->fid->metadata_areas_in_use);
+}
+
+uint64_t vg_mda_free(const struct volume_group *vg)
+{
+ uint64_t freespace = UINT64_MAX, mda_free;
+ struct metadata_area *mda;
+
+ dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) {
+ if (!mda->ops->mda_free_sectors)
+ continue;
+ mda_free = mda->ops->mda_free_sectors(mda);
+ if (mda_free < freespace)
+ freespace = mda_free;
+ }
+
+ if (freespace == UINT64_MAX)
+ freespace = UINT64_C(0);
+ return freespace;
+}
+
+int vg_set_mda_copies(struct volume_group *vg, uint32_t mda_copies)
+{
+ vg->mda_copies = mda_copies;
+
+ /* FIXME Use log_verbose when this is due to specific cmdline request. */
+ log_debug("Setting mda_copies to %"PRIu32" for VG %s",
+ mda_copies, vg->name);
+
+ return 1;
+}
+
+static int _recalc_extents(uint32_t *extents, const char *desc1,
+ const char *desc2, uint32_t old_size,
+ uint32_t new_size)
+{
+ uint64_t size = (uint64_t) old_size * (*extents);
+
+ if (size % new_size) {
+ log_error("New size %" PRIu64 " for %s%s not an exact number "
+ "of new extents.", size, desc1, desc2);
+ return 0;
+ }
+
+ size /= new_size;
+
+ if (size > UINT32_MAX) {
+ log_error("New extent count %" PRIu64 " for %s%s exceeds "
+ "32 bits.", size, desc1, desc2);
+ return 0;
+ }
+
+ *extents = (uint32_t) size;
+
+ return 1;
+}
+
+int vg_set_extent_size(struct volume_group *vg, uint32_t new_size)
+{
+ uint32_t old_size = vg->extent_size;
+ struct pv_list *pvl;
+ struct lv_list *lvl;
+ struct physical_volume *pv;
+ struct logical_volume *lv;
+ struct lv_segment *seg;
+ struct pv_segment *pvseg;
+ uint32_t s;
+
+ if (!vg_is_resizeable(vg)) {
+ log_error("Volume group \"%s\" must be resizeable "
+ "to change PE size", vg->name);
+ return 0;
+ }
+
+ if (!new_size) {
+ log_error("Physical extent size may not be zero");
+ return 0;
+ }
+
+ if (new_size == vg->extent_size)
+ return 1;
+
+ if (new_size & (new_size - 1)) {
+ log_error("Physical extent size must be a power of 2.");
+ return 0;
+ }
+
+ if (new_size > vg->extent_size) {
+ if ((uint64_t) vg_size(vg) % new_size) {
+ /* FIXME Adjust used PV sizes instead */
+ log_error("New extent size is not a perfect fit");
+ return 0;
+ }
+ }
+
+ vg->extent_size = new_size;
+
+ if (vg->fid->fmt->ops->vg_setup &&
+ !vg->fid->fmt->ops->vg_setup(vg->fid, vg))
+ return_0;
+
+ if (!_recalc_extents(&vg->extent_count, vg->name, "", old_size,
+ new_size))
+ return_0;
+
+ if (!_recalc_extents(&vg->free_count, vg->name, " free space",
+ old_size, new_size))
+ return_0;
+
+ /* foreach PV */
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ pv = pvl->pv;
+
+ pv->pe_size = new_size;
+ if (!_recalc_extents(&pv->pe_count, pv_dev_name(pv), "",
+ old_size, new_size))
+ return_0;
+
+ if (!_recalc_extents(&pv->pe_alloc_count, pv_dev_name(pv),
+ " allocated space", old_size, new_size))
+ return_0;
+
+ /* foreach free PV Segment */
+ dm_list_iterate_items(pvseg, &pv->segments) {
+ if (pvseg_is_allocated(pvseg))
+ continue;
+
+ if (!_recalc_extents(&pvseg->pe, pv_dev_name(pv),
+ " PV segment start", old_size,
+ new_size))
+ return_0;
+ if (!_recalc_extents(&pvseg->len, pv_dev_name(pv),
+ " PV segment length", old_size,
+ new_size))
+ return_0;
+ }
+ }
+
+ /* foreach LV */
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ lv = lvl->lv;
+
+ if (!_recalc_extents(&lv->le_count, lv->name, "", old_size,
+ new_size))
+ return_0;
+
+ dm_list_iterate_items(seg, &lv->segments) {
+ if (!_recalc_extents(&seg->le, lv->name,
+ " segment start", old_size,
+ new_size))
+ return_0;
+
+ if (!_recalc_extents(&seg->len, lv->name,
+ " segment length", old_size,
+ new_size))
+ return_0;
+
+ if (!_recalc_extents(&seg->area_len, lv->name,
+ " area length", old_size,
+ new_size))
+ return_0;
+
+ if (!_recalc_extents(&seg->extents_copied, lv->name,
+ " extents moved", old_size,
+ new_size))
+ return_0;
+
+ /* foreach area */
+ for (s = 0; s < seg->area_count; s++) {
+ switch (seg_type(seg, s)) {
+ case AREA_PV:
+ if (!_recalc_extents
+ (&seg_pe(seg, s),
+ lv->name,
+ " pvseg start", old_size,
+ new_size))
+ return_0;
+ if (!_recalc_extents
+ (&seg_pvseg(seg, s)->len,
+ lv->name,
+ " pvseg length", old_size,
+ new_size))
+ return_0;
+ break;
+ case AREA_LV:
+ if (!_recalc_extents
+ (&seg_le(seg, s), lv->name,
+ " area start", old_size,
+ new_size))
+ return_0;
+ break;
+ case AREA_UNASSIGNED:
+ log_error("Unassigned area %u found in "
+ "segment", s);
+ return 0;
+ }
+ }
+ }
+
+ }
+
+ return 1;
+}
+
+int vg_set_max_lv(struct volume_group *vg, uint32_t max_lv)
+{
+ if (!vg_is_resizeable(vg)) {
+ log_error("Volume group \"%s\" must be resizeable "
+ "to change MaxLogicalVolume", vg->name);
+ return 0;
+ }
+
+ if (!(vg->fid->fmt->features & FMT_UNLIMITED_VOLS)) {
+ if (!max_lv)
+ max_lv = 255;
+ else if (max_lv > 255) {
+ log_error("MaxLogicalVolume limit is 255");
+ return 0;
+ }
+ }
+
+ if (max_lv && max_lv < vg_visible_lvs(vg)) {
+ log_error("MaxLogicalVolume is less than the current number "
+ "%d of LVs for %s", vg_visible_lvs(vg),
+ vg->name);
+ return 0;
+ }
+ vg->max_lv = max_lv;
+
+ return 1;
+}
+
+int vg_set_max_pv(struct volume_group *vg, uint32_t max_pv)
+{
+ if (!vg_is_resizeable(vg)) {
+ log_error("Volume group \"%s\" must be resizeable "
+ "to change MaxPhysicalVolumes", vg->name);
+ return 0;
+ }
+
+ if (!(vg->fid->fmt->features & FMT_UNLIMITED_VOLS)) {
+ if (!max_pv)
+ max_pv = 255;
+ else if (max_pv > 255) {
+ log_error("MaxPhysicalVolume limit is 255");
+ return 0;
+ }
+ }
+
+ if (max_pv && max_pv < vg->pv_count) {
+ log_error("MaxPhysicalVolumes is less than the current number "
+ "%d of PVs for \"%s\"", vg->pv_count,
+ vg->name);
+ return 0;
+ }
+ vg->max_pv = max_pv;
+ return 1;
+}
+
+int vg_set_alloc_policy(struct volume_group *vg, alloc_policy_t alloc)
+{
+ if (alloc == ALLOC_INHERIT) {
+ log_error("Volume Group allocation policy cannot inherit "
+ "from anything");
+ return 0;
+ }
+
+ if (alloc == vg->alloc)
+ return 1;
+
+ vg->alloc = alloc;
+ return 1;
+}
+
+int vg_set_clustered(struct volume_group *vg, int clustered)
+{
+ struct lv_list *lvl;
+
+ /*
+ * We do not currently support switching the cluster attribute
+ * on active mirrors or snapshots.
+ */
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (lv_is_mirrored(lvl->lv) && lv_is_active(lvl->lv)) {
+ log_error("Mirror logical volumes must be inactive "
+ "when changing the cluster attribute.");
+ return 0;
+ }
+
+ if (clustered) {
+ if (lv_is_origin(lvl->lv) || lv_is_cow(lvl->lv)) {
+ log_error("Volume group %s contains snapshots "
+ "that are not yet supported.",
+ vg->name);
+ return 0;
+ }
+ }
+
+ if ((lv_is_origin(lvl->lv) || lv_is_cow(lvl->lv)) &&
+ lv_is_active(lvl->lv)) {
+ log_error("Snapshot logical volumes must be inactive "
+ "when changing the cluster attribute.");
+ return 0;
+ }
+ }
+
+ if (clustered)
+ vg->status |= CLUSTERED;
+ else
+ vg->status &= ~CLUSTERED;
+ return 1;
+}
+
+char *vg_attr_dup(struct dm_pool *mem, const struct volume_group *vg)
+{
+ char *repstr;
+
+ if (!(repstr = dm_pool_zalloc(mem, 7))) {
+ log_error("dm_pool_alloc failed");
+ return NULL;
+ }
+
+ repstr[0] = (vg->status & LVM_WRITE) ? 'w' : 'r';
+ repstr[1] = (vg_is_resizeable(vg)) ? 'z' : '-';
+ repstr[2] = (vg_is_exported(vg)) ? 'x' : '-';
+ repstr[3] = (vg_missing_pv_count(vg)) ? 'p' : '-';
+ repstr[4] = alloc_policy_char(vg->alloc);
+ repstr[5] = (vg_is_clustered(vg)) ? 'c' : '-';
+ return repstr;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _LVM_VG_H
+#define _LVM_VG_H
+
+struct cmd_context;
+struct dm_pool;
+struct format_instance;
+struct dm_list;
+struct id;
+
+typedef enum {
+ ALLOC_INVALID,
+ ALLOC_CONTIGUOUS,
+ ALLOC_CLING,
+ ALLOC_CLING_BY_TAGS, /* Internal - never written or displayed. */
+ ALLOC_NORMAL,
+ ALLOC_ANYWHERE,
+ ALLOC_INHERIT
+} alloc_policy_t;
+
+struct volume_group {
+ struct cmd_context *cmd;
+ struct dm_pool *vgmem;
+ struct format_instance *fid;
+ struct dm_list *cmd_vgs;/* List of wanted/locked and opened VGs */
+ uint32_t cmd_missing_vgs;/* Flag marks missing VG */
+ uint32_t seqno; /* Metadata sequence number */
+
+ alloc_policy_t alloc;
+ uint64_t status;
+
+ struct id id;
+ const char *name;
+ const char *old_name; /* Set during vgrename and vgcfgrestore */
+ char *system_id;
+
+ uint32_t extent_size;
+ uint32_t extent_count;
+ uint32_t free_count;
+
+ uint32_t max_lv;
+ uint32_t max_pv;
+
+ /* physical volumes */
+ uint32_t pv_count;
+ struct dm_list pvs;
+
+ /*
+ * logical volumes
+ * The following relationship should always hold:
+ * dm_list_size(lvs) = user visible lv_count + snapshot_count + other invisible LVs
+ *
+ * Snapshots consist of 2 instances of "struct logical_volume":
+ * - cow (lv_name is visible to the user)
+ * - snapshot (lv_name is 'snapshotN')
+ *
+ * Mirrors consist of multiple instances of "struct logical_volume":
+ * - one for the mirror log
+ * - one for each mirror leg
+ * - one for the user-visible mirror LV
+ */
+ struct dm_list lvs;
+
+ struct dm_list tags;
+
+ /*
+ * FIXME: Move the next fields into a different struct?
+ */
+
+ /*
+ * List of removed physical volumes by pvreduce.
+ * They have to get cleared on vg_commit.
+ */
+ struct dm_list removed_pvs;
+ uint32_t open_mode; /* FIXME: read or write - check lock type? */
+
+ /*
+ * Store result of the last vg_read().
+ * 0 for success else appropriate FAILURE_* bits set.
+ */
+ uint32_t read_status;
+ uint32_t mda_copies; /* target number of mdas for this VG */
+};
+
+char *vg_fmt_dup(const struct volume_group *vg);
+char *vg_name_dup(const struct volume_group *vg);
+char *vg_system_id_dup(const struct volume_group *vg);
+uint32_t vg_seqno(const struct volume_group *vg);
+uint64_t vg_status(const struct volume_group *vg);
+int vg_set_alloc_policy(struct volume_group *vg, alloc_policy_t alloc);
+int vg_set_clustered(struct volume_group *vg, int clustered);
+uint64_t vg_size(const struct volume_group *vg);
+uint64_t vg_free(const struct volume_group *vg);
+uint64_t vg_extent_size(const struct volume_group *vg);
+int vg_set_extent_size(struct volume_group *vg, uint32_t new_extent_size);
+uint64_t vg_extent_count(const struct volume_group *vg);
+uint64_t vg_free_count(const struct volume_group *vg);
+uint64_t vg_pv_count(const struct volume_group *vg);
+uint64_t vg_max_pv(const struct volume_group *vg);
+int vg_set_max_pv(struct volume_group *vg, uint32_t max_pv);
+uint64_t vg_max_lv(const struct volume_group *vg);
+int vg_set_max_lv(struct volume_group *vg, uint32_t max_lv);
+uint32_t vg_mda_count(const struct volume_group *vg);
+uint32_t vg_mda_used_count(const struct volume_group *vg);
+uint32_t vg_mda_copies(const struct volume_group *vg);
+int vg_set_mda_copies(struct volume_group *vg, uint32_t mda_copies);
+/*
+ * Returns visible LV count - number of LVs from user perspective
+ */
+unsigned vg_visible_lvs(const struct volume_group *vg);
+/*
+ * Count snapshot LVs.
+ */
+unsigned snapshot_count(const struct volume_group *vg);
+
+uint64_t vg_mda_size(const struct volume_group *vg);
+uint64_t vg_mda_free(const struct volume_group *vg);
+char *vg_attr_dup(struct dm_pool *mem, const struct volume_group *vg);
+char *vg_uuid_dup(const struct volume_group *vg);
+char *vg_tags_dup(const struct volume_group *vg);
+
+#endif /* _LVM_VG_H */
--- /dev/null
+init_segtype
--- /dev/null
+#
+# Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES = mirrored.c
+
+LIB_SHARED = liblvm2mirror.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+install: install_lvm2_plugin
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "metadata.h"
+#include "segtype.h"
+#include "display.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "defaults.h"
+#include "lvm-string.h"
+#include "targets.h"
+#include "activate.h"
+#include "sharedlib.h"
+#include "str_list.h"
+
+#include <sys/utsname.h>
+
+static int _block_on_error_available = 0;
+static unsigned _mirror_attributes = 0;
+
+enum {
+ MIRR_DISABLED,
+ MIRR_RUNNING,
+ MIRR_COMPLETED
+};
+
+struct mirror_state {
+ uint32_t default_region_size;
+};
+
+static const char *_mirrored_name(const struct lv_segment *seg)
+{
+ return seg->segtype->name;
+}
+
+static void _mirrored_display(const struct lv_segment *seg)
+{
+ const char *size;
+ uint32_t s;
+
+ log_print(" Mirrors\t\t%u", seg->area_count);
+ log_print(" Mirror size\t\t%u", seg->area_len);
+ if (seg->log_lv)
+ log_print(" Mirror log volume\t%s", seg->log_lv->name);
+
+ if (seg->region_size) {
+ size = display_size(seg->lv->vg->cmd,
+ (uint64_t) seg->region_size);
+ log_print(" Mirror region size\t%s", size);
+ }
+
+ log_print(" Mirror original:");
+ display_stripe(seg, 0, " ");
+ log_print(" Mirror destinations:");
+ for (s = 1; s < seg->area_count; s++)
+ display_stripe(seg, s, " ");
+ log_print(" ");
+}
+
+static int _mirrored_text_import_area_count(const struct config_node *sn, uint32_t *area_count)
+{
+ if (!get_config_uint32(sn, "mirror_count", area_count)) {
+ log_error("Couldn't read 'mirror_count' for "
+ "segment '%s'.", config_parent_name(sn));
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _mirrored_text_import(struct lv_segment *seg, const struct config_node *sn,
+ struct dm_hash_table *pv_hash)
+{
+ const struct config_node *cn;
+ const char *logname = NULL;
+
+ if (find_config_node(sn, "extents_moved")) {
+ if (get_config_uint32(sn, "extents_moved",
+ &seg->extents_copied))
+ seg->status |= PVMOVE;
+ else {
+ log_error("Couldn't read 'extents_moved' for "
+ "segment %s of logical volume %s.",
+ config_parent_name(sn), seg->lv->name);
+ return 0;
+ }
+ }
+
+ if (find_config_node(sn, "region_size")) {
+ if (!get_config_uint32(sn, "region_size",
+ &seg->region_size)) {
+ log_error("Couldn't read 'region_size' for "
+ "segment %s of logical volume %s.",
+ config_parent_name(sn), seg->lv->name);
+ return 0;
+ }
+ }
+
+ if ((cn = find_config_node(sn, "mirror_log"))) {
+ if (!cn->v || !cn->v->v.str) {
+ log_error("Mirror log type must be a string.");
+ return 0;
+ }
+ logname = cn->v->v.str;
+ if (!(seg->log_lv = find_lv(seg->lv->vg, logname))) {
+ log_error("Unrecognised mirror log in "
+ "segment %s of logical volume %s.",
+ config_parent_name(sn), seg->lv->name);
+ return 0;
+ }
+ seg->log_lv->status |= MIRROR_LOG;
+ }
+
+ if (logname && !seg->region_size) {
+ log_error("Missing region size for mirror log for "
+ "segment %s of logical volume %s.",
+ config_parent_name(sn), seg->lv->name);
+ return 0;
+ }
+
+ if (!(cn = find_config_node(sn, "mirrors"))) {
+ log_error("Couldn't find mirrors array for "
+ "segment %s of logical volume %s.",
+ config_parent_name(sn), seg->lv->name);
+ return 0;
+ }
+
+ return text_import_areas(seg, sn, cn, pv_hash, MIRROR_IMAGE);
+}
+
+static int _mirrored_text_export(const struct lv_segment *seg, struct formatter *f)
+{
+ outf(f, "mirror_count = %u", seg->area_count);
+ if (seg->status & PVMOVE)
+ outsize(f, (uint64_t) seg->extents_copied * seg->lv->vg->extent_size,
+ "extents_moved = %" PRIu32, seg->extents_copied);
+ if (seg->log_lv)
+ outf(f, "mirror_log = \"%s\"", seg->log_lv->name);
+ if (seg->region_size)
+ outf(f, "region_size = %" PRIu32, seg->region_size);
+
+ return out_areas(f, seg, "mirror");
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static struct mirror_state *_mirrored_init_target(struct dm_pool *mem,
+ struct cmd_context *cmd)
+{
+ struct mirror_state *mirr_state;
+
+ if (!(mirr_state = dm_pool_alloc(mem, sizeof(*mirr_state)))) {
+ log_error("struct mirr_state allocation failed");
+ return NULL;
+ }
+
+ mirr_state->default_region_size = 2 *
+ find_config_tree_int(cmd,
+ "activation/mirror_region_size",
+ DEFAULT_MIRROR_REGION_SIZE);
+
+ return mirr_state;
+}
+
+static int _mirrored_target_percent(void **target_state,
+ percent_t *percent,
+ struct dm_pool *mem,
+ struct cmd_context *cmd,
+ struct lv_segment *seg, char *params,
+ uint64_t *total_numerator,
+ uint64_t *total_denominator)
+{
+ struct mirror_state *mirr_state;
+ uint64_t numerator, denominator;
+ unsigned mirror_count, m;
+ int used;
+ char *pos = params;
+
+ if (!*target_state)
+ *target_state = _mirrored_init_target(mem, cmd);
+
+ mirr_state = *target_state;
+
+ /* Status line: <#mirrors> (maj:min)+ <synced>/<total_regions> */
+ log_debug("Mirror status: %s", params);
+
+ if (sscanf(pos, "%u %n", &mirror_count, &used) != 1) {
+ log_error("Failure parsing mirror status mirror count: %s",
+ params);
+ return 0;
+ }
+ pos += used;
+
+ for (m = 0; m < mirror_count; m++) {
+ if (sscanf(pos, "%*x:%*x %n", &used) != 0) {
+ log_error("Failure parsing mirror status devices: %s",
+ params);
+ return 0;
+ }
+ pos += used;
+ }
+
+ if (sscanf(pos, "%" PRIu64 "/%" PRIu64 "%n", &numerator, &denominator,
+ &used) != 2) {
+ log_error("Failure parsing mirror status fraction: %s", params);
+ return 0;
+ }
+ pos += used;
+
+ *total_numerator += numerator;
+ *total_denominator += denominator;
+
+ if (seg)
+ seg->extents_copied = seg->area_len * numerator / denominator;
+
+ *percent = make_percent(numerator, denominator);
+
+ return 1;
+}
+
+static int _mirrored_transient_status(struct lv_segment *seg, char *params)
+{
+ int i, j;
+ struct logical_volume *lv = seg->lv;
+ struct lvinfo info;
+ char *p = NULL;
+ char **args, **log_args;
+ struct logical_volume **images;
+ struct logical_volume *log;
+ int num_devs, log_argc;
+ int failed = 0;
+ char *status;
+
+ log_very_verbose("Mirrored transient status: \"%s\"", params);
+
+ /* number of devices */
+ if (!dm_split_words(params, 1, 0, &p))
+ return_0;
+
+ if (!(num_devs = atoi(p)))
+ return_0;
+
+ p += strlen(p) + 1;
+
+ if (num_devs > DEFAULT_MIRROR_MAX_IMAGES) {
+ log_error("Unexpectedly many (%d) mirror images in %s.",
+ num_devs, lv->name);
+ return_0;
+ }
+
+ args = alloca((num_devs + 5) * sizeof(char *));
+ images = alloca(num_devs * sizeof(struct logical_volume *));
+
+ if (dm_split_words(p, num_devs + 4, 0, args) < num_devs + 4)
+ return_0;
+
+ log_argc = atoi(args[3 + num_devs]);
+ log_args = alloca(log_argc * sizeof(char *));
+
+ if (log_argc > 16) {
+ log_error("Unexpectedly many (%d) log arguments in %s.",
+ log_argc, lv->name);
+ return_0;
+ }
+
+
+ if (dm_split_words(args[3 + num_devs] + strlen(args[3 + num_devs]) + 1,
+ log_argc, 0, log_args) < log_argc)
+ return_0;
+
+ if (num_devs != seg->area_count) {
+ log_error("Active mirror has a wrong number of mirror images!");
+ log_error("Metadata says %d, kernel says %d.", seg->area_count, num_devs);
+ return_0;
+ }
+
+ if (!strcmp(log_args[0], "disk")) {
+ char buf[32];
+ log = first_seg(lv)->log_lv;
+ if (!lv_info(lv->vg->cmd, log, 0, &info, 0, 0)) {
+ log_error("Check for existence of mirror log %s failed.",
+ log->name);
+ return 0;
+ }
+ log_debug("Found mirror log at %d:%d", info.major, info.minor);
+ sprintf(buf, "%d:%d", info.major, info.minor);
+ if (strcmp(buf, log_args[1])) {
+ log_error("Mirror log mismatch. Metadata says %s, kernel says %s.",
+ buf, log_args[1]);
+ return_0;
+ }
+ log_very_verbose("Status of log (%s): %s", buf, log_args[2]);
+ if (log_args[2][0] != 'A') {
+ log->status |= PARTIAL_LV;
+ ++failed;
+ }
+ }
+
+ for (i = 0; i < num_devs; ++i)
+ images[i] = NULL;
+
+ for (i = 0; i < seg->area_count; ++i) {
+ char buf[32];
+ if (!lv_info(lv->vg->cmd, seg_lv(seg, i), 0, &info, 0, 0)) {
+ log_error("Check for existence of mirror image %s failed.",
+ seg_lv(seg, i)->name);
+ return 0;
+ }
+ log_debug("Found mirror image at %d:%d", info.major, info.minor);
+ sprintf(buf, "%d:%d", info.major, info.minor);
+ for (j = 0; j < num_devs; ++j) {
+ if (!strcmp(buf, args[j])) {
+ log_debug("Match: metadata image %d matches kernel image %d", i, j);
+ images[j] = seg_lv(seg, i);
+ }
+ }
+ }
+
+ status = args[2 + num_devs];
+
+ for (i = 0; i < num_devs; ++i) {
+ if (!images[i]) {
+ log_error("Failed to find image %d (%s).", i, args[i]);
+ return_0;
+ }
+ log_very_verbose("Status of image %d: %c", i, status[i]);
+ if (status[i] != 'A') {
+ images[i]->status |= PARTIAL_LV;
+ ++failed;
+ }
+ }
+
+ /* update PARTIAL_LV flags across the VG */
+ if (failed)
+ vg_mark_partial_lvs(lv->vg);
+
+ return 1;
+}
+
+static int _add_log(struct dm_pool *mem, struct lv_segment *seg,
+ struct dm_tree_node *node, uint32_t area_count, uint32_t region_size)
+{
+ unsigned clustered = 0;
+ char *log_dlid = NULL;
+ uint32_t log_flags = 0;
+
+ /*
+ * Use clustered mirror log for non-exclusive activation
+ * in clustered VG.
+ */
+ if ((!(seg->lv->status & ACTIVATE_EXCL) &&
+ (vg_is_clustered(seg->lv->vg))))
+ clustered = 1;
+
+ if (seg->log_lv) {
+ /* If disk log, use its UUID */
+ if (!(log_dlid = build_dm_uuid(mem, seg->log_lv->lvid.s, NULL))) {
+ log_error("Failed to build uuid for log LV %s.",
+ seg->log_lv->name);
+ return 0;
+ }
+ } else {
+ /* If core log, use mirror's UUID and set DM_CORELOG flag */
+ if (!(log_dlid = build_dm_uuid(mem, seg->lv->lvid.s, NULL))) {
+ log_error("Failed to build uuid for mirror LV %s.",
+ seg->lv->name);
+ return 0;
+ }
+ log_flags |= DM_CORELOG;
+ }
+
+ if (mirror_in_sync() && !(seg->status & PVMOVE))
+ log_flags |= DM_NOSYNC;
+
+ if (_block_on_error_available && !(seg->status & PVMOVE))
+ log_flags |= DM_BLOCK_ON_ERROR;
+
+ return dm_tree_node_add_mirror_target_log(node, region_size, clustered, log_dlid, area_count, log_flags);
+}
+
+static int _mirrored_add_target_line(struct dev_manager *dm, struct dm_pool *mem,
+ struct cmd_context *cmd, void **target_state,
+ struct lv_segment *seg,
+ struct dm_tree_node *node, uint64_t len,
+ uint32_t *pvmove_mirror_count)
+{
+ struct mirror_state *mirr_state;
+ uint32_t area_count = seg->area_count;
+ unsigned start_area = 0u;
+ int mirror_status = MIRR_RUNNING;
+ uint32_t region_size;
+ int r;
+
+ if (!*target_state)
+ *target_state = _mirrored_init_target(mem, cmd);
+
+ mirr_state = *target_state;
+
+ /*
+ * Mirror segment could have only 1 area temporarily
+ * if the segment is under conversion.
+ */
+ if (seg->area_count == 1)
+ mirror_status = MIRR_DISABLED;
+
+ /*
+ * For pvmove, only have one mirror segment RUNNING at once.
+ * Segments before this are COMPLETED and use 2nd area.
+ * Segments after this are DISABLED and use 1st area.
+ */
+ if (seg->status & PVMOVE) {
+ if (seg->extents_copied == seg->area_len) {
+ mirror_status = MIRR_COMPLETED;
+ start_area = 1;
+ } else if ((*pvmove_mirror_count)++) {
+ mirror_status = MIRR_DISABLED;
+ area_count = 1;
+ }
+ /* else MIRR_RUNNING */
+ }
+
+ if (mirror_status != MIRR_RUNNING) {
+ if (!dm_tree_node_add_linear_target(node, len))
+ return_0;
+ goto done;
+ }
+
+ if (!(seg->status & PVMOVE)) {
+ if (!seg->region_size) {
+ log_error("Missing region size for mirror segment.");
+ return 0;
+ }
+ region_size = seg->region_size;
+
+ } else
+ region_size = adjusted_mirror_region_size(seg->lv->vg->extent_size,
+ seg->area_len,
+ mirr_state->default_region_size);
+
+ if (!dm_tree_node_add_mirror_target(node, len))
+ return_0;
+
+ if ((r = _add_log(mem, seg, node, area_count, region_size)) <= 0) {
+ stack;
+ return r;
+ }
+
+ done:
+ return add_areas_line(dm, seg, node, start_area, area_count);
+}
+
+static int _mirrored_target_present(struct cmd_context *cmd,
+ const struct lv_segment *seg,
+ unsigned *attributes)
+{
+ static int _mirrored_checked = 0;
+ static int _mirrored_present = 0;
+ uint32_t maj, min, patchlevel;
+ unsigned maj2, min2, patchlevel2;
+ char vsn[80];
+ struct utsname uts;
+ unsigned kmaj, kmin, krel;
+
+ if (!_mirrored_checked) {
+ _mirrored_present = target_present(cmd, "mirror", 1);
+
+ /*
+ * block_on_error available as "block_on_error" log
+ * argument with mirror target >= 1.1 and <= 1.11
+ * or with 1.0 in RHEL4U3 driver >= 4.5
+ *
+ * block_on_error available as "handle_errors" mirror
+ * argument with mirror target >= 1.12.
+ *
+ * libdm-deptree.c is smart enough to handle the differences
+ * between block_on_error and handle_errors for all
+ * mirror target versions >= 1.1
+ */
+ /* FIXME Move this into libdevmapper */
+
+ if (target_version("mirror", &maj, &min, &patchlevel) &&
+ maj == 1 &&
+ ((min >= 1) ||
+ (min == 0 && driver_version(vsn, sizeof(vsn)) &&
+ sscanf(vsn, "%u.%u.%u", &maj2, &min2, &patchlevel2) == 3 &&
+ maj2 == 4 && min2 == 5 && patchlevel2 == 0))) /* RHEL4U3 */
+ _block_on_error_available = 1;
+ }
+
+ /*
+ * Check only for modules if atttributes requested and no previous check.
+ * FIXME: Fails incorrectly if cmirror was built into kernel.
+ */
+ if (attributes) {
+ if (!_mirror_attributes) {
+ /*
+ * The dm-log-userspace module was added to the
+ * 2.6.31 kernel.
+ */
+ if (!uname(&uts) &&
+ (sscanf(uts.release, "%u.%u.%u", &kmaj, &kmin, &krel) == 3) &&
+ KERNEL_VERSION(kmaj, kmin, krel) < KERNEL_VERSION(2, 6, 31)) {
+ if (module_present(cmd, "log-clustered"))
+ _mirror_attributes |= MIRROR_LOG_CLUSTERED;
+ } else if (module_present(cmd, "log-userspace"))
+ _mirror_attributes |= MIRROR_LOG_CLUSTERED;
+
+ if (!(_mirror_attributes & MIRROR_LOG_CLUSTERED))
+ log_verbose("Cluster mirror log module is not available");
+
+ /*
+ * The cluster mirror log daemon must be running,
+ * otherwise, the kernel module will fail to make
+ * contact.
+ */
+#ifdef CMIRRORD_PIDFILE
+ if (!dm_daemon_is_running(CMIRRORD_PIDFILE)) {
+ log_verbose("Cluster mirror log daemon is not running");
+ _mirror_attributes &= ~MIRROR_LOG_CLUSTERED;
+ }
+#else
+ log_verbose("Cluster mirror log daemon not included in build");
+ _mirror_attributes &= ~MIRROR_LOG_CLUSTERED;
+#endif
+ }
+ *attributes = _mirror_attributes;
+ }
+ _mirrored_checked = 1;
+
+ return _mirrored_present;
+}
+
+#ifdef DMEVENTD
+static const char *_get_mirror_dso_path(struct cmd_context *cmd)
+{
+ return get_monitor_dso_path(cmd, find_config_tree_str(cmd, "dmeventd/mirror_library",
+ DEFAULT_DMEVENTD_MIRROR_LIB));
+}
+
+/* FIXME Cache this */
+static int _target_registered(struct lv_segment *seg, int *pending)
+{
+ return target_registered_with_dmeventd(seg->lv->vg->cmd, _get_mirror_dso_path(seg->lv->vg->cmd),
+ seg->lv, pending);
+}
+
+/* FIXME This gets run while suspended and performs banned operations. */
+static int _target_set_events(struct lv_segment *seg, int evmask, int set)
+{
+ return target_register_events(seg->lv->vg->cmd, _get_mirror_dso_path(seg->lv->vg->cmd),
+ seg->lv, evmask, set, 0);
+}
+
+static int _target_monitor_events(struct lv_segment *seg, int events)
+{
+ return _target_set_events(seg, events, 1);
+}
+
+static int _target_unmonitor_events(struct lv_segment *seg, int events)
+{
+ return _target_set_events(seg, events, 0);
+}
+
+#endif /* DMEVENTD */
+#endif /* DEVMAPPER_SUPPORT */
+
+static int _mirrored_modules_needed(struct dm_pool *mem,
+ const struct lv_segment *seg,
+ struct dm_list *modules)
+{
+ if (seg->log_lv &&
+ !list_segment_modules(mem, first_seg(seg->log_lv), modules))
+ return_0;
+
+ if (vg_is_clustered(seg->lv->vg) &&
+ !str_list_add(mem, modules, "clog")) {
+ log_error("cluster log string list allocation failed");
+ return 0;
+ }
+
+ if (!str_list_add(mem, modules, "mirror")) {
+ log_error("mirror string list allocation failed");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void _mirrored_destroy(struct segment_type *segtype)
+{
+ dm_free(segtype);
+}
+
+static struct segtype_handler _mirrored_ops = {
+ .name = _mirrored_name,
+ .display = _mirrored_display,
+ .text_import_area_count = _mirrored_text_import_area_count,
+ .text_import = _mirrored_text_import,
+ .text_export = _mirrored_text_export,
+#ifdef DEVMAPPER_SUPPORT
+ .add_target_line = _mirrored_add_target_line,
+ .target_percent = _mirrored_target_percent,
+ .target_present = _mirrored_target_present,
+ .check_transient_status = _mirrored_transient_status,
+#ifdef DMEVENTD
+ .target_monitored = _target_registered,
+ .target_monitor_events = _target_monitor_events,
+ .target_unmonitor_events = _target_unmonitor_events,
+#endif
+#endif
+ .modules_needed = _mirrored_modules_needed,
+ .destroy = _mirrored_destroy,
+};
+
+#ifdef MIRRORED_INTERNAL
+struct segment_type *init_mirrored_segtype(struct cmd_context *cmd)
+#else /* Shared */
+struct segment_type *init_segtype(struct cmd_context *cmd);
+struct segment_type *init_segtype(struct cmd_context *cmd)
+#endif
+{
+ struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+ if (!segtype)
+ return_NULL;
+
+ segtype->cmd = cmd;
+ segtype->ops = &_mirrored_ops;
+ segtype->name = "mirror";
+ segtype->private = NULL;
+ segtype->flags = SEG_AREAS_MIRRORED;
+
+#ifdef DMEVENTD
+ if (_get_mirror_dso_path(cmd))
+ segtype->flags |= SEG_MONITORED;
+#endif
+
+ log_very_verbose("Initialised segtype: %s", segtype->name);
+
+ return segtype;
+}
--- /dev/null
+/* lib/misc/configure.h.in. Generated from configure.in by autoheader. */
+
+/* Define to 1 if the `closedir' function returns void instead of `int'. */
+#undef CLOSEDIR_VOID
+
+/* Define to 1 to include built-in support for clustered LVM locking. */
+#undef CLUSTER_LOCKING_INTERNAL
+
+/* Path to clvmd binary. */
+#undef CLVMD_PATH
+
+/* Path to clvmd pidfile. */
+#undef CLVMD_PIDFILE
+
+/* Path to cmirrord pidfile. */
+#undef CMIRRORD_PIDFILE
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+ systems. This function is required for `alloca.c' support on those systems.
+ */
+#undef CRAY_STACKSEG_END
+
+/* Define to 1 if using `alloca.c'. */
+#undef C_ALLOCA
+
+/* Name of default metadata archive subdirectory. */
+#undef DEFAULT_ARCHIVE_SUBDIR
+
+/* Name of default metadata backup subdirectory. */
+#undef DEFAULT_BACKUP_SUBDIR
+
+/* Name of default metadata cache subdirectory. */
+#undef DEFAULT_CACHE_SUBDIR
+
+/* Default data alignment. */
+#undef DEFAULT_DATA_ALIGNMENT
+
+/* Name of default locking directory. */
+#undef DEFAULT_LOCK_DIR
+
+/* Name of default run directory. */
+#undef DEFAULT_RUN_DIR
+
+/* Define to 0 to reinstate the pre-2.02.54 handling of unit suffixes. */
+#undef DEFAULT_SI_UNIT_CONSISTENCY
+
+/* Path to LVM system directory. */
+#undef DEFAULT_SYS_DIR
+
+/* Define to 1 to enable LVM2 device-mapper interaction. */
+#undef DEVMAPPER_SUPPORT
+
+/* Define to 1 to enable the device-mapper event daemon. */
+#undef DMEVENTD
+
+/* Path to dmeventd binary. */
+#undef DMEVENTD_PATH
+
+/* Path to dmeventd pidfile. */
+#undef DMEVENTD_PIDFILE
+
+/* Library version */
+#undef DM_LIB_VERSION
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+#undef HAVE_ALLOCA
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+ */
+#undef HAVE_ALLOCA_H
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#undef HAVE_ARPA_INET_H
+
+/* Define to 1 if you have the <asm/byteorder.h> header file. */
+#undef HAVE_ASM_BYTEORDER_H
+
+/* Define to 1 if you have the <assert.h> header file. */
+#undef HAVE_ASSERT_H
+
+/* Define to 1 if canonicalize_file_name is available. */
+#undef HAVE_CANONICALIZE_FILE_NAME
+
+/* Define to 1 if you have the <ccs.h> header file. */
+#undef HAVE_CCS_H
+
+/* Define to 1 if your system has a working `chown' function. */
+#undef HAVE_CHOWN
+
+/* Define to 1 if you have the <corosync/confdb.h> header file. */
+#undef HAVE_COROSYNC_CONFDB_H
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#undef HAVE_CTYPE_H
+
+/* Define to 1 if you have the <dirent.h> header file. */
+#undef HAVE_DIRENT_H
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+#undef HAVE_DOPRNT
+
+/* Define to 1 if you have the `dup2' function. */
+#undef HAVE_DUP2
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define to 1 if you have the `ftruncate' function. */
+#undef HAVE_FTRUNCATE
+
+/* Define to 1 if you have the `gethostname' function. */
+#undef HAVE_GETHOSTNAME
+
+/* Define to 1 if getline is available. */
+#undef HAVE_GETLINE
+
+/* Define to 1 if you have the `getmntent' function. */
+#undef HAVE_GETMNTENT
+
+/* Define to 1 if getopt_long is available. */
+#undef HAVE_GETOPTLONG
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#undef HAVE_GETOPT_H
+
+/* Define to 1 if you have the `getpagesize' function. */
+#undef HAVE_GETPAGESIZE
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#undef HAVE_GETTIMEOFDAY
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#undef HAVE_LANGINFO_H
+
+/* Define to 1 if you have the <libcman.h> header file. */
+#undef HAVE_LIBCMAN_H
+
+/* Define to 1 if dynamic libraries are available. */
+#undef HAVE_LIBDL
+
+/* Define to 1 if you have the <libdlm.h> header file. */
+#undef HAVE_LIBDLM_H
+
+/* Define to 1 if you have the <libgen.h> header file. */
+#undef HAVE_LIBGEN_H
+
+/* Define to 1 if you have the <libgulm.h> header file. */
+#undef HAVE_LIBGULM_H
+
+/* Define to 1 if you have the <libintl.h> header file. */
+#undef HAVE_LIBINTL_H
+
+/* Define to 1 if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define to 1 if you have the <linux/fs.h> header file. */
+#undef HAVE_LINUX_FS_H
+
+/* Define to 1 if you have the <locale.h> header file. */
+#undef HAVE_LOCALE_H
+
+/* Define to 1 if `lstat' has the bug that it succeeds when given the
+ zero-length file name argument. */
+#undef HAVE_LSTAT_EMPTY_STRING_BUG
+
+/* Define to 1 if you have the <machine/endian.h> header file. */
+#undef HAVE_MACHINE_ENDIAN_H
+
+/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
+ to 0 otherwise. */
+#undef HAVE_MALLOC
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define to 1 if you have the `memmove' function. */
+#undef HAVE_MEMMOVE
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
+/* Define to 1 if you have the `mkdir' function. */
+#undef HAVE_MKDIR
+
+/* Define to 1 if you have the `mkfifo' function. */
+#undef HAVE_MKFIFO
+
+/* Define to 1 if you have a working `mmap' system call. */
+#undef HAVE_MMAP
+
+/* Define to 1 if you have the <mntent.h> header file. */
+#undef HAVE_MNTENT_H
+
+/* Define to 1 if you have the `munmap' function. */
+#undef HAVE_MUNMAP
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+#undef HAVE_NDIR_H
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#undef HAVE_NETINET_IN_H
+
+/* Define to 1 if you have the `nl_langinfo' function. */
+#undef HAVE_NL_LANGINFO
+
+/* Define to 1 if you have the <pthread.h> header file. */
+#undef HAVE_PTHREAD_H
+
+/* Define to 1 if you have the <readline/history.h> header file. */
+#undef HAVE_READLINE_HISTORY_H
+
+/* Define to 1 if you have the <readline/readline.h> header file. */
+#undef HAVE_READLINE_READLINE_H
+
+/* Define to 1 if your system has a GNU libc compatible `realloc' function,
+ and to 0 otherwise. */
+#undef HAVE_REALLOC
+
+/* Define to 1 to include support for realtime clock. */
+#undef HAVE_REALTIME
+
+/* Define to 1 if you have the `rl_completion_matches' function. */
+#undef HAVE_RL_COMPLETION_MATCHES
+
+/* Define to 1 if you have the `rmdir' function. */
+#undef HAVE_RMDIR
+
+/* Define to 1 if you have the <search.h> header file. */
+#undef HAVE_SEARCH_H
+
+/* Define to 1 if you have the `select' function. */
+#undef HAVE_SELECT
+
+/* Define to 1 to include support for selinux. */
+#undef HAVE_SELINUX
+
+/* Define to 1 if you have the <selinux/label.h> header file. */
+#undef HAVE_SELINUX_LABEL_H
+
+/* Define to 1 if you have the <selinux/selinux.h> header file. */
+#undef HAVE_SELINUX_SELINUX_H
+
+/* Define to 1 if sepol_check_context is available. */
+#undef HAVE_SEPOL
+
+/* Define to 1 if you have the `setenv' function. */
+#undef HAVE_SETENV
+
+/* Define to 1 if you have the `setlocale' function. */
+#undef HAVE_SETLOCALE
+
+/* Define to 1 if you have the `siginterrupt' function. */
+#undef HAVE_SIGINTERRUPT
+
+/* Define to 1 if you have the <signal.h> header file. */
+#undef HAVE_SIGNAL_H
+
+/* Define to 1 if you have the `socket' function. */
+#undef HAVE_SOCKET
+
+/* Define to 1 if `stat' has the bug that it succeeds when given the
+ zero-length file name argument. */
+#undef HAVE_STAT_EMPTY_STRING_BUG
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#undef HAVE_STDARG_H
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#undef HAVE_STDDEF_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* Define to 1 if you have the `strchr' function. */
+#undef HAVE_STRCHR
+
+/* Define to 1 if you have the `strcspn' function. */
+#undef HAVE_STRCSPN
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the `strerror' function. */
+#undef HAVE_STRERROR
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strncasecmp' function. */
+#undef HAVE_STRNCASECMP
+
+/* Define to 1 if you have the `strrchr' function. */
+#undef HAVE_STRRCHR
+
+/* Define to 1 if you have the `strspn' function. */
+#undef HAVE_STRSPN
+
+/* Define to 1 if you have the `strstr' function. */
+#undef HAVE_STRSTR
+
+/* Define to 1 if you have the `strtol' function. */
+#undef HAVE_STRTOL
+
+/* Define to 1 if you have the `strtoul' function. */
+#undef HAVE_STRTOUL
+
+/* Define to 1 if `st_rdev' is member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_RDEV
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_SYS_DIR_H
+
+/* Define to 1 if you have the <sys/disk.h> header file. */
+#undef HAVE_SYS_DISK_H
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#undef HAVE_SYS_FILE_H
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the <sys/ipc.h> header file. */
+#undef HAVE_SYS_IPC_H
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#undef HAVE_SYS_MMAN_H
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+#undef HAVE_SYS_MOUNT_H
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_SYS_NDIR_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#undef HAVE_SYS_RESOURCE_H
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#undef HAVE_SYS_SELECT_H
+
+/* Define to 1 if you have the <sys/sem.h> header file. */
+#undef HAVE_SYS_SEM_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/statvfs.h> header file. */
+#undef HAVE_SYS_STATVFS_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#undef HAVE_SYS_UIO_H
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#undef HAVE_SYS_UN_H
+
+/* Define to 1 if you have the <sys/utsname.h> header file. */
+#undef HAVE_SYS_UTSNAME_H
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to 1 if you have the <termios.h> header file. */
+#undef HAVE_TERMIOS_H
+
+/* Define to 1 if you have the <time.h> header file. */
+#undef HAVE_TIME_H
+
+/* Define to 1 if you have the `uname' function. */
+#undef HAVE_UNAME
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the <utmpx.h> header file. */
+#undef HAVE_UTMPX_H
+
+/* Define to 1 if you have the <valgrind/memcheck.h> header file. */
+#undef HAVE_VALGRIND_MEMCHECK_H
+
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* Define to 1 if you have the <vfork.h> header file. */
+#undef HAVE_VFORK_H
+
+/* Define to 1 if you have the `vprintf' function. */
+#undef HAVE_VPRINTF
+
+/* Define to 1 if `fork' works. */
+#undef HAVE_WORKING_FORK
+
+/* Define to 1 if `vfork' works. */
+#undef HAVE_WORKING_VFORK
+
+/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
+ slash. */
+#undef LSTAT_FOLLOWS_SLASHED_SYMLINK
+
+/* Define to 1 if 'lvm' should fall back to using LVM1 binaries if
+ device-mapper is missing from the kernel */
+#undef LVM1_FALLBACK
+
+/* Define to 1 to include built-in support for LVM1 metadata. */
+#undef LVM1_INTERNAL
+
+/* Path to lvm binary. */
+#undef LVM_PATH
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
+ */
+#undef MAJOR_IN_MKDEV
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in
+ <sysmacros.h>. */
+#undef MAJOR_IN_SYSMACROS
+
+/* Define to 1 to include built-in support for mirrors. */
+#undef MIRRORED_INTERNAL
+
+/* The path to 'modprobe', if available. */
+#undef MODPROBE_CMD
+
+/* Define to 1 to enable O_DIRECT support. */
+#undef O_DIRECT_SUPPORT
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 to include built-in support for GFS pool metadata. */
+#undef POOL_INTERNAL
+
+/* Define to 1 to include the LVM readline shell. */
+#undef READLINE_SUPPORT
+
+/* Define to 1 to include built-in support for replicators. */
+#undef REPLICATOR_INTERNAL
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#undef RETSIGTYPE
+
+/* Define to the type of arg 1 for `select'. */
+#undef SELECT_TYPE_ARG1
+
+/* Define to the type of args 2, 3 and 4 for `select'. */
+#undef SELECT_TYPE_ARG234
+
+/* Define to the type of arg 5 for `select'. */
+#undef SELECT_TYPE_ARG5
+
+/* Define to 1 to include built-in support for snapshots. */
+#undef SNAPSHOT_INTERNAL
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at runtime.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+#undef STACK_DIRECTION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+#undef TM_IN_SYS_TIME
+
+/* Define to 1 to enable synchronisation with udev processing. */
+#undef UDEV_SYNC_SUPPORT
+
+/* Enable a valgrind aware build of pool */
+#undef VALGRIND_POOL
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT32_T
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT64_T
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT8_T
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef gid_t
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to the type of a signed integer type of width exactly 16 bits if
+ such a type exists and the standard includes do not define it. */
+#undef int16_t
+
+/* Define to the type of a signed integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+#undef int32_t
+
+/* Define to the type of a signed integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+#undef int64_t
+
+/* Define to the type of a signed integer type of width exactly 8 bits if such
+ a type exists and the standard includes do not define it. */
+#undef int8_t
+
+/* Define to rpl_malloc if the replacement function should be used. */
+#undef malloc
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef mode_t
+
+/* Define to `long int' if <sys/types.h> does not define. */
+#undef off_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef pid_t
+
+/* Define to rpl_realloc if the replacement function should be used. */
+#undef realloc
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef ssize_t
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef uid_t
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint16_t
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint32_t
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint64_t
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint8_t
+
+/* Define as `fork' if `vfork' does not work. */
+#undef vfork
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+
+#include "crc.h"
+
+/* Calculate an endian-independent CRC of supplied buffer */
+#ifndef DEBUG_CRC32
+uint32_t calc_crc(uint32_t initial, const uint8_t *buf, uint32_t size)
+#else
+static uint32_t _calc_crc_new(uint32_t initial, const uint8_t *buf, uint32_t size)
+#endif
+{
+ /* CRC-32 byte lookup table generated by crc_gen.c */
+ static const uint32_t crctab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+ };
+ const uint32_t *start = (const uint32_t *) buf;
+ const uint32_t *end = (const uint32_t *) (buf + (size & 0xfffffffc));
+ uint32_t crc = initial;
+
+ /* Process 4 bytes per iteration */
+ while (start < end) {
+ crc = crc ^ *start++;
+ crc = crctab[crc & 0xff] ^ crc >> 8;
+ crc = crctab[crc & 0xff] ^ crc >> 8;
+ crc = crctab[crc & 0xff] ^ crc >> 8;
+ crc = crctab[crc & 0xff] ^ crc >> 8;
+ }
+
+ /* Process any bytes left over */
+ buf = (const uint8_t *) start;
+ size = size & 0x3;
+ while (size--) {
+ crc = crc ^ *buf++;
+ crc = crctab[crc & 0xff] ^ crc >> 8;
+ }
+
+ return crc;
+}
+
+#ifdef DEBUG_CRC32
+static uint32_t _calc_crc_old(uint32_t initial, const uint8_t *buf, uint32_t size)
+{
+ static const uint32_t crctab[] = {
+ 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+ 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+ 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+ 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
+ };
+ uint32_t i, crc = initial;
+
+ for (i = 0; i < size; i++) {
+ crc ^= *buf++;
+ crc = (crc >> 4) ^ crctab[crc & 0xf];
+ crc = (crc >> 4) ^ crctab[crc & 0xf];
+ }
+ return crc;
+}
+
+uint32_t calc_crc(uint32_t initial, const uint8_t *buf, uint32_t size)
+{
+ uint32_t new_crc = _calc_crc_new(initial, buf, size);
+ uint32_t old_crc = _calc_crc_old(initial, buf, size);
+
+ if (new_crc != old_crc)
+ log_error(INTERNAL_ERROR "Old and new crc32 algorithms mismatch: 0x%08x != 0x%08x", old_crc, new_crc);
+
+ return old_crc;
+}
+
+#endif /* DEBUG_CRC32 */
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_CRC_H
+#define _LVM_CRC_H
+
+#define INITIAL_CRC 0xf597a6cf
+
+uint32_t calc_crc(uint32_t initial, const uint8_t *buf, uint32_t size);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Helper program to generate table included in crc.c.
+ */
+#include "lib.h"
+
+int main(int argc, char **argv)
+{
+ uint32_t crc, i, j;
+
+ printf("\t/* CRC-32 byte lookup table generated by crc_gen.c */\n");
+ printf("\tstatic const uint32_t crctab[] = {");
+
+ for (i = 0; i < 256; i++) {
+ crc = i;
+ for (j = 0; j < 8; j++) {
+ if (crc & 1)
+ crc = 0xedb88320L ^ (crc >> 1);
+ else
+ crc = crc >> 1;
+ }
+
+ if (i % 8)
+ printf(" ");
+ else
+ printf("\n\t\t");
+
+ printf("0x%08.8x,", crc);
+ }
+
+ printf("\n\t};\n");
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_INTL_H
+#define _LVM_INTL_H
+
+#ifdef INTL_PACKAGE
+# include <libintl.h>
+# define _(String) dgettext(INTL_PACKAGE, (String))
+#else
+# define _(String) (String)
+#endif
+
+#endif
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Return the address of the last file name component of NAME.
+ * If NAME ends in a slash, return the empty string.
+ */
+
+#include <string.h>
+
+static inline const char *last_path_component(char const *name)
+{
+ char const *slash = strrchr(name, '/');
+
+ return (slash) ? slash + 1 : name;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This file must be included first by every library source file.
+ */
+#ifndef _LVM_LIB_H
+#define _LVM_LIB_H
+
+#include "configure.h"
+
+#define _REENTRANT
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "intl.h"
+#include "libdevmapper.h"
+#include "lvm-globals.h"
+#include "lvm-wrappers.h"
+#include "lvm-types.h"
+#include "util.h"
+
+#ifdef DM
+# include "dm-logging.h"
+#else
+# include "lvm-logging.h"
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "device.h"
+#include "locking.h"
+#include "lvm-exec.h"
+#include "toolcontext.h"
+
+#include <unistd.h>
+#include <sys/wait.h>
+
+/*
+ * Create verbose string with list of parameters
+ */
+static char *_verbose_args(const char *const argv[], char *buf, size_t sz)
+{
+ int pos = 0;
+ int len;
+ unsigned i;
+
+ buf[0] = '\0';
+ for (i = 0; argv[i]; i++) {
+ if ((len = dm_snprintf(buf + pos, sz - pos,
+ "%s ", argv[i])) < 0)
+ /* Truncated */
+ break;
+ pos += len;
+ }
+
+ return buf;
+}
+
+/*
+ * Execute and wait for external command
+ */
+int exec_cmd(struct cmd_context *cmd, const char *const argv[], int *rstatus)
+{
+ pid_t pid;
+ int status;
+ char buf[PATH_MAX * 2];
+
+ log_verbose("Executing: %s", _verbose_args(argv, buf, sizeof(buf)));
+
+ if ((pid = fork()) == -1) {
+ log_error("fork failed: %s", strerror(errno));
+ return 0;
+ }
+
+ if (!pid) {
+ /* Child */
+ reset_locking();
+ dev_close_all();
+ /* FIXME Fix effect of reset_locking on cache then include this */
+ /* destroy_toolcontext(cmd); */
+ /* FIXME Use execve directly */
+ execvp(argv[0], (char **const) argv);
+ log_sys_error("execvp", argv[0]);
+ _exit(errno);
+ }
+
+ if (rstatus)
+ *rstatus = -1;
+
+ /* Parent */
+ if (wait4(pid, &status, 0, NULL) != pid) {
+ log_error("wait4 child process %u failed: %s", pid,
+ strerror(errno));
+ return 0;
+ }
+
+ if (!WIFEXITED(status)) {
+ log_error("Child %u exited abnormally", pid);
+ return 0;
+ }
+
+ if (WEXITSTATUS(status)) {
+ if (rstatus) {
+ *rstatus = WEXITSTATUS(status);
+ log_verbose("%s failed: %u", argv[0], *rstatus);
+ } else
+ log_error("%s failed: %u", argv[0], WEXITSTATUS(status));
+ return 0;
+ }
+
+ if (rstatus)
+ *rstatus = 0;
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_EXEC_H
+#define _LVM_EXEC_H
+
+#include "lib.h"
+
+struct cmd_context;
+int exec_cmd(struct cmd_context *cmd, const char *const argv[], int *rstatus);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "lvm-file.h"
+#include "lvm-string.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+/*
+ * Creates a temporary filename, and opens a descriptor to the
+ * file. Both the filename and descriptor are needed so we can
+ * rename the file after successfully writing it. Grab
+ * NFS-supported exclusive fcntl discretionary lock.
+ */
+int create_temp_name(const char *dir, char *buffer, size_t len, int *fd,
+ unsigned *seed)
+{
+ int i, num;
+ pid_t pid;
+ char hostname[255];
+ struct flock lock = {
+ .l_type = F_WRLCK,
+ .l_whence = 0,
+ .l_start = 0,
+ .l_len = 0
+ };
+
+ num = rand_r(seed);
+ pid = getpid();
+ if (gethostname(hostname, sizeof(hostname)) < 0) {
+ log_sys_error("gethostname", "");
+ strcpy(hostname, "nohostname");
+ }
+
+ for (i = 0; i < 20; i++, num++) {
+
+ if (dm_snprintf(buffer, len, "%s/.lvm_%s_%d_%d",
+ dir, hostname, pid, num) == -1) {
+ log_error("Not enough space to build temporary file "
+ "string.");
+ return 0;
+ }
+
+ *fd = open(buffer, O_CREAT | O_EXCL | O_WRONLY | O_APPEND,
+ S_IRUSR | S_IRGRP | S_IROTH |
+ S_IWUSR | S_IWGRP | S_IWOTH);
+ if (*fd < 0)
+ continue;
+
+ if (!fcntl(*fd, F_SETLK, &lock))
+ return 1;
+
+ if (close(*fd))
+ log_sys_error("close", buffer);
+ }
+
+ return 0;
+}
+
+/*
+ * NFS-safe rename of a temporary file to a common name, designed
+ * to avoid race conditions and not overwrite the destination if
+ * it exists.
+ *
+ * Try to create the new filename as a hard link to the original.
+ * Check the link count of the original file to see if it worked.
+ * (Assumes nothing else touches our temporary file!) If it
+ * worked, unlink the old filename.
+ */
+int lvm_rename(const char *old, const char *new)
+{
+ struct stat buf;
+
+ if (link(old, new)) {
+ log_error("%s: rename to %s failed: %s", old, new,
+ strerror(errno));
+ return 0;
+ }
+
+ if (stat(old, &buf)) {
+ log_sys_error("stat", old);
+ return 0;
+ }
+
+ if (buf.st_nlink != 2) {
+ log_error("%s: rename to %s failed", old, new);
+ return 0;
+ }
+
+ if (unlink(old)) {
+ log_sys_error("unlink", old);
+ return 0;
+ }
+
+ return 1;
+}
+
+int path_exists(const char *path)
+{
+ struct stat info;
+
+ if (!*path)
+ return 0;
+
+ if (stat(path, &info) < 0)
+ return 0;
+
+ return 1;
+}
+
+int dir_exists(const char *path)
+{
+ struct stat info;
+
+ if (!*path)
+ return 0;
+
+ if (stat(path, &info) < 0)
+ return 0;
+
+ if (!S_ISDIR(info.st_mode))
+ return 0;
+
+ return 1;
+}
+
+int is_empty_dir(const char *dir)
+{
+ struct dirent *dirent;
+ DIR *d;
+
+ if (!(d = opendir(dir))) {
+ log_sys_error("opendir", dir);
+ return 0;
+ }
+
+ while ((dirent = readdir(d)))
+ if (strcmp(dirent->d_name, ".") && strcmp(dirent->d_name, ".."))
+ break;
+
+ if (closedir(d)) {
+ log_sys_error("closedir", dir);
+ }
+
+ return dirent ? 0 : 1;
+}
+
+void sync_dir(const char *file)
+{
+ int fd;
+ char *dir, *c;
+
+ if (!(dir = dm_strdup(file))) {
+ log_error("sync_dir failed in strdup");
+ return;
+ }
+
+ if (!dir_exists(dir)) {
+ c = dir + strlen(dir);
+ while (*c != '/' && c > dir)
+ c--;
+
+ if (c == dir)
+ *c++ = '.';
+
+ *c = '\0';
+ }
+
+ if ((fd = open(dir, O_RDONLY)) == -1) {
+ log_sys_error("open", dir);
+ goto out;
+ }
+
+ if (fsync(fd) && (errno != EROFS) && (errno != EINVAL))
+ log_sys_error("fsync", dir);
+
+ if (close(fd))
+ log_sys_error("close", dir);
+
+ out:
+ dm_free(dir);
+}
+
+/*
+ * Attempt to obtain fcntl lock on a file, if necessary creating file first
+ * or waiting.
+ * Returns file descriptor on success, else -1.
+ * mode is F_WRLCK or F_RDLCK
+ */
+int fcntl_lock_file(const char *file, short lock_type, int warn_if_read_only)
+{
+ int lockfd;
+ char *dir;
+ char *c;
+ struct flock lock = {
+ .l_type = lock_type,
+ .l_whence = 0,
+ .l_start = 0,
+ .l_len = 0
+ };
+
+ if (!(dir = dm_strdup(file))) {
+ log_error("fcntl_lock_file failed in strdup.");
+ return -1;
+ }
+
+ if ((c = strrchr(dir, '/')))
+ *c = '\0';
+
+ if (!dm_create_dir(dir)) {
+ dm_free(dir);
+ return -1;
+ }
+
+ dm_free(dir);
+
+ log_very_verbose("Locking %s (%s, %hd)", file,
+ (lock_type == F_WRLCK) ? "F_WRLCK" : "F_RDLCK",
+ lock_type);
+ if ((lockfd = open(file, O_RDWR | O_CREAT, 0777)) < 0) {
+ /* EACCES has been reported on NFS */
+ if (warn_if_read_only || (errno != EROFS && errno != EACCES))
+ log_sys_error("open", file);
+ else
+ stack;
+
+ return -1;
+ }
+
+ if (fcntl(lockfd, F_SETLKW, &lock)) {
+ log_sys_error("fcntl", file);
+ close(lockfd);
+ return -1;
+ }
+
+ return lockfd;
+}
+
+void fcntl_unlock_file(int lockfd)
+{
+ struct flock lock = {
+ .l_type = F_UNLCK,
+ .l_whence = 0,
+ .l_start = 0,
+ .l_len = 0
+ };
+
+ log_very_verbose("Unlocking fd %d", lockfd);
+
+ if (fcntl(lockfd, F_SETLK, &lock) == -1)
+ log_error("fcntl unlock failed on fd %d: %s", lockfd,
+ strerror(errno));
+
+ if (close(lockfd))
+ log_error("lock file close failed on fd %d: %s", lockfd,
+ strerror(errno));
+}
+
+int lvm_fclose(FILE *fp, const char *filename)
+{
+ if (!dm_fclose(fp))
+ return 0;
+ if (errno == 0)
+ log_error("%s: write error", filename);
+ else
+ log_sys_error("write error", filename);
+ return EOF;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_FILE_H
+#define _LVM_FILE_H
+
+/*
+ * Create a temporary filename, and opens a descriptor to the file.
+ */
+int create_temp_name(const char *dir, char *buffer, size_t len, int *fd,
+ unsigned *seed);
+
+/*
+ * NFS-safe rename of a temporary file to a common name, designed
+ * to avoid race conditions and not overwrite the destination if
+ * it exists.
+ */
+int lvm_rename(const char *old, const char *new);
+
+/*
+ * Return 1 if path exists else return 0
+ */
+int path_exists(const char *path);
+int dir_exists(const char *path);
+
+/*
+ * Return 1 if dir is empty
+ */
+int is_empty_dir(const char *dir);
+
+/* Sync directory changes */
+void sync_dir(const char *file);
+
+/* fcntl locking wrappers */
+int fcntl_lock_file(const char *file, short lock_type, int warn_if_read_only);
+void fcntl_unlock_file(int lockfd);
+
+#define is_same_inode(buf1, buf2) \
+ ((buf1).st_ino == (buf2).st_ino && \
+ (buf1).st_dev == (buf2).st_dev)
+
+/*
+ * Close the specified stream, taking care to detect and diagnose any write
+ * error. If there is an error, use the supplied file name in a diagnostic
+ * that is reported via log_error or log_sys_error, as appropriate.
+ * Use this function to close a stream when you've written data to it via
+ * unchecked fprintf, fputc, etc. calls. Return 0 on success, EOF on failure.
+ */
+int lvm_fclose(FILE *fp, const char *filename);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "device.h"
+#include "memlock.h"
+#include "lvm-string.h"
+#include "lvm-file.h"
+#include "defaults.h"
+
+#include <stdarg.h>
+
+static int _verbose_level = VERBOSE_BASE_LEVEL;
+static int _test = 0;
+static int _md_filtering = 0;
+static int _pvmove = 0;
+static int _full_scan_done = 0; /* Restrict to one full scan during each cmd */
+static int _trust_cache = 0; /* Don't scan when incomplete VGs encountered */
+static int _debug_level = 0;
+static int _log_cmd_name = 0;
+static int _ignorelockingfailure = 0;
+static int _security_level = SECURITY_LEVEL;
+static char _cmd_name[30] = "";
+static int _mirror_in_sync = 0;
+static int _dmeventd_monitor = DEFAULT_DMEVENTD_MONITOR;
+static int _background_polling = DEFAULT_BACKGROUND_POLLING;
+static int _ignore_suspended_devices = 0;
+static int _error_message_produced = 0;
+static unsigned _is_static = 0;
+static int _udev_checking = 1;
+static char _sysfs_dir_path[PATH_MAX] = "";
+static int _dev_disable_after_error_count = DEFAULT_DISABLE_AFTER_ERROR_COUNT;
+
+void init_verbose(int level)
+{
+ _verbose_level = level;
+}
+
+void init_test(int level)
+{
+ if (!_test && level)
+ log_print("Test mode: Metadata will NOT be updated.");
+ _test = level;
+}
+
+void init_md_filtering(int level)
+{
+ _md_filtering = level;
+}
+
+void init_pvmove(int level)
+{
+ _pvmove = level;
+}
+
+void init_full_scan_done(int level)
+{
+ _full_scan_done = level;
+}
+
+void init_trust_cache(int trustcache)
+{
+ _trust_cache = trustcache;
+}
+
+void init_ignorelockingfailure(int level)
+{
+ _ignorelockingfailure = level;
+}
+
+void init_security_level(int level)
+{
+ _security_level = level;
+}
+
+void init_mirror_in_sync(int in_sync)
+{
+ _mirror_in_sync = in_sync;
+}
+
+void init_dmeventd_monitor(int reg)
+{
+ _dmeventd_monitor = reg;
+}
+
+void init_background_polling(int polling)
+{
+ _background_polling = polling;
+}
+
+void init_ignore_suspended_devices(int ignore)
+{
+ _ignore_suspended_devices = ignore;
+}
+
+void init_cmd_name(int status)
+{
+ _log_cmd_name = status;
+}
+
+void init_is_static(unsigned value)
+{
+ _is_static = value;
+}
+
+void init_udev_checking(int checking)
+{
+ if ((_udev_checking = checking))
+ log_debug("LVM udev checking enabled");
+ else
+ log_debug("LVM udev checking disabled");
+}
+
+void init_dev_disable_after_error_count(int value)
+{
+ _dev_disable_after_error_count = value;
+}
+
+void set_cmd_name(const char *cmd)
+{
+ strncpy(_cmd_name, cmd, sizeof(_cmd_name));
+ _cmd_name[sizeof(_cmd_name) - 1] = '\0';
+}
+
+void set_sysfs_dir_path(const char *path)
+{
+ strncpy(_sysfs_dir_path, path, sizeof(_sysfs_dir_path));
+ _sysfs_dir_path[sizeof(_sysfs_dir_path) - 1] = '\0';
+}
+
+const char *log_command_name()
+{
+ if (!_log_cmd_name)
+ return "";
+
+ return _cmd_name;
+}
+
+void init_error_message_produced(int value)
+{
+ _error_message_produced = value;
+}
+
+int error_message_produced(void)
+{
+ return _error_message_produced;
+}
+
+int test_mode()
+{
+ return _test;
+}
+
+int md_filtering()
+{
+ return _md_filtering;
+}
+
+int pvmove_mode()
+{
+ return _pvmove;
+}
+
+int full_scan_done()
+{
+ return _full_scan_done;
+}
+
+int trust_cache()
+{
+ return _trust_cache;
+}
+
+int background_polling()
+{
+ return _background_polling;
+}
+
+int ignorelockingfailure()
+{
+ return _ignorelockingfailure;
+}
+
+int security_level()
+{
+ return _security_level;
+}
+
+int mirror_in_sync(void)
+{
+ return _mirror_in_sync;
+}
+
+int dmeventd_monitor_mode(void)
+{
+ return _dmeventd_monitor;
+}
+
+int ignore_suspended_devices(void)
+{
+ return _ignore_suspended_devices;
+}
+
+void init_debug(int level)
+{
+ _debug_level = level;
+}
+
+int verbose_level()
+{
+ return _verbose_level;
+}
+
+int debug_level()
+{
+ return _debug_level;
+}
+
+unsigned is_static(void)
+{
+ return _is_static;
+}
+
+int udev_checking(void)
+{
+ return _udev_checking;
+}
+
+const char *sysfs_dir_path()
+{
+ return _sysfs_dir_path;
+}
+
+int dev_disable_after_error_count(void)
+{
+ return _dev_disable_after_error_count;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_GLOBALS_H
+#define _LVM_GLOBALS_H
+
+#define VERBOSE_BASE_LEVEL _LOG_WARN
+#define SECURITY_LEVEL 0
+
+void init_verbose(int level);
+void init_test(int level);
+void init_md_filtering(int level);
+void init_pvmove(int level);
+void init_full_scan_done(int level);
+void init_trust_cache(int trustcache);
+void init_debug(int level);
+void init_cmd_name(int status);
+void init_ignorelockingfailure(int level);
+void init_lockingfailed(int level);
+void init_security_level(int level);
+void init_mirror_in_sync(int in_sync);
+void init_dmeventd_monitor(int reg);
+void init_background_polling(int polling);
+void init_ignore_suspended_devices(int ignore);
+void init_error_message_produced(int produced);
+void init_is_static(unsigned value);
+void init_udev_checking(int checking);
+void init_dev_disable_after_error_count(int value);
+
+void set_cmd_name(const char *cmd_name);
+void set_sysfs_dir_path(const char *path);
+
+int test_mode(void);
+int md_filtering(void);
+int pvmove_mode(void);
+int full_scan_done(void);
+int trust_cache(void);
+int verbose_level(void);
+int debug_level(void);
+int ignorelockingfailure(void);
+int lockingfailed(void);
+int security_level(void);
+int mirror_in_sync(void);
+int background_polling(void);
+int ignore_suspended_devices(void);
+const char *log_command_name(void);
+unsigned is_static(void);
+int udev_checking(void);
+const char *sysfs_dir_path(void);
+
+#define DMEVENTD_MONITOR_IGNORE -1
+int dmeventd_monitor_mode(void);
+
+#define NO_DEV_ERROR_COUNT_LIMIT 0
+int dev_disable_after_error_count(void);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lvm-percent.h"
+
+float percent_to_float(percent_t v)
+{
+ return (float)v / PERCENT_1;
+}
+
+percent_t make_percent(uint64_t numerator, uint64_t denominator)
+{
+ percent_t percent;
+ if (!denominator)
+ return PERCENT_100; /* FIXME? */
+ if (!numerator)
+ return PERCENT_0;
+ if (numerator == denominator)
+ return PERCENT_100;
+ switch (percent = PERCENT_100 * ((double) numerator / (double) denominator)) {
+ case PERCENT_100:
+ return PERCENT_100 - 1;
+ case PERCENT_0:
+ return PERCENT_0 + 1;
+ default:
+ return percent;
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_PERCENT_H
+#define _LVM_PERCENT_H
+#include <stdint.h>
+
+/*
+ * A fixed-point representation of percent values. One percent equals to
+ * PERCENT_1 as defined below. Values that are not multiples of PERCENT_1
+ * represent fractions, with precision of 1/1000000 of a percent. See
+ * percent_to_float for a conversion to a floating-point representation.
+ *
+ * You should always use make_percent when building percent_t values. The
+ * implementation of make_percent is biased towards the middle: it ensures that
+ * the result is PERCENT_0 or PERCENT_100 if and only if this is the actual
+ * value -- it never rounds any intermediate value (> 0 or < 100) to either 0
+ * or 100.
+ */
+typedef int32_t percent_t;
+
+typedef enum {
+ PERCENT_0 = 0,
+ PERCENT_1 = 1000000,
+ PERCENT_100 = 100 * PERCENT_1,
+ PERCENT_INVALID = -1
+} percent_range_t;
+
+float percent_to_float(percent_t v);
+percent_t make_percent(uint64_t numerator, uint64_t denominator);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "lvm-string.h"
+
+#include <ctype.h>
+
+int emit_to_buffer(char **buffer, size_t *size, const char *fmt, ...)
+{
+ int n;
+ va_list ap;
+
+ va_start(ap, fmt);
+ n = vsnprintf(*buffer, *size, fmt, ap);
+ va_end(ap);
+
+ /*
+ * Revert to old glibc behaviour (version <= 2.0.6) where snprintf
+ * returned -1 if buffer was too small. From glibc 2.1 it returns number
+ * of chars that would have been written had there been room.
+ */
+ if (n < 0 || ((unsigned) n + 1 > *size))
+ n = -1;
+
+ if (n < 0 || ((size_t)n == *size))
+ return 0;
+
+ *buffer += n;
+ *size -= n;
+ return 1;
+}
+
+/*
+ * Count occurences of 'c' in 'str' until we reach a null char.
+ *
+ * Returns:
+ * len - incremented for each char we encounter.
+ * count - number of occurrences of 'c' and 'c2'.
+ */
+static void _count_chars(const char *str, size_t *len, int *count,
+ const int c1, const int c2)
+{
+ const char *ptr;
+
+ for (ptr = str; *ptr; ptr++, (*len)++)
+ if (*ptr == c1 || *ptr == c2)
+ (*count)++;
+}
+
+/*
+ * Count occurences of 'c' in 'str' of length 'size'.
+ *
+ * Returns:
+ * Number of occurrences of 'c'
+ */
+unsigned count_chars(const char *str, size_t len, const int c)
+{
+ size_t i;
+ unsigned count = 0;
+
+ for (i = 0; i < len; i++)
+ if (str[i] == c)
+ count++;
+
+ return count;
+}
+
+/*
+ * Length of string after escaping double quotes and backslashes.
+ */
+size_t escaped_len(const char *str)
+{
+ size_t len = 1;
+ int count = 0;
+
+ _count_chars(str, &len, &count, '\"', '\\');
+
+ return count + len;
+}
+
+/*
+ * Copies a string, quoting orig_char with quote_char.
+ * Optionally also quote quote_char.
+ */
+static void _quote_characters(char **out, const char *src,
+ const int orig_char, const int quote_char,
+ int quote_quote_char)
+{
+ while (*src) {
+ if (*src == orig_char ||
+ (*src == quote_char && quote_quote_char))
+ *(*out)++ = quote_char;
+
+ *(*out)++ = *src++;
+ }
+}
+
+static void _unquote_one_character(char *src, const char orig_char,
+ const char quote_char)
+{
+ char *out;
+ char s, n;
+
+ /* Optimise for the common case where no changes are needed. */
+ while ((s = *src++)) {
+ if (s == quote_char &&
+ ((n = *src) == orig_char || n == quote_char)) {
+ out = src++;
+ *(out - 1) = n;
+
+ while ((s = *src++)) {
+ if (s == quote_char &&
+ ((n = *src) == orig_char || n == quote_char)) {
+ s = n;
+ src++;
+ }
+ *out = s;
+ out++;
+ }
+
+ *out = '\0';
+ return;
+ }
+ }
+}
+
+/*
+ * Unquote each character given in orig_char array and unquote quote_char
+ * as well. Also save the first occurrence of each character from orig_char
+ * that was found unquoted in arr_substr_first_unquoted array. This way we can
+ * process several characters in one go.
+ */
+static void _unquote_characters(char *src, const char *orig_chars,
+ const int num_orig_chars,
+ const char quote_char,
+ char *arr_substr_first_unquoted[])
+{
+ char *out = src;
+ char c, s, n;
+ unsigned i;
+
+ while ((s = *src++)) {
+ for (i = 0; i < num_orig_chars; i++) {
+ c = orig_chars[i];
+ if (s == quote_char &&
+ ((n = *src) == c || n == quote_char)) {
+ s = n;
+ src++;
+ break;
+ }
+ if (arr_substr_first_unquoted && (s == c) &&
+ !arr_substr_first_unquoted[i])
+ arr_substr_first_unquoted[i] = out;
+ };
+ *out++ = s;
+ }
+
+ *out = '\0';
+}
+
+/*
+ * Copies a string, quoting hyphens with hyphens.
+ */
+static void _quote_hyphens(char **out, const char *src)
+{
+ _quote_characters(out, src, '-', '-', 0);
+}
+
+/*
+ * <vg>-<lv>-<layer> or if !layer just <vg>-<lv>.
+ */
+char *build_dm_name(struct dm_pool *mem, const char *vgname,
+ const char *lvname, const char *layer)
+{
+ size_t len = 1;
+ int hyphens = 1;
+ char *r, *out;
+
+ _count_chars(vgname, &len, &hyphens, '-', 0);
+ _count_chars(lvname, &len, &hyphens, '-', 0);
+
+ if (layer && *layer) {
+ _count_chars(layer, &len, &hyphens, '-', 0);
+ hyphens++;
+ }
+
+ len += hyphens;
+
+ if (!(r = dm_pool_alloc(mem, len))) {
+ log_error("build_dm_name: Allocation failed for %" PRIsize_t
+ " for %s %s %s.", len, vgname, lvname, layer);
+ return NULL;
+ }
+
+ out = r;
+ _quote_hyphens(&out, vgname);
+ *out++ = '-';
+ _quote_hyphens(&out, lvname);
+
+ if (layer && *layer) {
+ /* No hyphen if the layer begins with _ e.g. _mlog */
+ if (*layer != '_')
+ *out++ = '-';
+ _quote_hyphens(&out, layer);
+ }
+ *out = '\0';
+
+ return r;
+}
+
+char *build_dm_uuid(struct dm_pool *mem, const char *lvid, const char *layer)
+{
+ char *dmuuid;
+ size_t len;
+
+ if (!layer)
+ layer = "";
+
+ len = sizeof(UUID_PREFIX) + strlen(lvid) + strlen(layer) + 1;
+
+ if (!(dmuuid = dm_pool_alloc(mem, len))) {
+ log_error("build_dm_name: Allocation failed for %" PRIsize_t
+ " %s %s.", len, lvid, layer);
+ return NULL;
+ }
+
+ sprintf(dmuuid, UUID_PREFIX "%s%s%s", lvid, (*layer) ? "-" : "", layer);
+
+ return dmuuid;
+}
+
+/*
+ * Copies a string, quoting double quotes with backslashes.
+ */
+char *escape_double_quotes(char *out, const char *src)
+{
+ char *buf = out;
+
+ _quote_characters(&buf, src, '\"', '\\', 1);
+ *buf = '\0';
+
+ return out;
+}
+
+/*
+ * Undo quoting in situ.
+ */
+void unescape_double_quotes(char *src)
+{
+ _unquote_one_character(src, '\"', '\\');
+}
+
+/*
+ * Unescape colons and "at" signs in situ and save the substrings
+ * starting at the position of the first unescaped colon and the
+ * first unescaped "at" sign. This is normally used to unescape
+ * device names used as PVs.
+ */
+void unescape_colons_and_at_signs(char *src,
+ char **substr_first_unquoted_colon,
+ char **substr_first_unquoted_at_sign)
+{
+ const char *orig_chars = ":@";
+ char *arr_substr_first_unquoted[] = {NULL, NULL, NULL};
+
+ _unquote_characters(src, orig_chars, 2, '\\', arr_substr_first_unquoted);
+
+ if (substr_first_unquoted_colon)
+ *substr_first_unquoted_colon = arr_substr_first_unquoted[0];
+
+ if (substr_first_unquoted_at_sign)
+ *substr_first_unquoted_at_sign = arr_substr_first_unquoted[1];
+}
+
+/*
+ * A-Za-z0-9._-+/=!:&#
+ */
+int validate_tag(const char *n)
+{
+ register char c;
+ register int len = 0;
+
+ if (!n || !*n)
+ return 0;
+
+ while ((len++, c = *n++))
+ if (!isalnum(c) && c != '.' && c != '_' && c != '-' && c != '+' && c != '/'
+ && c != '=' && c != '!' && c != ':' && c != '&' && c != '#')
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Device layer names are all of the form <vg>-<lv>-<layer>, any
+ * other hyphens that appear in these names are quoted with yet
+ * another hyphen. The top layer of any device has no layer
+ * name. eg, vg0-lvol0.
+ */
+int validate_name(const char *n)
+{
+ register char c;
+ register int len = 0;
+
+ if (!n || !*n)
+ return 0;
+
+ /* Hyphen used as VG-LV separator - ambiguity if LV starts with it */
+ if (*n == '-')
+ return 0;
+
+ if (!strcmp(n, ".") || !strcmp(n, ".."))
+ return 0;
+
+ while ((len++, c = *n++))
+ if (!isalnum(c) && c != '.' && c != '_' && c != '-' && c != '+')
+ return 0;
+
+ if (len > NAME_LEN)
+ return 0;
+
+ return 1;
+}
+
+int apply_lvname_restrictions(const char *name)
+{
+ if (!strncmp(name, "snapshot", 8)) {
+ log_error("Names starting \"snapshot\" are reserved. "
+ "Please choose a different LV name.");
+ return 0;
+ }
+
+ if (!strncmp(name, "pvmove", 6)) {
+ log_error("Names starting \"pvmove\" are reserved. "
+ "Please choose a different LV name.");
+ return 0;
+ }
+
+ if (strstr(name, "_mlog")) {
+ log_error("Names including \"_mlog\" are reserved. "
+ "Please choose a different LV name.");
+ return 0;
+ }
+
+ if (strstr(name, "_mimage")) {
+ log_error("Names including \"_mimage\" are reserved. "
+ "Please choose a different LV name.");
+ return 0;
+ }
+
+ if (strstr(name, "_vorigin")) {
+ log_error("Names including \"_vorigin\" are reserved. "
+ "Please choose a different LV name.");
+ return 0;
+ }
+
+ return 1;
+}
+
+int is_reserved_lvname(const char *name)
+{
+ int rc, old_suppress;
+
+ old_suppress = log_suppress(2);
+ rc = !apply_lvname_restrictions(name);
+ log_suppress(old_suppress);
+
+ return rc;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_STRING_H
+#define _LVM_STRING_H
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#define NAME_LEN 128
+#define UUID_PREFIX "LVM-"
+
+struct pool;
+
+int emit_to_buffer(char **buffer, size_t *size, const char *fmt, ...)
+ __attribute__ ((format(printf, 3, 4)));
+
+char *build_dm_name(struct dm_pool *mem, const char *vg,
+ const char *lv, const char *layer);
+char *build_dm_uuid(struct dm_pool *mem, const char *lvid,
+ const char *layer);
+
+int validate_name(const char *n);
+int validate_tag(const char *n);
+
+int apply_lvname_restrictions(const char *name);
+int is_reserved_lvname(const char *name);
+
+/*
+ * Returns number of occurrences of c in first len characters of str.
+ */
+unsigned count_chars(const char *str, size_t len, const int c);
+
+/*
+ * Returns what length of escaped string would be including terminating NUL.
+ */
+size_t escaped_len(const char *str);
+
+/*
+ * Copies a string from src to out.
+ * Double quotation marks and backslashes are quoted with a backslash.
+ * Caller must ensure *out has enough space - see escaped_len().
+ * Returns *out.
+ */
+char *escape_double_quotes(char *out, const char *src);
+
+/*
+ * Removes quoting of double quotation marks and backslashes in situ.
+ */
+void unescape_double_quotes(char *src);
+
+/*
+ * Unescape colons and at signs in situ and save the substring starting
+ * at the position of the first unescaped colon and the first unescaped
+ * "at" sign.
+ */
+void unescape_colons_and_at_signs(char *src,
+ char **substr_first_unquoted_colon,
+ char **substr_first_unquoted_at_sign);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_VERSION_H
+/**
+ * The LVM version number
+ *
+ * LVM_MAJOR.LVM_MINOR.LVM_PATCHLEVEL(LVM_LIBAPI)[-LVM_RELEASE]
+ */
+
+#define LVM_VERSION @LVM_VERSION@
+#define LVM_MAJOR @LVM_MAJOR@
+#define LVM_MINOR @LVM_MINOR@
+#define LVM_PATCHLEVEL @LVM_PATCHLEVEL@
+#define LVM_LIBAPI @LVM_LIBAPI@
+#define LVM_RELEASE @LVM_RELEASE@
+#define LVM_RELEASE_DATE @LVM_RELEASE_DATE@
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+int lvm_getpagesize(void)
+{
+ return getpagesize();
+}
+
+int read_urandom(void *buf, size_t len)
+{
+ int fd;
+
+ /* FIXME: we should stat here, and handle other cases */
+ /* FIXME: use common _io() routine's open/read/close */
+ if ((fd = open("/dev/urandom", O_RDONLY)) < 0) {
+ log_sys_error("open", "read_urandom: /dev/urandom");
+ return 0;
+ }
+
+ if (read(fd, buf, len) != (ssize_t) len) {
+ log_sys_error("read", "read_urandom: /dev/urandom");
+ if (close(fd))
+ stack;
+ return 0;
+ }
+
+ if (close(fd))
+ stack;
+
+ return 1;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_WRAPPERS_H
+#define _LVM_WRAPPERS_H
+
+int lvm_getpagesize(void);
+
+/*
+ * Read 'len' bytes of entropy from /dev/urandom and store in 'buf'.
+ */
+int read_urandom(void *buf, size_t len);
+
+# ifndef HAVE_SIGINTERRUPT
+# define siginterrupt(sig, flag) \
+ do { \
+ int ret; \
+ struct sigaction act; \
+ (void) sigaction(sig, NULL, &act); \
+ if (flag) \
+ act.sa_flags &= SA_RESTART; \
+ else \
+ act.sa_flags |= SA_RESTART; \
+ ret = sigaction(sig, &act, NULL); \
+ return ret; \
+ while (0)
+# endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "config.h"
+#include "lvm-string.h"
+#include "sharedlib.h"
+#include "toolcontext.h"
+
+#include <limits.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+
+void get_shared_library_path(struct cmd_context *cmd, const char *libname,
+ char *path, size_t path_len)
+{
+ struct stat info;
+ const char *lib_dir;
+
+ /* If libname doesn't begin with '/' then use lib_dir/libname,
+ * if present */
+ if (libname[0] == '/' ||
+ !(lib_dir = find_config_tree_str(cmd, "global/library_dir", 0)) ||
+ (dm_snprintf(path, path_len, "%s/%s", lib_dir,
+ libname) == -1) || stat(path, &info) == -1)
+ strncpy(path, libname, path_len);
+}
+
+void *load_shared_library(struct cmd_context *cmd, const char *libname,
+ const char *desc, int silent)
+{
+ char path[PATH_MAX];
+ void *library;
+
+ if (is_static()) {
+ log_error("Not loading shared %s library %s in static mode.",
+ desc, libname);
+ return NULL;
+ }
+
+ get_shared_library_path(cmd, libname, path, sizeof(path));
+
+ log_very_verbose("Opening shared %s library %s", desc, path);
+
+ if (!(library = dlopen(path, RTLD_LAZY | RTLD_GLOBAL))) {
+ if (silent && ignorelockingfailure())
+ log_verbose("Unable to open external %s library %s: %s",
+ desc, path, dlerror());
+ else
+ log_error("Unable to open external %s library %s: %s",
+ desc, path, dlerror());
+ }
+
+ return library;
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_SHAREDLIB_H
+#define _LVM_SHAREDLIB_H
+
+#include "config.h"
+#include <dlfcn.h>
+
+void get_shared_library_path(struct cmd_context *cmd, const char *libname,
+ char *path, size_t path_len);
+void *load_shared_library(struct cmd_context *cmd, const char *libname,
+ const char *what, int silent);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2006 Rackable Systems 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Abstract out the time methods used so they can be adjusted later -
+ * the results of these routines should stay in-core. This implementation
+ * requires librt.
+ */
+
+#include "lib.h"
+#include <stdlib.h>
+
+#include "timestamp.h"
+
+/*
+ * The realtime section uses clock_gettime with the CLOCK_MONOTONIC
+ * parameter to prevent issues with time warps
+ */
+#ifdef HAVE_REALTIME
+
+#include <time.h>
+#include <bits/time.h>
+
+struct timestamp {
+ struct timespec t;
+};
+
+struct timestamp *get_timestamp(void)
+{
+ struct timestamp *ts = NULL;
+
+ if (!(ts = dm_malloc(sizeof(*ts))))
+ return_NULL;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts->t)) {
+ log_sys_error("clock_gettime", "get_timestamp");
+ return NULL;
+ }
+
+ return ts;
+}
+
+/* cmp_timestamp: Compare two timestamps
+ *
+ * Return: -1 if t1 is less than t2
+ * 0 if t1 is equal to t2
+ * 1 if t1 is greater than t2
+ */
+int cmp_timestamp(struct timestamp *t1, struct timestamp *t2)
+{
+ if(t1->t.tv_sec < t2->t.tv_sec)
+ return -1;
+ if(t1->t.tv_sec > t2->t.tv_sec)
+ return 1;
+
+ if(t1->t.tv_nsec < t2->t.tv_nsec)
+ return -1;
+ if(t1->t.tv_nsec > t2->t.tv_nsec)
+ return 1;
+
+ return 0;
+}
+
+#else /* ! HAVE_REALTIME */
+
+/*
+ * The !realtime section just uses gettimeofday and is therefore subject
+ * to ntp-type time warps - not sure if should allow that.
+ */
+
+#include <sys/time.h>
+
+struct timestamp {
+ struct timeval t;
+};
+
+struct timestamp *get_timestamp(void)
+{
+ struct timestamp *ts = NULL;
+
+ if (!(ts = dm_malloc(sizeof(*ts))))
+ return_NULL;
+
+ if (gettimeofday(&ts->t, NULL)) {
+ log_sys_error("gettimeofday", "get_timestamp");
+ return NULL;
+ }
+
+ return ts;
+}
+
+/* cmp_timestamp: Compare two timestamps
+ *
+ * Return: -1 if t1 is less than t2
+ * 0 if t1 is equal to t2
+ * 1 if t1 is greater than t2
+ */
+int cmp_timestamp(struct timestamp *t1, struct timestamp *t2)
+{
+ if(t1->t.tv_sec < t2->t.tv_sec)
+ return -1;
+ if(t1->t.tv_sec > t2->t.tv_sec)
+ return 1;
+
+ if(t1->t.tv_usec < t2->t.tv_usec)
+ return -1;
+ if(t1->t.tv_usec > t2->t.tv_usec)
+ return 1;
+
+ return 0;
+}
+
+#endif /* HAVE_REALTIME */
+
+void destroy_timestamp(struct timestamp *t)
+{
+ if (t)
+ dm_free(t);
+}
--- /dev/null
+/*
+ * Copyright (C) 2006 Rackable Systems 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_TIMESTAMP_H
+#define _LVM_TIMESTAMP_H
+
+struct timestamp;
+
+struct timestamp *get_timestamp(void);
+
+/* cmp_timestamp: Compare two timestamps
+ *
+ * Return: -1 if t1 is less than t2
+ * 0 if t1 is equal to t2
+ * 1 if t1 is greater than t2
+ */
+int cmp_timestamp(struct timestamp *t1, struct timestamp *t2);
+
+void destroy_timestamp(struct timestamp *t);
+
+#endif /* _LVM_TIMESTAMP_H */
+
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Return the address of the last file name component of NAME.
+ * If NAME ends in a slash, return the empty string.
+ */
+
+#include "lib.h"
+
+/* empty for now. */
--- /dev/null
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_UTIL_H
+#define _LVM_UTIL_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; })
+
+#ifdef __clang__
+#define uninitialized_var(x) x
+#else
+#define uninitialized_var(x) x = x
+#endif
+
+#define KERNEL_VERSION(major, minor, release) (((major) << 16) + ((minor) << 8) + (release))
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "memlock.h"
+#include "defaults.h"
+#include "config.h"
+#include "toolcontext.h"
+
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#ifndef DEVMAPPER_SUPPORT
+
+void memlock_inc(struct cmd_context *cmd)
+{
+ return;
+}
+void memlock_dec(struct cmd_context *cmd)
+{
+ return;
+}
+int memlock(void)
+{
+ return 0;
+}
+void memlock_init(struct cmd_context *cmd)
+{
+ return;
+}
+
+#else /* DEVMAPPER_SUPPORT */
+
+static size_t _size_stack;
+static size_t _size_malloc_tmp;
+static size_t _size_malloc = 2000000;
+
+static void *_malloc_mem = NULL;
+static int _memlock_count = 0;
+static int _memlock_count_daemon = 0;
+static int _priority;
+static int _default_priority;
+
+/* list of maps, that are unconditionaly ignored */
+static const char * const _ignore_maps[] = {
+ "[vdso]",
+ "[vsyscall]",
+};
+
+/* default blacklist for maps */
+static const char * const _blacklist_maps[] = {
+ "locale/locale-archive",
+ "gconv/gconv-modules.cache",
+ "/libreadline.so.", /* not using readline during mlock */
+ "/libncurses.so.", /* not using readline during mlock */
+ "/libdl-", /* not using dlopen,dlsym during mlock */
+ /* "/libdevmapper-event.so" */
+};
+
+typedef enum { LVM_MLOCK, LVM_MUNLOCK } lvmlock_t;
+
+static unsigned _use_mlockall;
+static int _maps_fd;
+static size_t _maps_len = 8192; /* Initial buffer size for reading /proc/self/maps */
+static char *_maps_buffer;
+static char _procselfmaps[PATH_MAX] = "";
+#define SELF_MAPS "/self/maps"
+
+static size_t _mstats; /* statistic for maps locking */
+
+static void _touch_memory(void *mem, size_t size)
+{
+ size_t pagesize = lvm_getpagesize();
+ char *pos = mem;
+ char *end = pos + size - sizeof(long);
+
+ while (pos < end) {
+ *(long *) pos = 1;
+ pos += pagesize;
+ }
+}
+
+static void _allocate_memory(void)
+{
+ void *stack_mem, *temp_malloc_mem;
+
+ if ((stack_mem = alloca(_size_stack)))
+ _touch_memory(stack_mem, _size_stack);
+
+ if ((temp_malloc_mem = malloc(_size_malloc_tmp)))
+ _touch_memory(temp_malloc_mem, _size_malloc_tmp);
+
+ if ((_malloc_mem = malloc(_size_malloc)))
+ _touch_memory(_malloc_mem, _size_malloc);
+
+ free(temp_malloc_mem);
+}
+
+static void _release_memory(void)
+{
+ free(_malloc_mem);
+}
+
+/*
+ * mlock/munlock memory areas from /proc/self/maps
+ * format described in kernel/Documentation/filesystem/proc.txt
+ */
+static int _maps_line(const struct config_node *cn, lvmlock_t lock,
+ const char* line, size_t* mstats)
+{
+ const struct config_value *cv;
+ long from, to;
+ int pos, i;
+ char fr, fw, fx, fp;
+ size_t sz;
+
+ if (sscanf(line, "%lx-%lx %c%c%c%c%n",
+ &from, &to, &fr, &fw, &fx, &fp, &pos) != 6) {
+ log_error("Failed to parse maps line: %s", line);
+ return 0;
+ }
+
+ /* Select readable maps */
+ if (fr != 'r') {
+ log_debug("%s area unreadable %s : Skipping.",
+ (lock == LVM_MLOCK) ? "mlock" : "munlock", line);
+ return 1;
+ }
+
+ /* always ignored areas */
+ for (i = 0; i < sizeof(_ignore_maps) / sizeof(_ignore_maps[0]); ++i)
+ if (strstr(line + pos, _ignore_maps[i])) {
+ log_debug("mlock ignore filter '%s' matches '%s': Skipping.",
+ _ignore_maps[i], line);
+ return 1;
+ }
+
+ sz = to - from;
+ if (!cn) {
+ /* If no blacklist configured, use an internal set */
+ for (i = 0; i < sizeof(_blacklist_maps) / sizeof(_blacklist_maps[0]); ++i)
+ if (strstr(line + pos, _blacklist_maps[i])) {
+ log_debug("mlock default filter '%s' matches '%s': Skipping.",
+ _blacklist_maps[i], line);
+ return 1;
+ }
+ } else {
+ for (cv = cn->v; cv; cv = cv->next) {
+ if ((cv->type != CFG_STRING) || !cv->v.str[0])
+ continue;
+ if (strstr(line + pos, cv->v.str)) {
+ log_debug("mlock_filter '%s' matches '%s': Skipping.",
+ cv->v.str, line);
+ return 1;
+ }
+ }
+ }
+
+ *mstats += sz;
+ log_debug("%s %10ldKiB %12lx - %12lx %c%c%c%c%s",
+ (lock == LVM_MLOCK) ? "mlock" : "munlock",
+ ((long)sz + 1023) / 1024, from, to, fr, fw, fx, fp, line + pos);
+
+ if (lock == LVM_MLOCK) {
+ if (mlock((const void*)from, sz) < 0) {
+ log_sys_error("mlock", line);
+ return 0;
+ }
+ } else {
+ if (munlock((const void*)from, sz) < 0) {
+ log_sys_error("munlock", line);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int _memlock_maps(struct cmd_context *cmd, lvmlock_t lock, size_t *mstats)
+{
+ const struct config_node *cn;
+ char *line, *line_end;
+ size_t len;
+ ssize_t n;
+ int ret = 1;
+
+ if (_use_mlockall) {
+#ifdef MCL_CURRENT
+ if (lock == LVM_MLOCK) {
+ if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
+ log_sys_error("mlockall", "");
+ return 0;
+ }
+ } else {
+ if (munlockall()) {
+ log_sys_error("munlockall", "");
+ return 0;
+ }
+ }
+ return 1;
+#else
+ return 0;
+#endif
+ }
+
+ /* Force libc.mo load */
+ if (lock == LVM_MLOCK)
+ (void)strerror(0);
+ /* Reset statistic counters */
+ *mstats = 0;
+
+ /* read mapping into a single memory chunk without reallocation
+ * in the middle of reading maps file */
+ for (len = 0;;) {
+ if (!_maps_buffer || len >= _maps_len) {
+ if (_maps_buffer)
+ _maps_len *= 2;
+ if (!(_maps_buffer = dm_realloc(_maps_buffer, _maps_len))) {
+ log_error("Allocation of maps buffer failed");
+ return 0;
+ }
+ }
+ lseek(_maps_fd, 0, SEEK_SET);
+ for (len = 0 ; len < _maps_len; len += n) {
+ if (!(n = read(_maps_fd, _maps_buffer + len, _maps_len - len))) {
+ _maps_buffer[len] = '\0';
+ break; /* EOF */
+ }
+ if (n == -1)
+ return_0;
+ }
+ if (len < _maps_len) /* fits in buffer */
+ break;
+ }
+
+ line = _maps_buffer;
+ cn = find_config_tree_node(cmd, "activation/mlock_filter");
+
+ while ((line_end = strchr(line, '\n'))) {
+ *line_end = '\0'; /* remove \n */
+ if (!_maps_line(cn, lock, line, mstats))
+ ret = 0;
+ line = line_end + 1;
+ }
+
+ log_debug("%socked %ld bytes",
+ (lock == LVM_MLOCK) ? "L" : "Unl", (long)*mstats);
+
+ return ret;
+}
+
+/* Stop memory getting swapped out */
+static void _lock_mem(struct cmd_context *cmd)
+{
+ _allocate_memory();
+
+ /*
+ * For daemon we need to use mlockall()
+ * so even future adition of thread which may not even use lvm lib
+ * will not block memory locked thread
+ * Note: assuming _memlock_count_daemon is updated before _memlock_count
+ */
+ _use_mlockall = _memlock_count_daemon ? 1 :
+ find_config_tree_bool(cmd, "activation/use_mlockall", DEFAULT_USE_MLOCKALL);
+
+ if (!_use_mlockall) {
+ if (!*_procselfmaps &&
+ dm_snprintf(_procselfmaps, sizeof(_procselfmaps),
+ "%s" SELF_MAPS, cmd->proc_dir) < 0) {
+ log_error("proc_dir too long");
+ return;
+ }
+
+ if (!(_maps_fd = open(_procselfmaps, O_RDONLY))) {
+ log_sys_error("open", _procselfmaps);
+ return;
+ }
+ }
+
+ log_very_verbose("Locking memory");
+ if (!_memlock_maps(cmd, LVM_MLOCK, &_mstats))
+ stack;
+
+ errno = 0;
+ if (((_priority = getpriority(PRIO_PROCESS, 0)) == -1) && errno)
+ log_sys_error("getpriority", "");
+ else
+ if (setpriority(PRIO_PROCESS, 0, _default_priority))
+ log_error("setpriority %d failed: %s",
+ _default_priority, strerror(errno));
+}
+
+static void _unlock_mem(struct cmd_context *cmd)
+{
+ size_t unlock_mstats;
+
+ log_very_verbose("Unlocking memory");
+
+ if (!_memlock_maps(cmd, LVM_MUNLOCK, &unlock_mstats))
+ stack;
+
+ if (!_use_mlockall) {
+ if (close(_maps_fd))
+ log_sys_error("close", _procselfmaps);
+ dm_free(_maps_buffer);
+ _maps_buffer = NULL;
+ if (_mstats < unlock_mstats)
+ log_error(INTERNAL_ERROR "Maps lock %ld < unlock %ld",
+ (long)_mstats, (long)unlock_mstats);
+ }
+
+ if (setpriority(PRIO_PROCESS, 0, _priority))
+ log_error("setpriority %u failed: %s", _priority,
+ strerror(errno));
+ _release_memory();
+}
+
+static void _lock_mem_if_needed(struct cmd_context *cmd)
+{
+ if ((_memlock_count + _memlock_count_daemon) == 1)
+ _lock_mem(cmd);
+}
+
+static void _unlock_mem_if_possible(struct cmd_context *cmd)
+{
+ if ((_memlock_count + _memlock_count_daemon) == 0)
+ _unlock_mem(cmd);
+}
+
+void memlock_inc(struct cmd_context *cmd)
+{
+ ++_memlock_count;
+ _lock_mem_if_needed(cmd);
+ log_debug("memlock_count inc to %d", _memlock_count);
+}
+
+void memlock_dec(struct cmd_context *cmd)
+{
+ if (!_memlock_count)
+ log_error(INTERNAL_ERROR "_memlock_count has dropped below 0.");
+ --_memlock_count;
+ _unlock_mem_if_possible(cmd);
+ log_debug("memlock_count dec to %d", _memlock_count);
+}
+
+/*
+ * The memlock_*_daemon functions will force the mlockall() call that we need
+ * to stay in memory, but they will have no effect on device scans (unlike
+ * normal memlock_inc and memlock_dec). Memory is kept locked as long as either
+ * of memlock or memlock_daemon is in effect.
+ */
+
+void memlock_inc_daemon(struct cmd_context *cmd)
+{
+ ++_memlock_count_daemon;
+ if (_memlock_count_daemon == 1 && _memlock_count > 0)
+ log_error(INTERNAL_ERROR "_memlock_inc_daemon used after _memlock_inc.");
+ _lock_mem_if_needed(cmd);
+ log_debug("memlock_count_daemon inc to %d", _memlock_count_daemon);
+}
+
+void memlock_dec_daemon(struct cmd_context *cmd)
+{
+ if (!_memlock_count_daemon)
+ log_error(INTERNAL_ERROR "_memlock_count_daemon has dropped below 0.");
+ --_memlock_count_daemon;
+ _unlock_mem_if_possible(cmd);
+ log_debug("memlock_count_daemon dec to %d", _memlock_count_daemon);
+}
+
+/*
+ * This disregards the daemon (dmeventd) locks, since we use memlock() to check
+ * whether it is safe to run a device scan, which would normally coincide with
+ * !memlock() -- but the daemon global memory lock breaks this assumption, so
+ * we do not take those into account here.
+ */
+int memlock(void)
+{
+ return _memlock_count;
+}
+
+void memlock_init(struct cmd_context *cmd)
+{
+ _size_stack = find_config_tree_int(cmd,
+ "activation/reserved_stack",
+ DEFAULT_RESERVED_STACK) * 1024;
+ _size_malloc_tmp = find_config_tree_int(cmd,
+ "activation/reserved_memory",
+ DEFAULT_RESERVED_MEMORY) * 1024;
+ _default_priority = find_config_tree_int(cmd,
+ "activation/process_priority",
+ DEFAULT_PROCESS_PRIORITY);
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LVM_MEMLOCK_H
+#define LVM_MEMLOCK_H
+
+struct cmd_context;
+
+void memlock_inc(struct cmd_context *cmd);
+void memlock_dec(struct cmd_context *cmd);
+void memlock_inc_daemon(struct cmd_context *cmd);
+void memlock_dec_daemon(struct cmd_context *cmd);
+int memlock(void);
+void memlock_init(struct cmd_context *cmd);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_XLATE_H
+#define _LVM_XLATE_H
+
+#ifdef linux
+# include <endian.h>
+# include <byteswap.h>
+#else
+# include <machine/endian.h>
+# define bswap_16(x) (((x) & 0x00ffU) << 8 | \
+ ((x) & 0xff00U) >> 8)
+# define bswap_32(x) (((x) & 0x000000ffU) << 24 | \
+ ((x) & 0xff000000U) >> 24 | \
+ ((x) & 0x0000ff00U) << 8 | \
+ ((x) & 0x00ff0000U) >> 8)
+# define bswap_64(x) (((x) & 0x00000000000000ffULL) << 56 | \
+ ((x) & 0xff00000000000000ULL) >> 56 | \
+ ((x) & 0x000000000000ff00ULL) << 40 | \
+ ((x) & 0x00ff000000000000ULL) >> 40 | \
+ ((x) & 0x0000000000ff0000ULL) << 24 | \
+ ((x) & 0x0000ff0000000000ULL) >> 24 | \
+ ((x) & 0x00000000ff000000ULL) << 8 | \
+ ((x) & 0x000000ff00000000ULL) >> 8)
+#endif
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+# define xlate16(x) (x)
+# define xlate32(x) (x)
+# define xlate64(x) (x)
+# define xlate16_be(x) bswap_16(x)
+# define xlate32_be(x) bswap_32(x)
+# define xlate64_be(x) bswap_64(x)
+#elif BYTE_ORDER == BIG_ENDIAN
+# define xlate16(x) bswap_16(x)
+# define xlate32(x) bswap_32(x)
+# define xlate64(x) bswap_64(x)
+# define xlate16_be(x) (x)
+# define xlate32_be(x) (x)
+# define xlate64_be(x) (x)
+#else
+# include <asm/byteorder.h>
+# define xlate16(x) __cpu_to_le16((x))
+# define xlate32(x) __cpu_to_le32((x))
+# define xlate64(x) __cpu_to_le64((x))
+# define xlate16_be(x) __cpu_to_be16((x))
+# define xlate32_be(x) __cpu_to_be32((x))
+# define xlate64_be(x) __cpu_to_be64((x))
+#endif
+
+#endif
--- /dev/null
+init_segtype
--- /dev/null
+#
+# Copyright (C) 2009-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES = replicator.c
+
+LIB_SHARED = liblvm2replicator.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+install: install_lib_shared_plugin
--- /dev/null
+/*
+ * Copyright (C) 2009-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "metadata.h"
+#include "segtype.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "activate.h"
+#include "str_list.h"
+#ifdef DMEVENTD
+# include "sharedlib.h"
+# include "libdevmapper-event.h"
+#endif
+
+/* Dm kernel module name for replicator */
+#define REPLICATOR_MODULE "replicator"
+#define REPLICATOR_DEV_MODULE "replicator-dev"
+
+/*
+ * Macro used as return argument - returns 0.
+ * return is left to be written in the function for better readability.
+ */
+#define SEG_LOG_ERROR(t, p...) \
+ log_error(t " segment %s of logical volume %s.", ## p, \
+ config_parent_name(sn), seg->lv->name), 0;
+
+
+/*
+ * Replicator target
+ */
+static const char *_replicator_name(const struct lv_segment *seg)
+{
+ return seg->segtype->name;
+}
+
+/* FIXME: missing implementation */
+static void _replicator_display(const struct lv_segment *seg)
+{
+ //const char *size;
+ //uint32_t s;
+
+ log_print(" Replicator");
+ if (seg->rlog_lv)
+ log_print(" Replicator volume\t%s", seg->rlog_lv->name);
+}
+
+/* Wrapper for get_config_uint32() with default value */
+static uint32_t _get_config_uint32(const struct config_node *cn,
+ const char *path,
+ uint32_t def)
+{
+ uint32_t t;
+
+ return get_config_uint32(cn, path, &t) ? t : def;
+}
+
+/* Wrapper for get_config_uint64() with default value */
+static uint64_t _get_config_uint64(const struct config_node *cn,
+ const char *path,
+ uint64_t def)
+{
+ uint64_t t;
+
+ return get_config_uint64(cn, path, &t) ? t : def;
+}
+
+
+/* Strings replicator_state_t enum */
+static const char _state_txt[NUM_REPLICATOR_STATE][8] = {
+ "passive",
+ "active"
+};
+
+/* Parse state string */
+static replicator_state_t _get_state(const struct config_node *sn,
+ const char *path, replicator_state_t def)
+{
+ const char *str;
+ unsigned i;
+
+ if (get_config_str(sn, path, &str)) {
+ for (i = 0; i < sizeof(_state_txt)/sizeof(_state_txt[0]); ++i)
+ if (strcasecmp(str, _state_txt[i]) == 0)
+ return (replicator_state_t) i;
+
+ log_warn("%s: unknown value '%s', using default '%s' state",
+ path, str, _state_txt[def]);
+ }
+
+ return def;
+}
+
+/* Strings for replicator_action_t enum */
+static const char _op_mode_txt[NUM_DM_REPLICATOR_MODES][8] = {
+ "sync",
+ "warn",
+ "stall",
+ "drop",
+ "fail"
+};
+
+
+/* Parse action string */
+static dm_replicator_mode_t _get_op_mode(const struct config_node *sn,
+ const char *path, dm_replicator_mode_t def)
+{
+ const char *str;
+ unsigned i;
+
+ if (get_config_str(sn, path, &str)) {
+ for (i = 0; i < sizeof(_op_mode_txt)/sizeof(_op_mode_txt[0]); ++i)
+ if (strcasecmp(str, _op_mode_txt[i]) == 0) {
+ log_very_verbose("Setting %s to %s",
+ path, _op_mode_txt[i]);
+ return (dm_replicator_mode_t) i;
+ }
+ log_warn("%s: unknown value '%s', using default '%s' operation mode",
+ path, str, _op_mode_txt[def]);
+ }
+
+ return def;
+}
+
+static struct replicator_site *_get_site(struct logical_volume *replicator,
+ const char *key)
+{
+ struct dm_pool *mem = replicator->vg->vgmem;
+ struct replicator_site *rsite;
+
+ dm_list_iterate_items(rsite, &replicator->rsites)
+ if (strcasecmp(rsite->name, key) == 0)
+ return rsite;
+
+ if (!(rsite = dm_pool_zalloc(mem, sizeof(*rsite))))
+ return_NULL;
+
+ if (!(rsite->name = dm_pool_strdup(mem, key)))
+ return_NULL;
+
+ rsite->replicator = replicator;
+ dm_list_init(&rsite->rdevices);
+ dm_list_add(&replicator->rsites, &rsite->list);
+
+ return rsite;
+}
+
+
+/* Parse replicator site element */
+static int _add_site(struct lv_segment *seg,
+ const char *key,
+ const struct config_node *sn)
+{
+ struct dm_pool *mem = seg->lv->vg->vgmem;
+ const struct config_node *cn;
+ struct replicator_site *rsite;
+
+ if (!(rsite = _get_site(seg->lv, key)))
+ return_0;
+
+ if (!find_config_node(sn, "site_index"))
+ return SEG_LOG_ERROR("Mandatory site_index is missing for");
+
+ rsite->state = _get_state(sn, "state", REPLICATOR_STATE_PASSIVE);
+ rsite->site_index = _get_config_uint32(sn, "site_index", 0);
+ if (rsite->site_index > seg->rsite_index_highest)
+ return SEG_LOG_ERROR("site_index=%d > highest_site_index=%d for",
+ rsite->site_index, seg->rsite_index_highest);
+
+ rsite->fall_behind_data = _get_config_uint64(sn, "fall_behind_data", 0);
+ rsite->fall_behind_ios = _get_config_uint32(sn, "fall_behind_ios", 0);
+ rsite->fall_behind_timeout = _get_config_uint32(sn, "fall_behind_timeout", 0);
+ rsite->op_mode = DM_REPLICATOR_SYNC;
+
+ if (rsite->fall_behind_data ||
+ rsite->fall_behind_ios ||
+ rsite->fall_behind_timeout) {
+ if (rsite->fall_behind_data && rsite->fall_behind_ios)
+ return SEG_LOG_ERROR("Defined both fall_behind_data "
+ "and fall_behind_ios in");
+
+ if (rsite->fall_behind_data && rsite->fall_behind_timeout)
+ return SEG_LOG_ERROR("Defined both fall_behind_data "
+ "and fall_behind_timeout in");
+
+ if (rsite->fall_behind_ios && rsite->fall_behind_timeout)
+ return SEG_LOG_ERROR("Defined both fall_behind_ios "
+ "and fall_behind_timeout in");
+
+ rsite->op_mode = _get_op_mode(sn, "operation_mode",
+ rsite->op_mode);
+ }
+
+ if ((cn = find_config_node(sn, "volume_group"))) {
+ if (!cn->v || cn->v->type != CFG_STRING)
+ return SEG_LOG_ERROR("volume_group must be a string in");
+
+ if (!(rsite->vg_name = dm_pool_strdup(mem, cn->v->v.str)))
+ return_0;
+
+ } else if (rsite->site_index != 0)
+ return SEG_LOG_ERROR("volume_group is mandatory for remote site in");
+
+ return 1;
+}
+
+
+/* Import replicator segment */
+static int _replicator_text_import(struct lv_segment *seg,
+ const struct config_node *sn,
+ struct dm_hash_table *pv_hash __attribute__((unused)))
+{
+ const struct config_node *cn;
+ struct logical_volume *rlog_lv;
+
+ if (!replicator_add_replicator_dev(seg->lv, NULL))
+ return_0;
+
+ if (!(cn = find_config_node(sn, "replicator_log")) ||
+ !cn->v || cn->v->type != CFG_STRING)
+ return SEG_LOG_ERROR("Replicator log type must be a string in");
+
+ if (!(rlog_lv = find_lv(seg->lv->vg, cn->v->v.str)))
+ return SEG_LOG_ERROR("Unknown replicator log %s in",
+ cn->v->v.str);
+
+ if (!(cn = find_config_node(sn, "replicator_log_type")) ||
+ !cn->v || cn->v->type != CFG_STRING)
+ return SEG_LOG_ERROR("Replicator log's type must be a string in");
+ if (strcasecmp(cn->v->v.str, "ringbuffer"))
+ return SEG_LOG_ERROR("Only ringbuffer replicator log type is supported in");
+
+ if (!(seg->rlog_type = dm_pool_strdup(seg->lv->vg->vgmem, cn->v->v.str)))
+ return_0;
+
+
+ log_very_verbose("replicator_log = %s", rlog_lv->name);
+ log_very_verbose("replicator_log_type = %s", seg->rlog_type);
+
+ if (!replicator_add_rlog(seg, rlog_lv))
+ return_0;
+
+ seg->rdevice_index_highest = _get_config_uint64(sn, "highest_device_index", 0);
+ seg->rsite_index_highest = _get_config_uint32(sn, "highest_site_index", 0);
+
+ seg->region_size = _get_config_uint32(sn, "sync_log_size", 0);
+
+ for (; sn; sn = sn->sib)
+ if (!sn->v) {
+ for (cn = sn->sib; cn; cn = cn->sib)
+ if (!cn->v && (strcasecmp(cn->key ,sn->key) == 0))
+ return SEG_LOG_ERROR("Detected duplicate site "
+ "name %s in", sn->key);
+ if (!_add_site(seg, sn->key, sn->child))
+ return_0;
+ }
+ return 1;
+}
+
+/* Export replicator segment */
+static int _replicator_text_export(const struct lv_segment *seg,
+ struct formatter *f)
+{
+ struct replicator_site *rsite;
+
+ if (!seg->rlog_lv)
+ return_0;
+
+ outf(f, "replicator_log = \"%s\"", seg->rlog_lv->name);
+ outf(f, "replicator_log_type = \"%s\"", seg->rlog_type);
+ outf(f, "highest_device_index = %" PRIu64, seg->rdevice_index_highest);
+ outf(f, "highest_site_index = %d", seg->rsite_index_highest);
+
+ if (seg->region_size)
+ outsize(f, (uint64_t)seg->region_size,
+ "sync_log_size = %" PRIu32, seg->region_size);
+
+ if (!dm_list_empty(&seg->lv->rsites))
+ outnl(f);
+
+ dm_list_iterate_items(rsite, &seg->lv->rsites) {
+ outf(f, "%s {", rsite->name);
+ out_inc_indent(f);
+
+ outf(f, "state = \"%s\"", _state_txt[rsite->state]);
+ outf(f, "site_index = %d", rsite->site_index);
+
+ /* Only non-default parameters are written */
+ if (rsite->op_mode != DM_REPLICATOR_SYNC)
+ outf(f, "operation_mode = \"%s\"",
+ _op_mode_txt[rsite->op_mode]);
+ if (rsite->fall_behind_timeout)
+ outfc(f, "# seconds", "fall_behind_timeout = %u",
+ rsite->fall_behind_timeout);
+ if (rsite->fall_behind_ios)
+ outfc(f, "# io operations", "fall_behind_ios = %u",
+ rsite->fall_behind_ios);
+ if (rsite->fall_behind_data)
+ outsize(f, rsite->fall_behind_data, "fall_behind_data = %" PRIu64,
+ rsite->fall_behind_data);
+ if (rsite->state != REPLICATOR_STATE_ACTIVE && rsite->vg_name)
+ outf(f, "volume_group = \"%s\"", rsite->vg_name);
+
+ out_dec_indent(f);
+ outf(f, "}");
+ }
+
+ return 1;
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static int _replicator_add_target_line(struct dev_manager *dm,
+ struct dm_pool *mem,
+ struct cmd_context *cmd,
+ void **target_state,
+ struct lv_segment *seg,
+ struct dm_tree_node *node,
+ uint64_t len,
+ uint32_t *pvmove_mirror_count)
+{
+ const char *rlog_dlid;
+ struct replicator_site *rsite;
+
+ if (!seg->rlog_lv)
+ return_0;
+
+ if (!(rlog_dlid = build_dm_uuid(mem, seg->rlog_lv->lvid.s, NULL)))
+ return_0;
+
+ dm_list_iterate_items(rsite, &seg->lv->rsites) {
+ if (!dm_tree_node_add_replicator_target(node,
+ seg->rlog_lv->size,
+ rlog_dlid,
+ seg->rlog_type,
+ rsite->site_index,
+ rsite->op_mode,
+ rsite->fall_behind_timeout,
+ rsite->fall_behind_data,
+ rsite->fall_behind_ios)) {
+ if (rsite->site_index == 0) {
+ log_error("Failed to add replicator log '%s' "
+ "to replicator '%s'.",
+ rlog_dlid, seg->lv->name);
+ return 0;
+ }
+ // FIXME:
+ }
+ }
+
+ return 1;
+}
+
+/* FIXME: write something useful for replicator here */
+static int _replicator_target_percent(void **target_state,
+ percent_t *percent,
+ struct dm_pool *mem,
+ struct cmd_context *cmd,
+ struct lv_segment *seg,
+ char *params, uint64_t *total_numerator,
+ uint64_t *total_denominator)
+{
+ return 1;
+}
+
+/* Check for module presence */
+static int _replicator_target_present(struct cmd_context *cmd,
+ const struct lv_segment *seg __attribute__((unused)),
+ unsigned *attributes __attribute__((unused)))
+{
+ static int _checked = 0;
+ static int _present = 0;
+
+ if (!_checked) {
+ _present = target_present(cmd, REPLICATOR_MODULE, 1);
+ _checked = 1;
+ }
+
+ return _present;
+}
+
+#endif
+
+static int _replicator_modules_needed(struct dm_pool *mem,
+ const struct lv_segment *seg __attribute__((unused)),
+ struct dm_list *modules)
+{
+ if (!str_list_add(mem, modules, REPLICATOR_MODULE))
+ return_0;
+
+ if (!str_list_add(mem, modules, REPLICATOR_DEV_MODULE))
+ return_0;
+
+ return 1;
+}
+
+static void _replicator_destroy(struct segment_type *segtype)
+{
+ dm_free(segtype);
+}
+
+static struct segtype_handler _replicator_ops = {
+ .name = _replicator_name,
+ .display = _replicator_display,
+ .text_import = _replicator_text_import,
+ .text_export = _replicator_text_export,
+#ifdef DEVMAPPER_SUPPORT
+ .add_target_line = _replicator_add_target_line,
+ .target_percent = _replicator_target_percent,
+ .target_present = _replicator_target_present,
+#endif
+ .modules_needed = _replicator_modules_needed,
+ .destroy = _replicator_destroy,
+};
+
+/*
+ * Replicator-dev target
+ */
+static void _replicator_dev_display(const struct lv_segment *seg)
+{
+ //const char *size;
+ //uint32_t s;
+ // FIXME: debug test code for now
+ log_print(" Replicator\t\t%u", seg->area_count);
+ log_print(" Mirror size\t\t%u", seg->area_len);
+ if (seg->log_lv)
+ log_print(" Replicator log volume\t%s", seg->rlog_lv->name);
+
+}
+
+static int _add_device(struct lv_segment *seg,
+ const char *site_name,
+ const struct config_node *sn,
+ uint64_t devidx)
+{
+ struct dm_pool *mem = seg->lv->vg->vgmem;
+ struct logical_volume *lv = NULL;
+ struct logical_volume *slog_lv = NULL;
+ struct replicator_site *rsite = _get_site(seg->replicator, site_name);
+ struct replicator_device *rdev;
+ const char *dev_str = NULL;
+ const char *slog_str = NULL;
+ const struct config_node *cn;
+
+ dm_list_iterate_items(rdev, &rsite->rdevices)
+ if (rdev->replicator_dev == seg)
+ return SEG_LOG_ERROR("Duplicate site found in");
+
+ if ((cn = find_config_node(sn, "sync_log"))) {
+ if (!cn->v || !cn->v->v.str)
+ return SEG_LOG_ERROR("Sync log must be a string in");
+ slog_str = cn->v->v.str;
+ }
+
+ if (!(cn = find_config_node(sn, "logical_volume")) ||
+ !cn->v || !cn->v->v.str)
+ return SEG_LOG_ERROR("Logical volume must be a string in");
+
+ dev_str = cn->v->v.str;
+
+ if (!seg->lv->rdevice) {
+ if (slog_str)
+ return SEG_LOG_ERROR("Sync log %s defined for local "
+ "device in", slog_str);
+
+ /* Check for device in current VG */
+ if (!(lv = find_lv(seg->lv->vg, dev_str)))
+ return SEG_LOG_ERROR("Logical volume %s not found in",
+ dev_str);
+ } else {
+ if (!slog_str)
+ return SEG_LOG_ERROR("Sync log is missing for remote "
+ "device in");
+ /* Check for slog device in current VG */
+ if (!(slog_lv = find_lv(seg->lv->vg, slog_str)))
+ return SEG_LOG_ERROR("Sync log %s not found in",
+ slog_str);
+ }
+
+ if (!(rdev = dm_pool_zalloc(mem, sizeof(*rdev))))
+ return_0;
+
+ if (!(rdev->name = dm_pool_strdup(mem, dev_str)))
+ return_0;
+
+ rdev->replicator_dev = seg;
+ rdev->rsite = rsite;
+ rdev->device_index = devidx;
+
+ if (!seg->lv->rdevice) {
+ if (!replicator_dev_add_rimage(rdev, lv))
+ return SEG_LOG_ERROR("LV inconsistency found in");
+ seg->lv->rdevice = rdev;
+ } else {
+ if (!slog_str ||
+ !(rdev->slog_name = dm_pool_strdup(mem, slog_str)))
+ return_0;
+
+ if (!replicator_dev_add_slog(rdev, slog_lv))
+ return SEG_LOG_ERROR("Sync log inconsistency found in");
+ }
+
+ dm_list_add(&rsite->rdevices, &rdev->list);// linked site list
+
+ return 1;
+}
+
+/* Import replicator segment */
+static int _replicator_dev_text_import(struct lv_segment *seg,
+ const struct config_node *sn,
+ struct dm_hash_table *pv_hash __attribute__((unused)))
+{
+ const struct config_node *cn;
+ struct logical_volume *replicator;
+ uint64_t devidx;
+
+ if (!(cn = find_config_node(sn, "replicator")))
+ return SEG_LOG_ERROR("Replicator is missing for");
+
+ if (!cn->v || !cn->v->v.str)
+ return SEG_LOG_ERROR("Replicator must be a string for");
+
+ if (!(replicator = find_lv(seg->lv->vg, cn->v->v.str)))
+ return SEG_LOG_ERROR("Unknown replicator %s for", cn->v->v.str);
+
+ if (!replicator_add_replicator_dev(replicator, seg))
+ return_0;
+
+ log_very_verbose("replicator=%s", replicator->name);
+
+ /* Mandatory */
+ if (!find_config_node(sn, "device_index") ||
+ !get_config_uint64(sn, "device_index", &devidx))
+ return SEG_LOG_ERROR("Could not read 'device_index' for");
+
+ /* Read devices from sites */
+ for (; sn; sn = sn->sib)
+ if (!(sn->v) && !_add_device(seg, sn->key, sn->child, devidx))
+ return_0;
+
+ if (!seg->lv->rdevice)
+ return SEG_LOG_ERROR("Replicator device without site in");
+
+ seg->rlog_lv = NULL;
+ seg->lv->status |= REPLICATOR;
+
+ return 1;
+}
+
+/* Export replicator-dev segment */
+static int _replicator_dev_text_export(const struct lv_segment *seg,
+ struct formatter *f)
+{
+ struct replicator_site *rsite;
+ struct replicator_device *rdev;
+
+ if (!seg->replicator || !seg->lv->rdevice)
+ return_0;
+
+ outf(f, "replicator = \"%s\"", seg->replicator->name);
+ outf(f, "device_index = %" PRId64, seg->lv->rdevice->device_index);
+
+ outnl(f);
+
+ dm_list_iterate_items(rsite, &seg->replicator->rsites) {
+ dm_list_iterate_items(rdev, &rsite->rdevices) {
+ if (rdev->replicator_dev != seg)
+ continue;
+
+ outf(f, "%s {", rdev->rsite->name);
+
+ out_inc_indent(f);
+
+ outf(f, "logical_volume = \"%s\"",
+ rdev->name ? rdev->name : rdev->lv->name);
+
+ if (rdev->slog)
+ outf(f, "sync_log = \"%s\"", rdev->slog->name);
+ else if (rdev->slog_name)
+ outf(f, "sync_log = \"%s\"", rdev->slog_name);
+
+ out_dec_indent(f);
+
+ outf(f, "}");
+ }
+ }
+
+ return 1;
+}
+
+#ifdef DEVMAPPER_SUPPORT
+/*
+ * Add target for passive site matching the device index
+ */
+static int _replicator_dev_add_target_line(struct dev_manager *dm,
+ struct dm_pool *mem,
+ struct cmd_context *cmd,
+ void **target_state,
+ struct lv_segment *seg,
+ struct dm_tree_node *node,
+ uint64_t len,
+ uint32_t *pvmove_mirror_count)
+{
+ const char *replicator_dlid, *rdev_dlid, *slog_dlid;
+ struct replicator_device *rdev, *rdev_search;
+ struct replicator_site *rsite;
+ uint32_t slog_size;
+ uint32_t slog_flags;
+
+ if (!lv_is_active_replicator_dev(seg->lv)) {
+ /* Create passive linear mapping */
+ log_very_verbose("Inactive replicator %s using %s.",
+ seg->lv->name, seg->lv->rdevice->lv->name);
+ if (!dm_tree_node_add_linear_target(node, seg->lv->size))
+ return_0;
+ if (!(rdev_dlid = build_dm_uuid(mem, seg->lv->rdevice->lv->lvid.s, NULL)))
+ return_0;
+ return dm_tree_node_add_target_area(node, NULL, rdev_dlid, 0);
+ } else if (seg->lv->rdevice->rsite->site_index) {
+ log_error("Active site with site_index != 0 (%s, %d)",
+ seg->lv->rdevice->rsite->name,
+ seg->lv->rdevice->rsite->site_index);
+ return 0; /* Replicator without any active site */
+ }
+
+ /*
+ * At this point all devices that have some connection with replicator
+ * must be present in dm_tree
+ */
+ if (!seg_is_replicator_dev(seg) ||
+ !(replicator_dlid = build_dm_uuid(mem, seg->replicator->lvid.s, NULL)))
+ return_0;
+
+ /* Select remote devices with the same device index */
+ dm_list_iterate_items(rsite, &seg->replicator->rsites) {
+ if (rsite->site_index == 0) {
+ /* Local slink0 device */
+ rdev = seg->lv->rdevice;
+ } else {
+ rdev = NULL;
+ dm_list_iterate_items(rdev_search, &rsite->rdevices) {
+ if (rdev_search->replicator_dev == seg) {
+ rdev = rdev_search;
+ break;
+ }
+ }
+
+ if (!rdev) {
+ log_error(INTERNAL_ERROR "rdev list not found.");
+ return 0;
+ }
+ }
+
+ if (!rdev->lv ||
+ !(rdev_dlid = build_dm_uuid(mem, rdev->lv->lvid.s, NULL)))
+ return_0;
+
+ slog_dlid = NULL;
+
+ /* Using either disk or core (in memory) log */
+ if (rdev->slog) {
+ slog_flags = DM_NOSYNC;
+ slog_size = (uint32_t) rdev->slog->size;
+ if (!(slog_dlid = build_dm_uuid(mem, rdev->slog->lvid.s, NULL)))
+ return_0;
+ } else if (rdev->slog_name &&
+ sscanf(rdev->slog_name, "%" PRIu32, &slog_size) == 1) {
+ slog_flags = DM_CORELOG | DM_FORCESYNC;
+ if (slog_size == 0) {
+ log_error("Failed to use empty corelog size "
+ "in replicator '%s'.",
+ rsite->replicator->name);
+ return 0;
+ }
+ } else {
+ slog_flags = DM_CORELOG | DM_FORCESYNC;
+ slog_size = 0; /* NOLOG */
+ }
+
+ if (!dm_tree_node_add_replicator_dev_target(node,
+ seg->lv->size,
+ replicator_dlid,
+ seg->lv->rdevice->device_index,
+ rdev_dlid,
+ rsite->site_index,
+ slog_dlid,
+ slog_flags,
+ slog_size)) {
+ return_0;
+ /* FIXME: handle 'state = dropped' in future */
+ }
+ }
+
+ return 1;
+}
+
+/* FIXME: write something useful for replicator-dev here */
+static int _replicator_dev_target_percent(void **target_state,
+ percent_t *percent,
+ struct dm_pool *mem,
+ struct cmd_context *cmd,
+ struct lv_segment *seg,
+ char *params,
+ uint64_t *total_numerator,
+ uint64_t *total_denominator)
+{
+ return 1;
+}
+
+/* Check for module presence */
+static int _replicator_dev_target_present(struct cmd_context *cmd,
+ const struct lv_segment *seg __attribute__((unused)),
+ unsigned *attributes __attribute__((unused)))
+{
+ static int _checked = 0;
+ static int _present = 0;
+
+ if (!_checked) {
+ _present = target_present(cmd, REPLICATOR_DEV_MODULE, 1);
+ _checked = 1;
+ }
+
+ return _present;
+}
+
+#endif
+
+static struct segtype_handler _replicator_dev_ops = {
+ .name = _replicator_name,
+ .display = _replicator_dev_display,
+ .text_import = _replicator_dev_text_import,
+ .text_export = _replicator_dev_text_export,
+#ifdef DEVMAPPER_SUPPORT
+ .add_target_line = _replicator_dev_add_target_line,
+ .target_percent = _replicator_dev_target_percent,
+ .target_present = _replicator_dev_target_present,
+#endif
+ .modules_needed = _replicator_modules_needed,
+ .destroy = _replicator_destroy,
+};
+
+#ifdef REPLICATOR_INTERNAL
+int init_replicator_segtype(struct segtype_library *seglib)
+#else /* Shared */
+int init_multiple_segtype(struct segtype_library *seglib);
+int init_multiple_segtype(struct segtype_library *seglib)
+#endif
+{
+ struct segment_type *segtype;
+
+ if (!(segtype = dm_malloc(sizeof(*segtype))))
+ return_0;
+
+ segtype->ops = &_replicator_ops;
+ segtype->name = REPLICATOR_MODULE;
+ segtype->private = NULL;
+ segtype->flags = SEG_REPLICATOR;
+
+ if (!lvm_register_segtype(seglib, segtype))
+ return_0;
+
+ log_very_verbose("Initialised segtype: " REPLICATOR_MODULE);
+
+ if (!(segtype = dm_malloc(sizeof(*segtype))))
+ return_0;
+
+ segtype->ops = &_replicator_dev_ops;
+ segtype->name = REPLICATOR_DEV_MODULE;
+ segtype->private = NULL;
+ segtype->flags = SEG_REPLICATOR_DEV;
+
+ if (!lvm_register_segtype(seglib, segtype))
+ return_0;
+
+ log_very_verbose("Initialised segtype: " REPLICATOR_DEV_MODULE);
+
+ return 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This file defines the fields (columns) for the reporting commands
+ * (pvs/vgs/lvs).
+ */
+/*
+ * The 'FIELD' macro arguments are defined as follows:
+ * 1. report_type. An enum value that selects a specific
+ * struct dm_report_object_type in the _report_types array. The value is
+ * used to select the containing base object address (see *obj_get*
+ * functions) for any data values of any field in the report.
+ * 2. Containing struct. The structure that either contains the field data
+ * as a member or should be used to obtain the field data. The containing
+ * struct should match the base object of the report_type.
+ * 3. Field type. This must be either 'STR' or 'NUM'.
+ * 4. Report heading. This is the field heading that is displayed by the
+ * reporting commands.
+ * 5. Data value pointer. This argument is always a member of the
+ * containing struct. It may point directly to the data value (for example,
+ * lv_uuid - see _uuid_disp()) or may be used to derive the data value (for
+ * example, seg_count - see _lvsegcount_disp()). In the FIELD macro
+ * definition, it is used in an offset calculation to derive the offset to
+ * the data value from the containing struct base address. Note that in some
+ * cases, the argument is the first member of the struct, in which case the
+ * data value pointer points to the start of the struct itself (for example,
+ * 'lvid' field of struct 'lv').
+ * 6. Minimum display width. This is the minimum width used to display
+ * the field value, typically matching the width of the column heading.
+ * 7. Display function identifier. Used to derive the full name of the
+ * function that displays this field. Derivation is done by appending '_'
+ * then prepending this argument to '_disp'. For example, if this argument
+ * is 'uuid', the display function is _uuid_disp(). Adding a new field may
+ * require defining a new display function (for example _myfieldname_disp()),
+ * or re-use of an existing one (for example, _uint32_disp()).
+ * 8. Unique format identifier / field id. This name must be unique and is
+ * used to select fields via '-o' in the reporting commands (pvs/vgs/lvs).
+ * The string used to specify the field - the 'id' member of
+ * struct dm_report_field_type.
+ * 9. Description of field. This is a brief (ideally <= 52 chars) description
+ * of the field used in the reporting commands.
+ * 10. Flags.
+ * FIELD_MODIFIABLE. A '_set' function exists to change the field's value.
+ * The function name is derived in a similar way to item 7 above.
+ */
+
+#define FIELD_MODIFIABLE 0x00000001
+
+/* *INDENT-OFF* */
+FIELD(LVS, lv, STR, "LV UUID", lvid.id[1], 38, uuid, lv_uuid, "Unique identifier.", 0)
+FIELD(LVS, lv, STR, "LV", lvid, 4, lvname, lv_name, "Name. LVs created for internal use are enclosed in brackets.", 0)
+FIELD(LVS, lv, STR, "Path", lvid, 4, lvpath, lv_path, "Full pathname for LV.", 0)
+FIELD(LVS, lv, STR, "Attr", lvid, 4, lvstatus, lv_attr, "Various attributes - see man page.", 0)
+FIELD(LVS, lv, NUM, "Maj", major, 3, int32, lv_major, "Persistent major number or -1 if not persistent.", 0)
+FIELD(LVS, lv, NUM, "Min", minor, 3, int32, lv_minor, "Persistent minor number or -1 if not persistent.", 0)
+FIELD(LVS, lv, NUM, "Rahead", lvid, 6, lvreadahead, lv_read_ahead, "Read ahead setting in current units.", 0)
+FIELD(LVS, lv, STR, "KMaj", lvid, 4, lvkmaj, lv_kernel_major, "Currently assigned major number or -1 if LV is not active.", 0)
+FIELD(LVS, lv, STR, "KMin", lvid, 4, lvkmin, lv_kernel_minor, "Currently assigned minor number or -1 if LV is not active.", 0)
+FIELD(LVS, lv, NUM, "KRahead", lvid, 7, lvkreadahead, lv_kernel_read_ahead, "Currently-in-use read ahead setting in current units.", 0)
+FIELD(LVS, lv, NUM, "LSize", size, 5, size64, lv_size, "Size of LV in current units.", 0)
+FIELD(LVS, lv, NUM, "#Seg", lvid, 4, lvsegcount, seg_count, "Number of segments in LV.", 0)
+FIELD(LVS, lv, STR, "Origin", lvid, 6, origin, origin, "For snapshots, the origin device of this LV.", 0)
+FIELD(LVS, lv, NUM, "OSize", lvid, 5, originsize, origin_size, "For snapshots, the size of the origin device of this LV.", 0)
+FIELD(LVS, lv, NUM, "Snap%", lvid, 6, snpercent, snap_percent, "For snapshots, the percentage full if LV is active.", 0)
+FIELD(LVS, lv, NUM, "Copy%", lvid, 6, copypercent, copy_percent, "For mirrors and pvmove, current percentage in-sync.", 0)
+FIELD(LVS, lv, STR, "Move", lvid, 4, movepv, move_pv, "For pvmove, Source PV of temporary LV created by pvmove.", 0)
+FIELD(LVS, lv, STR, "Convert", lvid, 7, convertlv, convert_lv, "For lvconvert, Name of temporary LV created by lvconvert.", 0)
+FIELD(LVS, lv, STR, "LV Tags", tags, 7, tags, lv_tags, "Tags, if any.", 0)
+FIELD(LVS, lv, STR, "Log", lvid, 3, loglv, mirror_log, "For mirrors, the LV holding the synchronisation log.", 0)
+FIELD(LVS, lv, STR, "Modules", lvid, 7, modules, modules, "Kernel device-mapper modules required for this LV.", 0)
+
+FIELD(LABEL, pv, STR, "Fmt", id, 3, pvfmt, pv_fmt, "Type of metadata.", 0)
+FIELD(LABEL, pv, STR, "PV UUID", id, 38, uuid, pv_uuid, "Unique identifier.", 0)
+FIELD(LABEL, pv, NUM, "DevSize", id, 7, devsize, dev_size, "Size of underlying device in current units.", 0)
+FIELD(LABEL, pv, STR, "PV", dev, 10, dev_name, pv_name, "Name.", 0)
+FIELD(LABEL, pv, NUM, "PMdaFree", id, 9, pvmdafree, pv_mda_free, "Free metadata area space on this device in current units.", 0)
+FIELD(LABEL, pv, NUM, "PMdaSize", id, 9, pvmdasize, pv_mda_size, "Size of smallest metadata area on this device in current units.", 0)
+
+FIELD(PVS, pv, NUM, "1st PE", pe_start, 7, size64, pe_start, "Offset to the start of data on the underlying device.", 0)
+FIELD(PVS, pv, NUM, "PSize", id, 5, pvsize, pv_size, "Size of PV in current units.", 0)
+FIELD(PVS, pv, NUM, "PFree", id, 5, pvfree, pv_free, "Total amount of unallocated space in current units.", 0)
+FIELD(PVS, pv, NUM, "Used", id, 4, pvused, pv_used, "Total amount of allocated space in current units.", 0)
+FIELD(PVS, pv, STR, "Attr", id, 4, pvstatus, pv_attr, "Various attributes - see man page.", 0)
+FIELD(PVS, pv, NUM, "PE", pe_count, 3, uint32, pv_pe_count, "Total number of Physical Extents.", 0)
+FIELD(PVS, pv, NUM, "Alloc", pe_alloc_count, 5, uint32, pv_pe_alloc_count, "Total number of allocated Physical Extents.", 0)
+FIELD(PVS, pv, STR, "PV Tags", tags, 7, tags, pv_tags, "Tags, if any.", 0)
+FIELD(PVS, pv, NUM, "#PMda", id, 5, pvmdas, pv_mda_count, "Number of metadata areas on this device.", 0)
+FIELD(PVS, pv, NUM, "#PMdaUse", id, 8, pvmdasused, pv_mda_used_count, "Number of metadata areas in use on this device.", 0)
+
+FIELD(VGS, vg, STR, "Fmt", cmd, 3, vgfmt, vg_fmt, "Type of metadata.", 0)
+FIELD(VGS, vg, STR, "VG UUID", id, 38, uuid, vg_uuid, "Unique identifier.", 0)
+FIELD(VGS, vg, STR, "VG", name, 4, string, vg_name, "Name.", 0)
+FIELD(VGS, vg, STR, "Attr", cmd, 5, vgstatus, vg_attr, "Various attributes - see man page.", 0)
+FIELD(VGS, vg, NUM, "VSize", cmd, 5, vgsize, vg_size, "Total size of VG in current units.", 0)
+FIELD(VGS, vg, NUM, "VFree", cmd, 5, vgfree, vg_free, "Total amount of free space in current units.", 0)
+FIELD(VGS, vg, STR, "SYS ID", system_id, 6, string, vg_sysid, "System ID indicating when and where it was created.", 0)
+FIELD(VGS, vg, NUM, "Ext", extent_size, 3, size32, vg_extent_size, "Size of Physical Extents in current units.", 0)
+FIELD(VGS, vg, NUM, "#Ext", extent_count, 4, uint32, vg_extent_count, "Total number of Physical Extents.", 0)
+FIELD(VGS, vg, NUM, "Free", free_count, 4, uint32, vg_free_count, "Total number of unallocated Physical Extents.", 0)
+FIELD(VGS, vg, NUM, "MaxLV", max_lv, 5, uint32, max_lv, "Maximum number of LVs allowed in VG or 0 if unlimited.", 0)
+FIELD(VGS, vg, NUM, "MaxPV", max_pv, 5, uint32, max_pv, "Maximum number of PVs allowed in VG or 0 if unlimited.", 0)
+FIELD(VGS, vg, NUM, "#PV", pv_count, 3, uint32, pv_count, "Number of PVs.", 0)
+FIELD(VGS, vg, NUM, "#LV", cmd, 3, lvcount, lv_count, "Number of LVs.", 0)
+FIELD(VGS, vg, NUM, "#SN", cmd, 3, snapcount, snap_count, "Number of snapshots.", 0)
+FIELD(VGS, vg, NUM, "Seq", seqno, 3, uint32, vg_seqno, "Revision number of internal metadata. Incremented whenever it changes.", 0)
+FIELD(VGS, vg, STR, "VG Tags", tags, 7, tags, vg_tags, "Tags, if any.", 0)
+FIELD(VGS, vg, NUM, "#VMda", cmd, 5, vgmdas, vg_mda_count, "Number of metadata areas on this VG.", 0)
+FIELD(VGS, vg, NUM, "#VMdaUse", cmd, 8, vgmdasused, vg_mda_used_count, "Number of metadata areas in use on this VG.", 0)
+FIELD(VGS, vg, NUM, "VMdaFree", cmd, 9, vgmdafree, vg_mda_free, "Free metadata area space for this VG in current units.", 0)
+FIELD(VGS, vg, NUM, "VMdaSize", cmd, 9, vgmdasize, vg_mda_size, "Size of smallest metadata area for this VG in current units.", 0)
+FIELD(VGS, vg, NUM, "#VMdaCps", cmd, 8, vgmdacopies, vg_mda_copies, "Target number of in use metadata areas in the VG.", 1)
+
+FIELD(SEGS, seg, STR, "Type", list, 4, segtype, segtype, "Type of LV segment.", 0)
+FIELD(SEGS, seg, NUM, "#Str", area_count, 4, uint32, stripes, "Number of stripes or mirror legs.", 0)
+FIELD(SEGS, seg, NUM, "Stripe", stripe_size, 6, size32, stripesize, "For stripes, amount of data placed on one device before switching to the next.", 0)
+FIELD(SEGS, seg, NUM, "Stripe", stripe_size, 6, size32, stripe_size, "For stripes, amount of data placed on one device before switching to the next.", 0)
+FIELD(SEGS, seg, NUM, "Region", region_size, 6, size32, regionsize, "For mirrors, the unit of data copied when synchronising devices.", 0)
+FIELD(SEGS, seg, NUM, "Region", region_size, 6, size32, region_size, "For mirrors, the unit of data copied when synchronising devices.", 0)
+FIELD(SEGS, seg, NUM, "Chunk", list, 5, chunksize, chunksize, "For snapshots, the unit of data used when tracking changes.", 0)
+FIELD(SEGS, seg, NUM, "Chunk", list, 5, chunksize, chunk_size, "For snapshots, the unit of data used when tracking changes.", 0)
+FIELD(SEGS, seg, NUM, "Start", list, 5, segstart, seg_start, "Offset within the LV to the start of the segment in current units.", 0)
+FIELD(SEGS, seg, NUM, "Start", list, 5, segstartpe, seg_start_pe, "Offset within the LV to the start of the segment in physical extents.", 0)
+FIELD(SEGS, seg, NUM, "SSize", list, 5, segsize, seg_size, "Size of segment in current units.", 0)
+FIELD(SEGS, seg, STR, "Seg Tags", tags, 8, tags, seg_tags, "Tags, if any.", 0)
+FIELD(SEGS, seg, STR, "PE Ranges", list, 9, peranges, seg_pe_ranges, "Ranges of Physical Extents of underlying devices in command line format.", 0)
+FIELD(SEGS, seg, STR, "Devices", list, 7, devices, devices, "Underlying devices used with starting extent numbers.", 0)
+
+FIELD(PVSEGS, pvseg, NUM, "Start", pe, 5, uint32, pvseg_start, "Physical Extent number of start of segment.", 0)
+FIELD(PVSEGS, pvseg, NUM, "SSize", len, 5, uint32, pvseg_size, "Number of extents in segment.", 0)
+/* *INDENT-ON* */
--- /dev/null
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+
+#include "libdevmapper.h"
+#include "properties.h"
+#include "activate.h"
+#include "lvm-logging.h"
+#include "lvm-types.h"
+#include "metadata.h"
+
+#define GET_NUM_PROPERTY_FN(NAME, VALUE, TYPE, VAR) \
+static int _ ## NAME ## _get (const void *obj, struct lvm_property_type *prop) \
+{ \
+ const struct TYPE *VAR = (const struct TYPE *)obj; \
+\
+ prop->value.integer = VALUE; \
+ return 1; \
+}
+#define GET_VG_NUM_PROPERTY_FN(NAME, VALUE) \
+ GET_NUM_PROPERTY_FN(NAME, VALUE, volume_group, vg)
+#define GET_PV_NUM_PROPERTY_FN(NAME, VALUE) \
+ GET_NUM_PROPERTY_FN(NAME, VALUE, physical_volume, pv)
+#define GET_LV_NUM_PROPERTY_FN(NAME, VALUE) \
+ GET_NUM_PROPERTY_FN(NAME, VALUE, logical_volume, lv)
+#define GET_LVSEG_NUM_PROPERTY_FN(NAME, VALUE) \
+ GET_NUM_PROPERTY_FN(NAME, VALUE, lv_segment, lvseg)
+#define GET_PVSEG_NUM_PROPERTY_FN(NAME, VALUE) \
+ GET_NUM_PROPERTY_FN(NAME, VALUE, pv_segment, pvseg)
+
+#define SET_NUM_PROPERTY_FN(NAME, SETFN, TYPE, VAR) \
+static int _ ## NAME ## _set (void *obj, struct lvm_property_type *prop) \
+{ \
+ struct TYPE *VAR = (struct TYPE *)obj; \
+\
+ SETFN(VAR, prop->value.integer); \
+ return 1; \
+}
+#define SET_VG_NUM_PROPERTY_FN(NAME, SETFN) \
+ SET_NUM_PROPERTY_FN(NAME, SETFN, volume_group, vg)
+#define SET_PV_NUM_PROPERTY_FN(NAME, SETFN) \
+ SET_NUM_PROPERTY_FN(NAME, SETFN, physical_volume, pv)
+#define SET_LV_NUM_PROPERTY_FN(NAME, SETFN) \
+ SET_NUM_PROPERTY_FN(NAME, SETFN, logical_volume, lv)
+
+#define GET_STR_PROPERTY_FN(NAME, VALUE, TYPE, VAR) \
+static int _ ## NAME ## _get (const void *obj, struct lvm_property_type *prop) \
+{ \
+ const struct TYPE *VAR = (const struct TYPE *)obj; \
+\
+ prop->value.string = (char *)VALUE; \
+ return 1; \
+}
+#define GET_VG_STR_PROPERTY_FN(NAME, VALUE) \
+ GET_STR_PROPERTY_FN(NAME, VALUE, volume_group, vg)
+#define GET_PV_STR_PROPERTY_FN(NAME, VALUE) \
+ GET_STR_PROPERTY_FN(NAME, VALUE, physical_volume, pv)
+#define GET_LV_STR_PROPERTY_FN(NAME, VALUE) \
+ GET_STR_PROPERTY_FN(NAME, VALUE, logical_volume, lv)
+#define GET_LVSEG_STR_PROPERTY_FN(NAME, VALUE) \
+ GET_STR_PROPERTY_FN(NAME, VALUE, lv_segment, lvseg)
+#define GET_PVSEG_STR_PROPERTY_FN(NAME, VALUE) \
+ GET_STR_PROPERTY_FN(NAME, VALUE, pv_segment, pvseg)
+
+static int _not_implemented_get(const void *obj, struct lvm_property_type *prop)
+{
+ log_errno(ENOSYS, "Function not implemented");
+ return 0;
+}
+
+static int _not_implemented_set(void *obj, struct lvm_property_type *prop)
+{
+ log_errno(ENOSYS, "Function not implemented");
+ return 0;
+}
+
+static percent_t _copy_percent(const struct logical_volume *lv) {
+ percent_t perc;
+ lv_mirror_percent(lv->vg->cmd, (struct logical_volume *) lv, 0, &perc, NULL);
+ return perc;
+}
+
+static percent_t _snap_percent(const struct logical_volume *lv) {
+ percent_t perc;
+ lv_snapshot_percent(lv, &perc);
+ return perc;
+}
+
+/* PV */
+GET_PV_STR_PROPERTY_FN(pv_fmt, pv_fmt_dup(pv))
+#define _pv_fmt_set _not_implemented_set
+GET_PV_STR_PROPERTY_FN(pv_uuid, pv_uuid_dup(pv))
+#define _pv_uuid_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(dev_size, SECTOR_SIZE * pv_dev_size(pv))
+#define _dev_size_set _not_implemented_set
+GET_PV_STR_PROPERTY_FN(pv_name, pv_name_dup(pv))
+#define _pv_name_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_mda_free, SECTOR_SIZE * pv_mda_free(pv))
+#define _pv_mda_free_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_mda_size, SECTOR_SIZE * pv_mda_size(pv))
+#define _pv_mda_size_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pe_start, SECTOR_SIZE * pv->pe_start)
+#define _pe_start_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_size, SECTOR_SIZE * pv_size_field(pv))
+#define _pv_size_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_free, SECTOR_SIZE * pv_free(pv))
+#define _pv_free_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_used, SECTOR_SIZE * pv_used(pv))
+#define _pv_used_set _not_implemented_set
+GET_PV_STR_PROPERTY_FN(pv_attr, pv_attr_dup(pv->vg->vgmem, pv))
+#define _pv_attr_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_pe_count, pv->pe_count)
+#define _pv_pe_count_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_pe_alloc_count, pv->pe_alloc_count)
+#define _pv_pe_alloc_count_set _not_implemented_set
+GET_PV_STR_PROPERTY_FN(pv_tags, pv_tags_dup(pv))
+#define _pv_tags_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_mda_count, pv_mda_count(pv))
+#define _pv_mda_count_set _not_implemented_set
+GET_PV_NUM_PROPERTY_FN(pv_mda_used_count, pv_mda_used_count(pv))
+#define _pv_mda_used_count_set _not_implemented_set
+
+/* LV */
+GET_LV_STR_PROPERTY_FN(lv_uuid, lv_uuid_dup(lv))
+#define _lv_uuid_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(lv_name, lv_name_dup(lv->vg->vgmem, lv))
+#define _lv_name_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(lv_path, lv_path_dup(lv->vg->vgmem, lv))
+#define _lv_path_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(lv_attr, lv_attr_dup(lv->vg->vgmem, lv))
+#define _lv_attr_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_major, lv->major)
+#define _lv_major_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_minor, lv->minor)
+#define _lv_minor_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_read_ahead, lv->read_ahead * SECTOR_SIZE)
+#define _lv_read_ahead_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_kernel_major, lv_kernel_major(lv))
+#define _lv_kernel_major_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_kernel_minor, lv_kernel_minor(lv))
+#define _lv_kernel_minor_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_kernel_read_ahead, lv_kernel_read_ahead(lv) * SECTOR_SIZE)
+#define _lv_kernel_read_ahead_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(lv_size, lv->size * SECTOR_SIZE)
+#define _lv_size_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(seg_count, dm_list_size(&lv->segments))
+#define _seg_count_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(origin, lv_origin_dup(lv->vg->vgmem, lv))
+#define _origin_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(origin_size, lv_origin_size(lv))
+#define _origin_size_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(snap_percent, _snap_percent(lv))
+#define _snap_percent_set _not_implemented_set
+GET_LV_NUM_PROPERTY_FN(copy_percent, _copy_percent(lv))
+#define _copy_percent_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(move_pv, lv_move_pv_dup(lv->vg->vgmem, lv))
+#define _move_pv_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(convert_lv, lv_convert_lv_dup(lv->vg->vgmem, lv))
+#define _convert_lv_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(lv_tags, lv_tags_dup(lv))
+#define _lv_tags_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(mirror_log, lv_mirror_log_dup(lv->vg->vgmem, lv))
+#define _mirror_log_set _not_implemented_set
+GET_LV_STR_PROPERTY_FN(modules, lv_modules_dup(lv->vg->vgmem, lv))
+#define _modules_set _not_implemented_set
+
+/* VG */
+GET_VG_STR_PROPERTY_FN(vg_fmt, vg_fmt_dup(vg))
+#define _vg_fmt_set _not_implemented_set
+GET_VG_STR_PROPERTY_FN(vg_uuid, vg_uuid_dup(vg))
+#define _vg_uuid_set _not_implemented_set
+GET_VG_STR_PROPERTY_FN(vg_name, vg_name_dup(vg))
+#define _vg_name_set _not_implemented_set
+GET_VG_STR_PROPERTY_FN(vg_attr, vg_attr_dup(vg->vgmem, vg))
+#define _vg_attr_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_size, (SECTOR_SIZE * vg_size(vg)))
+#define _vg_size_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_free, (SECTOR_SIZE * vg_free(vg)))
+#define _vg_free_set _not_implemented_set
+GET_VG_STR_PROPERTY_FN(vg_sysid, vg_system_id_dup(vg))
+#define _vg_sysid_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_extent_size, vg->extent_size)
+#define _vg_extent_size_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_extent_count, vg->extent_count)
+#define _vg_extent_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_free_count, vg->free_count)
+#define _vg_free_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(max_lv, vg->max_lv)
+#define _max_lv_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(max_pv, vg->max_pv)
+#define _max_pv_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(pv_count, vg->pv_count)
+#define _pv_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(lv_count, (vg_visible_lvs(vg)))
+#define _lv_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(snap_count, (snapshot_count(vg)))
+#define _snap_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_seqno, vg->seqno)
+#define _vg_seqno_set _not_implemented_set
+GET_VG_STR_PROPERTY_FN(vg_tags, vg_tags_dup(vg))
+#define _vg_tags_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_mda_count, (vg_mda_count(vg)))
+#define _vg_mda_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_mda_used_count, (vg_mda_used_count(vg)))
+#define _vg_mda_used_count_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_mda_free, (SECTOR_SIZE * vg_mda_free(vg)))
+#define _vg_mda_free_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_mda_size, (SECTOR_SIZE * vg_mda_size(vg)))
+#define _vg_mda_size_set _not_implemented_set
+GET_VG_NUM_PROPERTY_FN(vg_mda_copies, (vg_mda_copies(vg)))
+SET_VG_NUM_PROPERTY_FN(vg_mda_copies, vg_set_mda_copies)
+
+/* LVSEG */
+GET_LVSEG_STR_PROPERTY_FN(segtype, lvseg_segtype_dup(lvseg))
+#define _segtype_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(stripes, lvseg->area_count)
+#define _stripes_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(stripesize, lvseg->stripe_size)
+#define _stripesize_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(stripe_size, lvseg->stripe_size)
+#define _stripe_size_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(regionsize, lvseg->region_size)
+#define _regionsize_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(region_size, lvseg->region_size)
+#define _region_size_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(chunksize, lvseg_chunksize(lvseg))
+#define _chunksize_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(chunk_size, lvseg_chunksize(lvseg))
+#define _chunk_size_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(seg_start, lvseg_start(lvseg))
+#define _seg_start_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(seg_start_pe, lvseg->le)
+#define _seg_start_pe_set _not_implemented_set
+GET_LVSEG_NUM_PROPERTY_FN(seg_size, (SECTOR_SIZE * lvseg_size(lvseg)))
+#define _seg_size_set _not_implemented_set
+GET_LVSEG_STR_PROPERTY_FN(seg_tags, lvseg_tags_dup(lvseg))
+#define _seg_tags_set _not_implemented_set
+#define _seg_pe_ranges_get _not_implemented_get
+#define _seg_pe_ranges_set _not_implemented_set
+#define _devices_get _not_implemented_get
+#define _devices_set _not_implemented_set
+
+
+/* PVSEG */
+GET_PVSEG_NUM_PROPERTY_FN(pvseg_start, pvseg->pe)
+#define _pvseg_start_set _not_implemented_set
+GET_PVSEG_NUM_PROPERTY_FN(pvseg_size, pvseg->len)
+#define _pvseg_size_set _not_implemented_set
+
+
+#define STR DM_REPORT_FIELD_TYPE_STRING
+#define NUM DM_REPORT_FIELD_TYPE_NUMBER
+#define FIELD(type, strct, sorttype, head, field, width, fn, id, desc, settable) \
+ { type, #id, settable, sorttype == STR, sorttype == NUM, { .integer = 0 }, _ ## id ## _get, _ ## id ## _set },
+
+struct lvm_property_type _properties[] = {
+#include "columns.h"
+ { 0, "", 0, 0, 0, { .integer = 0 }, _not_implemented_get, _not_implemented_set },
+};
+
+#undef STR
+#undef NUM
+#undef FIELD
+
+
+static int _get_property(const void *obj, struct lvm_property_type *prop,
+ report_type_t type)
+{
+ struct lvm_property_type *p;
+
+ p = _properties;
+ while (p->id[0]) {
+ if (!strcmp(p->id, prop->id))
+ break;
+ p++;
+ }
+ if (!p->id[0]) {
+ log_errno(EINVAL, "Invalid property name %s", prop->id);
+ return 0;
+ }
+ if (!(p->type & type)) {
+ log_errno(EINVAL, "Property name %s does not match type %d",
+ prop->id, p->type);
+ return 0;
+ }
+
+ *prop = *p;
+ if (!p->get(obj, prop)) {
+ return 0;
+ }
+ return 1;
+}
+
+static int _set_property(void *obj, struct lvm_property_type *prop,
+ report_type_t type)
+{
+ struct lvm_property_type *p;
+
+ p = _properties;
+ while (p->id[0]) {
+ if (!strcmp(p->id, prop->id))
+ break;
+ p++;
+ }
+ if (!p->id[0]) {
+ log_errno(EINVAL, "Invalid property name %s", prop->id);
+ return 0;
+ }
+ if (!p->is_settable) {
+ log_errno(EINVAL, "Unable to set read-only property %s",
+ prop->id);
+ return 0;
+ }
+ if (!(p->type & type)) {
+ log_errno(EINVAL, "Property name %s does not match type %d",
+ prop->id, p->type);
+ return 0;
+ }
+
+ if (p->is_string)
+ p->value.string = prop->value.string;
+ else
+ p->value.integer = prop->value.integer;
+ if (!p->set(obj, p)) {
+ return 0;
+ }
+ return 1;
+}
+
+int lvseg_get_property(const struct lv_segment *lvseg,
+ struct lvm_property_type *prop)
+{
+ return _get_property(lvseg, prop, SEGS);
+}
+
+int lv_get_property(const struct logical_volume *lv,
+ struct lvm_property_type *prop)
+{
+ return _get_property(lv, prop, LVS);
+}
+
+int vg_get_property(const struct volume_group *vg,
+ struct lvm_property_type *prop)
+{
+ return _get_property(vg, prop, VGS);
+}
+
+int pvseg_get_property(const struct pv_segment *pvseg,
+ struct lvm_property_type *prop)
+{
+ return _get_property(pvseg, prop, PVSEGS);
+}
+
+int pv_get_property(const struct physical_volume *pv,
+ struct lvm_property_type *prop)
+{
+ return _get_property(pv, prop, PVS | LABEL);
+}
+
+int lv_set_property(struct logical_volume *lv,
+ struct lvm_property_type *prop)
+{
+ return _set_property(lv, prop, LVS);
+}
+
+int vg_set_property(struct volume_group *vg,
+ struct lvm_property_type *prop)
+{
+ return _set_property(vg, prop, VGS);
+}
+
+int pv_set_property(struct physical_volume *pv,
+ struct lvm_property_type *prop)
+{
+ return _set_property(pv, prop, PVS | LABEL);
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _LVM_PROPERTIES_H
+#define _LVM_PROPERTIES_H
+
+#include "libdevmapper.h"
+#include "lvm-types.h"
+#include "metadata.h"
+#include "report.h"
+
+struct lvm_property_type {
+ report_type_t type;
+ const char *id;
+ unsigned is_settable:1;
+ unsigned is_string:1;
+ unsigned is_integer:1;
+ union {
+ const char *string;
+ uint64_t integer;
+ } value;
+ int (*get) (const void *obj, struct lvm_property_type *prop);
+ int (*set) (void *obj, struct lvm_property_type *prop);
+};
+
+int lvseg_get_property(const struct lv_segment *lvseg,
+ struct lvm_property_type *prop);
+int lv_get_property(const struct logical_volume *lv,
+ struct lvm_property_type *prop);
+int vg_get_property(const struct volume_group *vg,
+ struct lvm_property_type *prop);
+int pvseg_get_property(const struct pv_segment *pvseg,
+ struct lvm_property_type *prop);
+int pv_get_property(const struct physical_volume *pv,
+ struct lvm_property_type *prop);
+int lv_set_property(struct logical_volume *lv,
+ struct lvm_property_type *prop);
+int vg_set_property(struct volume_group *vg,
+ struct lvm_property_type *prop);
+int pv_set_property(struct physical_volume *pv,
+ struct lvm_property_type *prop);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "report.h"
+#include "toolcontext.h"
+#include "lvm-string.h"
+#include "display.h"
+#include "activate.h"
+#include "segtype.h"
+#include "str_list.h"
+#include "lvmcache.h"
+
+#include <stddef.h> /* offsetof() */
+
+struct lvm_report_object {
+ struct volume_group *vg;
+ struct logical_volume *lv;
+ struct physical_volume *pv;
+ struct lv_segment *seg;
+ struct pv_segment *pvseg;
+};
+
+static const uint64_t _minusone64 = UINT64_C(-1);
+static const int32_t _minusone32 = INT32_C(-1);
+
+/*
+ * Data-munging functions to prepare each data type for display and sorting
+ */
+static int _string_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 **) data);
+}
+
+static int _dev_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 = dev_name(*(const struct device * const *) data);
+
+ return dm_report_field_string(rh, field, &name);
+}
+
+static int _format_pvsegs(struct dm_pool *mem, struct dm_report_field *field,
+ const void *data, int range_format)
+{
+ const struct lv_segment *seg = (const struct lv_segment *) data;
+ unsigned int s;
+ const char *name = NULL;
+ uint32_t extent = 0;
+ char extent_str[32];
+
+ if (!dm_pool_begin_object(mem, 256)) {
+ log_error("dm_pool_begin_object failed");
+ return 0;
+ }
+
+ for (s = 0; s < seg->area_count; s++) {
+ switch (seg_type(seg, s)) {
+ case AREA_LV:
+ name = seg_lv(seg, s)->name;
+ extent = seg_le(seg, s);
+ break;
+ case AREA_PV:
+ name = dev_name(seg_dev(seg, s));
+ extent = seg_pe(seg, s);
+ break;
+ case AREA_UNASSIGNED:
+ name = "unassigned";
+ extent = 0;
+ }
+
+ if (!dm_pool_grow_object(mem, name, strlen(name))) {
+ log_error("dm_pool_grow_object failed");
+ return 0;
+ }
+
+ if (dm_snprintf(extent_str, sizeof(extent_str),
+ "%s%" PRIu32 "%s",
+ range_format ? ":" : "(", extent,
+ range_format ? "-" : ")") < 0) {
+ log_error("Extent number dm_snprintf failed");
+ return 0;
+ }
+ if (!dm_pool_grow_object(mem, extent_str, strlen(extent_str))) {
+ log_error("dm_pool_grow_object failed");
+ return 0;
+ }
+
+ if (range_format) {
+ if (dm_snprintf(extent_str, sizeof(extent_str),
+ "%" PRIu32, extent + seg->area_len - 1) < 0) {
+ log_error("Extent number dm_snprintf failed");
+ return 0;
+ }
+ if (!dm_pool_grow_object(mem, extent_str, strlen(extent_str))) {
+ log_error("dm_pool_grow_object failed");
+ return 0;
+ }
+ }
+
+ if ((s != seg->area_count - 1) &&
+ !dm_pool_grow_object(mem, range_format ? " " : ",", 1)) {
+ log_error("dm_pool_grow_object failed");
+ return 0;
+ }
+ }
+
+ if (!dm_pool_grow_object(mem, "\0", 1)) {
+ log_error("dm_pool_grow_object failed");
+ return 0;
+ }
+
+ dm_report_field_set_value(field, dm_pool_end_object(mem), NULL);
+
+ return 1;
+}
+
+static int _devices_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ return _format_pvsegs(mem, field, data, 0);
+}
+
+static int _peranges_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ return _format_pvsegs(mem, field, data, 1);
+}
+
+static int _tags_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ const struct dm_list *tags = (const struct dm_list *) data;
+ char *tags_str;
+
+ if (!(tags_str = tags_format_and_copy(mem, tags)))
+ return 0;
+
+ dm_report_field_set_value(field, tags_str, NULL);
+
+ return 1;
+}
+
+static int _modules_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct logical_volume *lv = (const struct logical_volume *) data;
+ char *modules_str;
+
+ if (!(modules_str = lv_modules_dup(mem, lv)))
+ return 0;
+
+ dm_report_field_set_value(field, modules_str, NULL);
+ return 1;
+}
+
+static int _vgfmt_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct volume_group *vg = (const struct volume_group *) data;
+
+ if (!vg->fid) {
+ dm_report_field_set_value(field, "", NULL);
+ return 1;
+ }
+
+ return _string_disp(rh, mem, field, &vg->fid->fmt->name, private);
+}
+
+static int _pvfmt_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct physical_volume *pv =
+ (const struct physical_volume *) data;
+
+ if (!pv->fmt) {
+ dm_report_field_set_value(field, "", NULL);
+ return 1;
+ }
+
+ return _string_disp(rh, mem, field, &pv->fmt->name, private);
+}
+
+static int _lvkmaj_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 logical_volume *lv = (const struct logical_volume *) data;
+ int major;
+
+ if ((major = lv_kernel_major(lv)) >= 0)
+ return dm_report_field_int(rh, field, &major);
+
+ return dm_report_field_int32(rh, field, &_minusone32);
+}
+
+static int _lvkmin_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 logical_volume *lv = (const struct logical_volume *) data;
+ int minor;
+
+ if ((minor = lv_kernel_minor(lv)) >= 0)
+ return dm_report_field_int(rh, field, &minor);
+
+ return dm_report_field_int32(rh, field, &_minusone32);
+}
+
+static int _lvstatus_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ const struct logical_volume *lv = (const struct logical_volume *) data;
+ char *repstr;
+
+ if (!(repstr = lv_attr_dup(mem, lv)))
+ return 0;
+
+ dm_report_field_set_value(field, repstr, NULL);
+ return 1;
+}
+
+static int _pvstatus_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ const struct physical_volume *pv =
+ (const struct physical_volume *) data;
+ char *repstr;
+
+ if (!(repstr = pv_attr_dup(mem, pv)))
+ return 0;
+
+ dm_report_field_set_value(field, repstr, NULL);
+ return 1;
+}
+
+static int _vgstatus_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ const struct volume_group *vg = (const struct volume_group *) data;
+ char *repstr;
+
+ if (!(repstr = vg_attr_dup(mem, vg)))
+ return 0;
+
+ dm_report_field_set_value(field, repstr, NULL);
+ return 1;
+}
+
+static int _segtype_disp(struct dm_report *rh __attribute__((unused)),
+ struct dm_pool *mem __attribute__((unused)),
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ const struct lv_segment *seg = (const struct lv_segment *) data;
+
+ char *name;
+ name = lvseg_segtype_dup(seg);
+ dm_report_field_set_value(field, name, NULL);
+ return 1;
+}
+
+static int _loglv_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 logical_volume *lv = (const struct logical_volume *) data;
+ const char *name;
+
+ if ((name = lv_mirror_log_dup(mem, lv)))
+ return dm_report_field_string(rh, field, &name);
+
+ dm_report_field_set_value(field, "", NULL);
+ return 1;
+}
+
+static int _lvname_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ const struct logical_volume *lv = (const struct logical_volume *) data;
+ char *repstr, *lvname;
+ size_t len;
+
+ if (lv_is_visible(lv)) {
+ repstr = lv->name;
+ return dm_report_field_string(rh, field, (const char **) &repstr);
+ }
+
+ len = strlen(lv->name) + 3;
+ if (!(repstr = dm_pool_zalloc(mem, len))) {
+ log_error("dm_pool_alloc failed");
+ return 0;
+ }
+
+ if (dm_snprintf(repstr, len, "[%s]", lv->name) < 0) {
+ log_error("lvname snprintf failed");
+ return 0;
+ }
+
+ if (!(lvname = dm_pool_strdup(mem, lv->name))) {
+ log_error("dm_pool_strdup failed");
+ return 0;
+ }
+
+ dm_report_field_set_value(field, repstr, lvname);
+
+ return 1;
+}
+
+static int _lvpath_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ const struct logical_volume *lv = (const struct logical_volume *) data;
+ char *repstr;
+
+ if (!(repstr = lv_path_dup(mem, lv)))
+ return 0;
+
+ dm_report_field_set_value(field, repstr, NULL);
+
+ return 1;
+}
+
+static int _origin_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct logical_volume *lv = (const struct logical_volume *) data;
+
+ if (lv_is_cow(lv))
+ return _lvname_disp(rh, mem, field, origin_from_cow(lv), private);
+
+ dm_report_field_set_value(field, "", NULL);
+ return 1;
+}
+
+static int _movepv_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 logical_volume *lv = (const struct logical_volume *) data;
+ const char *name;
+
+ if (!(name = lv_move_pv_dup(mem, lv)))
+ dm_report_field_set_value(field, "", NULL);
+ else
+ return dm_report_field_string(rh, field, &name);
+ return 1;
+}
+
+static int _convertlv_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 logical_volume *lv = (const struct logical_volume *) data;
+ const char *name = NULL;
+
+ name = lv_convert_lv_dup(mem, lv);
+ if (name)
+ return dm_report_field_string(rh, field, &name);
+
+ dm_report_field_set_value(field, "", NULL);
+ return 1;
+}
+
+static int _size32_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const uint32_t size = *(const uint32_t *) data;
+ const char *disp, *repstr;
+ uint64_t *sortval;
+
+ if (!*(disp = display_size_units(private, (uint64_t) size)))
+ return_0;
+
+ if (!(repstr = dm_pool_strdup(mem, disp))) {
+ log_error("dm_pool_strdup failed");
+ return 0;
+ }
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
+ log_error("dm_pool_alloc failed");
+ return 0;
+ }
+
+ *sortval = (const uint64_t) size;
+
+ dm_report_field_set_value(field, repstr, sortval);
+
+ return 1;
+}
+
+static int _size64_disp(struct dm_report *rh __attribute__((unused)),
+ struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const uint64_t size = *(const uint64_t *) data;
+ const char *disp, *repstr;
+ uint64_t *sortval;
+
+ if (!*(disp = display_size_units(private, size)))
+ return_0;
+
+ if (!(repstr = dm_pool_strdup(mem, disp))) {
+ log_error("dm_pool_strdup failed");
+ return 0;
+ }
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
+ log_error("dm_pool_alloc failed");
+ return 0;
+ }
+
+ *sortval = size;
+ dm_report_field_set_value(field, repstr, sortval);
+
+ return 1;
+}
+
+static int _lvreadahead_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ const struct logical_volume *lv = (const struct logical_volume *) data;
+
+ if (lv->read_ahead == DM_READ_AHEAD_AUTO) {
+ dm_report_field_set_value(field, "auto", &_minusone64);
+ return 1;
+ }
+
+ return _size32_disp(rh, mem, field, &lv->read_ahead, private);
+}
+
+static int _lvkreadahead_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data,
+ void *private)
+{
+ const struct logical_volume *lv = (const struct logical_volume *) data;
+ uint32_t read_ahead;
+
+ if ((read_ahead = lv_kernel_read_ahead(lv)) == UINT32_MAX)
+ return dm_report_field_int32(rh, field, &_minusone32);
+
+ return _size32_disp(rh, mem, field, &read_ahead, private);
+}
+
+static int _vgsize_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct volume_group *vg = (const struct volume_group *) data;
+ uint64_t size;
+
+ size = (uint64_t) vg_size(vg);
+
+ return _size64_disp(rh, mem, field, &size, private);
+}
+
+static int _segstart_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct lv_segment *seg = (const struct lv_segment *) data;
+ uint64_t start;
+
+ start = lvseg_start(seg);
+
+ return _size64_disp(rh, mem, field, &start, private);
+}
+
+static int _segstartpe_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 lv_segment *seg = (const struct lv_segment *) data;
+
+ return dm_report_field_uint32(rh, field, &seg->le);
+}
+
+static int _segsize_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct lv_segment *seg = (const struct lv_segment *) data;
+ uint64_t size;
+
+ size = lvseg_size(seg);
+
+ return _size64_disp(rh, mem, field, &size, private);
+}
+
+static int _chunksize_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct lv_segment *seg = (const struct lv_segment *) data;
+ uint64_t size;
+
+ size = lvseg_chunksize(seg);
+
+ return _size64_disp(rh, mem, field, &size, private);
+}
+
+static int _originsize_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct logical_volume *lv = (const struct logical_volume *) data;
+ uint64_t size;
+
+ size = lv_origin_size(lv);
+
+ return _size64_disp(rh, mem, field, &size, private);
+}
+
+static int _pvused_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct physical_volume *pv =
+ (const struct physical_volume *) data;
+ uint64_t used;
+
+ used = pv_used(pv);
+
+ return _size64_disp(rh, mem, field, &used, private);
+}
+
+static int _pvfree_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct physical_volume *pv =
+ (const struct physical_volume *) data;
+ uint64_t freespace;
+
+ freespace = pv_free(pv);
+
+ return _size64_disp(rh, mem, field, &freespace, private);
+}
+
+static int _pvsize_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct physical_volume *pv =
+ (const struct physical_volume *) data;
+ uint64_t size;
+
+ size = pv_size_field(pv);
+
+ return _size64_disp(rh, mem, field, &size, private);
+}
+
+static int _devsize_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct physical_volume *pv =
+ (const struct physical_volume *) data;
+ uint64_t size;
+
+ size = pv_dev_size(pv);
+
+ return _size64_disp(rh, mem, field, &size, private);
+}
+
+static int _vgfree_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct volume_group *vg = (const struct volume_group *) data;
+ uint64_t freespace;
+
+ freespace = (uint64_t) vg_free(vg);
+
+ return _size64_disp(rh, mem, field, &freespace, private);
+}
+
+static int _uuid_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ char *repstr = NULL;
+
+ if (!(repstr = id_format_and_copy(mem, (struct id *)data)))
+ return_0;
+
+ dm_report_field_set_value(field, repstr, NULL);
+ return 1;
+}
+
+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)))
+{
+ return dm_report_field_uint32(rh, field, data);
+}
+
+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)))
+{
+ return dm_report_field_int32(rh, field, data);
+}
+
+static int _pvmdas_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ uint32_t count;
+ const struct physical_volume *pv =
+ (const struct physical_volume *) data;
+
+ count = pv_mda_count(pv);
+
+ return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _pvmdasused_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ uint32_t count;
+ const struct physical_volume *pv =
+ (const struct physical_volume *) data;
+
+ count = pv_mda_used_count(pv);
+
+ return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _vgmdas_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct volume_group *vg = (const struct volume_group *) data;
+ uint32_t count;
+
+ count = vg_mda_count(vg);
+
+ return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _vgmdasused_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct volume_group *vg = (const struct volume_group *) data;
+ uint32_t count;
+
+ count = vg_mda_used_count(vg);
+
+ return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _vgmdacopies_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct volume_group *vg = (const struct volume_group *) data;
+ uint32_t count;
+
+ count = vg_mda_copies(vg);
+
+ if (count == VGMETADATACOPIES_UNMANAGED) {
+ dm_report_field_set_value(field, "unmanaged", &_minusone64);
+ return 1;
+ }
+
+ return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _pvmdafree_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct physical_volume *pv =
+ (const struct physical_volume *) data;
+ uint64_t freespace;
+
+ freespace = pv_mda_free(pv);
+
+ return _size64_disp(rh, mem, field, &freespace, private);
+}
+
+static int _pvmdasize_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct physical_volume *pv =
+ (const struct physical_volume *) data;
+ uint64_t min_mda_size;
+
+ min_mda_size = pv_mda_size(pv);
+
+ return _size64_disp(rh, mem, field, &min_mda_size, private);
+}
+
+static int _vgmdasize_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct volume_group *vg = (const struct volume_group *) data;
+ uint64_t min_mda_size;
+
+ min_mda_size = vg_mda_size(vg);
+
+ return _size64_disp(rh, mem, field, &min_mda_size, private);
+}
+
+static int _vgmdafree_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct volume_group *vg = (const struct volume_group *) data;
+ uint64_t freespace;
+
+ freespace = vg_mda_free(vg);
+
+ return _size64_disp(rh, mem, field, &freespace, private);
+}
+
+static int _lvcount_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct volume_group *vg = (const struct volume_group *) data;
+ uint32_t count;
+
+ count = vg_visible_lvs(vg);
+
+ return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _lvsegcount_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct logical_volume *lv = (const struct logical_volume *) data;
+ uint32_t count;
+
+ count = dm_list_size(&lv->segments);
+
+ return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _snapcount_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ const struct volume_group *vg = (const struct volume_group *) data;
+ uint32_t count;
+
+ count = snapshot_count(vg);
+
+ return _uint32_disp(rh, mem, field, &count, private);
+}
+
+static int _snpercent_disp(struct dm_report *rh __attribute__((unused)), struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ const struct logical_volume *lv = (const struct logical_volume *) data;
+ struct lvinfo info;
+ percent_t snap_percent;
+ uint64_t *sortval;
+ char *repstr;
+
+ /* Suppress snapshot percentage if not using driver */
+ if (!activation()) {
+ dm_report_field_set_value(field, "", NULL);
+ return 1;
+ }
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
+ log_error("dm_pool_alloc failed");
+ return 0;
+ }
+
+ if ((!lv_is_cow(lv) && !lv_is_merging_origin(lv)) ||
+ !lv_info(lv->vg->cmd, lv, 0, &info, 0, 0) || !info.exists) {
+ *sortval = UINT64_C(0);
+ dm_report_field_set_value(field, "", sortval);
+ return 1;
+ }
+
+ if (!lv_snapshot_percent(lv, &snap_percent) ||
+ (snap_percent == PERCENT_INVALID)) {
+ if (!lv_is_merging_origin(lv)) {
+ *sortval = UINT64_C(100);
+ dm_report_field_set_value(field, "100.00", sortval);
+ } else {
+ /* onactivate merge that hasn't started yet would
+ * otherwise display incorrect snap% in origin
+ */
+ *sortval = UINT64_C(0);
+ dm_report_field_set_value(field, "", sortval);
+ }
+ return 1;
+ }
+
+ if (!(repstr = dm_pool_zalloc(mem, 8))) {
+ log_error("dm_pool_alloc failed");
+ return 0;
+ }
+
+ if (dm_snprintf(repstr, 7, "%.2f", percent_to_float(snap_percent)) < 0) {
+ log_error("snapshot percentage too large");
+ return 0;
+ }
+
+ *sortval = (uint64_t)(snap_percent * 1000.f);
+ dm_report_field_set_value(field, repstr, sortval);
+
+ return 1;
+}
+
+static int _copypercent_disp(struct dm_report *rh __attribute__((unused)),
+ struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private __attribute__((unused)))
+{
+ struct logical_volume *lv = (struct logical_volume *) data;
+ percent_t percent;
+ uint64_t *sortval;
+ char *repstr;
+
+ if (!(sortval = dm_pool_alloc(mem, sizeof(uint64_t)))) {
+ log_error("dm_pool_alloc failed");
+ return 0;
+ }
+
+ if ((!(lv->status & PVMOVE) && !(lv->status & MIRRORED)) ||
+ !lv_mirror_percent(lv->vg->cmd, lv, 0, &percent,
+ NULL) || (percent == PERCENT_INVALID)) {
+ *sortval = UINT64_C(0);
+ dm_report_field_set_value(field, "", sortval);
+ return 1;
+ }
+
+ percent = copy_percent(lv);
+
+ if (!(repstr = dm_pool_zalloc(mem, 8))) {
+ log_error("dm_pool_alloc failed");
+ return 0;
+ }
+
+ if (dm_snprintf(repstr, 7, "%.2f", percent_to_float(percent)) < 0) {
+ log_error("copy percentage too large");
+ return 0;
+ }
+
+ *sortval = (uint64_t)(percent * 1000.f);
+ dm_report_field_set_value(field, repstr, sortval);
+
+ return 1;
+}
+
+/* Report object types */
+
+/* necessary for displaying something for PVs not belonging to VG */
+static struct format_instance _dummy_fid = {
+ .metadata_areas_in_use = { &(_dummy_fid.metadata_areas_in_use), &(_dummy_fid.metadata_areas_in_use) },
+ .metadata_areas_ignored = { &(_dummy_fid.metadata_areas_ignored), &(_dummy_fid.metadata_areas_ignored) },
+};
+
+static struct volume_group _dummy_vg = {
+ .fid = &_dummy_fid,
+ .name = (char *) "",
+ .system_id = (char *) "",
+ .pvs = { &(_dummy_vg.pvs), &(_dummy_vg.pvs) },
+ .lvs = { &(_dummy_vg.lvs), &(_dummy_vg.lvs) },
+ .tags = { &(_dummy_vg.tags), &(_dummy_vg.tags) },
+};
+
+static void *_obj_get_vg(void *obj)
+{
+ struct volume_group *vg = ((struct lvm_report_object *)obj)->vg;
+
+ return vg ? vg : &_dummy_vg;
+}
+
+static void *_obj_get_lv(void *obj)
+{
+ return ((struct lvm_report_object *)obj)->lv;
+}
+
+static void *_obj_get_pv(void *obj)
+{
+ return ((struct lvm_report_object *)obj)->pv;
+}
+
+static void *_obj_get_seg(void *obj)
+{
+ return ((struct lvm_report_object *)obj)->seg;
+}
+
+static void *_obj_get_pvseg(void *obj)
+{
+ return ((struct lvm_report_object *)obj)->pvseg;
+}
+
+static const struct dm_report_object_type _report_types[] = {
+ { VGS, "Volume Group", "vg_", _obj_get_vg },
+ { LVS, "Logical Volume", "lv_", _obj_get_lv },
+ { PVS, "Physical Volume", "pv_", _obj_get_pv },
+ { LABEL, "Physical Volume Label", "pv_", _obj_get_pv },
+ { SEGS, "Logical Volume Segment", "seg_", _obj_get_seg },
+ { PVSEGS, "Physical Volume Segment", "pvseg_", _obj_get_pvseg },
+ { 0, "", "", NULL },
+};
+
+/*
+ * Import column definitions
+ */
+
+#define STR DM_REPORT_FIELD_TYPE_STRING
+#define NUM DM_REPORT_FIELD_TYPE_NUMBER
+#define FIELD(type, strct, sorttype, head, field, width, func, id, desc, writeable) \
+ {type, sorttype, offsetof(type_ ## strct, field), width, \
+ #id, head, &_ ## func ## _disp, desc},
+
+typedef struct physical_volume type_pv;
+typedef struct logical_volume type_lv;
+typedef struct volume_group type_vg;
+typedef struct lv_segment type_seg;
+typedef struct pv_segment type_pvseg;
+
+static const struct dm_report_field_type _fields[] = {
+#include "columns.h"
+{0, 0, 0, 0, "", "", NULL, NULL},
+};
+
+#undef STR
+#undef NUM
+#undef FIELD
+
+void *report_init(struct cmd_context *cmd, const char *format, const char *keys,
+ report_type_t *report_type, const char *separator,
+ int aligned, int buffered, int headings, int field_prefixes,
+ int quoted, int columns_as_rows)
+{
+ uint32_t report_flags = 0;
+ void *rh;
+
+ if (aligned)
+ report_flags |= DM_REPORT_OUTPUT_ALIGNED;
+
+ if (buffered)
+ report_flags |= DM_REPORT_OUTPUT_BUFFERED;
+
+ if (headings)
+ report_flags |= DM_REPORT_OUTPUT_HEADINGS;
+
+ if (field_prefixes)
+ report_flags |= DM_REPORT_OUTPUT_FIELD_NAME_PREFIX;
+
+ if (!quoted)
+ report_flags |= DM_REPORT_OUTPUT_FIELD_UNQUOTED;
+
+ if (columns_as_rows)
+ report_flags |= DM_REPORT_OUTPUT_COLUMNS_AS_ROWS;
+
+ rh = dm_report_init(report_type, _report_types, _fields, format,
+ separator, report_flags, keys, cmd);
+
+ if (rh && field_prefixes)
+ dm_report_set_output_field_name_prefix(rh, "lvm2_");
+
+ return rh;
+}
+
+/*
+ * Create a row of data for an object
+ */
+int report_object(void *handle, struct volume_group *vg,
+ struct logical_volume *lv, struct physical_volume *pv,
+ struct lv_segment *seg, struct pv_segment *pvseg)
+{
+ struct lvm_report_object obj;
+
+ /* The two format fields might as well match. */
+ if (!vg && pv)
+ _dummy_fid.fmt = pv->fmt;
+
+ obj.vg = vg;
+ obj.lv = lv;
+ obj.pv = pv;
+ obj.seg = seg;
+ obj.pvseg = pvseg;
+
+ return dm_report_object(handle, &obj);
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_REPORT_H
+#define _LVM_REPORT_H
+
+#include "metadata-exported.h"
+
+typedef enum {
+ LVS = 1,
+ PVS = 2,
+ VGS = 4,
+ SEGS = 8,
+ PVSEGS = 16,
+ LABEL = 32
+} report_type_t;
+
+struct field;
+struct report_handle;
+
+typedef int (*field_report_fn) (struct report_handle * dh, struct field * field,
+ const void *data);
+
+void *report_init(struct cmd_context *cmd, const char *format, const char *keys,
+ report_type_t *report_type, const char *separator,
+ int aligned, int buffered, int headings, int field_prefixes,
+ int quoted, int columns_as_rows);
+void report_free(void *handle);
+int report_object(void *handle, struct volume_group *vg,
+ struct logical_volume *lv, struct physical_volume *pv,
+ struct lv_segment *seg, struct pv_segment *pvseg);
+int report_output(void *handle);
+
+#endif
--- /dev/null
+init_segtype
--- /dev/null
+#
+# Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES = snapshot.c
+
+LIB_SHARED = liblvm2snapshot.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_LVM)
+
+include $(top_builddir)/make.tmpl
+
+install: install_lvm2_plugin
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "metadata.h"
+#include "segtype.h"
+#include "text_export.h"
+#include "config.h"
+#include "activate.h"
+#include "str_list.h"
+#include "defaults.h"
+
+static const char *_snap_name(const struct lv_segment *seg)
+{
+ return seg->segtype->name;
+}
+
+static const char *_snap_target_name(const struct lv_segment *seg)
+{
+ if (seg->status & MERGING)
+ return "snapshot-merge";
+
+ return _snap_name(seg);
+}
+
+static int _snap_text_import(struct lv_segment *seg, const struct config_node *sn,
+ struct dm_hash_table *pv_hash __attribute__((unused)))
+{
+ uint32_t chunk_size;
+ const char *org_name, *cow_name;
+ struct logical_volume *org, *cow;
+ int old_suppress, merge = 0;
+
+ if (!get_config_uint32(sn, "chunk_size", &chunk_size)) {
+ log_error("Couldn't read chunk size for snapshot.");
+ return 0;
+ }
+
+ old_suppress = log_suppress(1);
+
+ if ((cow_name = find_config_str(sn, "merging_store", NULL))) {
+ if (find_config_str(sn, "cow_store", NULL)) {
+ log_suppress(old_suppress);
+ log_error("Both snapshot cow and merging storage were specified.");
+ return 0;
+ }
+ merge = 1;
+ }
+ else if (!(cow_name = find_config_str(sn, "cow_store", NULL))) {
+ log_suppress(old_suppress);
+ log_error("Snapshot cow storage not specified.");
+ return 0;
+ }
+
+ if (!(org_name = find_config_str(sn, "origin", NULL))) {
+ log_suppress(old_suppress);
+ log_error("Snapshot origin not specified.");
+ return 0;
+ }
+
+ log_suppress(old_suppress);
+
+ if (!(cow = find_lv(seg->lv->vg, cow_name))) {
+ log_error("Unknown logical volume specified for "
+ "snapshot cow store.");
+ return 0;
+ }
+
+ if (!(org = find_lv(seg->lv->vg, org_name))) {
+ log_error("Unknown logical volume specified for "
+ "snapshot origin.");
+ return 0;
+ }
+
+ init_snapshot_seg(seg, org, cow, chunk_size, merge);
+
+ return 1;
+}
+
+static int _snap_text_export(const struct lv_segment *seg, struct formatter *f)
+{
+ outf(f, "chunk_size = %u", seg->chunk_size);
+ outf(f, "origin = \"%s\"", seg->origin->name);
+ if (!(seg->status & MERGING))
+ outf(f, "cow_store = \"%s\"", seg->cow->name);
+ else
+ outf(f, "merging_store = \"%s\"", seg->cow->name);
+
+ return 1;
+}
+
+static int _snap_target_status_compatible(const char *type)
+{
+ return (strcmp(type, "snapshot-merge") == 0);
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static int _snap_target_percent(void **target_state __attribute__((unused)),
+ percent_t *percent,
+ struct dm_pool *mem __attribute__((unused)),
+ struct cmd_context *cmd __attribute__((unused)),
+ struct lv_segment *seg __attribute__((unused)),
+ char *params, uint64_t *total_numerator,
+ uint64_t *total_denominator)
+{
+ uint64_t total_sectors, sectors_allocated, metadata_sectors;
+ int r;
+
+ /*
+ * snapshot target's percent format:
+ * <= 1.7.0: <sectors_allocated>/<total_sectors>
+ * >= 1.8.0: <sectors_allocated>/<total_sectors> <metadata_sectors>
+ */
+ r = sscanf(params, "%" PRIu64 "/%" PRIu64 " %" PRIu64,
+ §ors_allocated, &total_sectors, &metadata_sectors);
+ if (r == 2 || r == 3) {
+ *total_numerator += sectors_allocated;
+ *total_denominator += total_sectors;
+ if (r == 3 && sectors_allocated == metadata_sectors)
+ *percent = PERCENT_0;
+ else if (sectors_allocated == total_sectors)
+ *percent = PERCENT_100;
+ else
+ *percent = make_percent(*total_numerator, *total_denominator);
+ } else if (!strcmp(params, "Invalid") ||
+ !strcmp(params, "Merge failed"))
+ *percent = PERCENT_INVALID;
+ else
+ return 0;
+
+ return 1;
+}
+
+static int _snap_target_present(struct cmd_context *cmd,
+ const struct lv_segment *seg,
+ unsigned *attributes __attribute__((unused)))
+{
+ static int _snap_checked = 0;
+ static int _snap_merge_checked = 0;
+ static int _snap_present = 0;
+ static int _snap_merge_present = 0;
+
+ if (!_snap_checked) {
+ _snap_present = target_present(cmd, "snapshot", 1) &&
+ target_present(cmd, "snapshot-origin", 0);
+ _snap_checked = 1;
+ }
+
+ if (!_snap_merge_checked && seg && (seg->status & MERGING)) {
+ _snap_merge_present = target_present(cmd, "snapshot-merge", 0);
+ _snap_merge_checked = 1;
+ return _snap_present && _snap_merge_present;
+ }
+
+ return _snap_present;
+}
+
+#ifdef DMEVENTD
+
+static const char *_get_snapshot_dso_path(struct cmd_context *cmd)
+{
+ return get_monitor_dso_path(cmd, find_config_tree_str(cmd, "dmeventd/snapshot_library",
+ DEFAULT_DMEVENTD_SNAPSHOT_LIB));
+}
+
+/* FIXME Cache this */
+static int _target_registered(struct lv_segment *seg, int *pending)
+{
+ return target_registered_with_dmeventd(seg->lv->vg->cmd, _get_snapshot_dso_path(seg->lv->vg->cmd),
+ seg->cow, pending);
+}
+
+/* FIXME This gets run while suspended and performs banned operations. */
+static int _target_set_events(struct lv_segment *seg, int evmask, int set)
+{
+ /* FIXME Make timeout (10) configurable */
+ return target_register_events(seg->lv->vg->cmd, _get_snapshot_dso_path(seg->lv->vg->cmd),
+ seg->cow, evmask, set, 10);
+}
+
+static int _target_register_events(struct lv_segment *seg,
+ int events)
+{
+ return _target_set_events(seg, events, 1);
+}
+
+static int _target_unregister_events(struct lv_segment *seg,
+ int events)
+{
+ return _target_set_events(seg, events, 0);
+}
+
+#endif /* DMEVENTD */
+#endif
+
+static int _snap_modules_needed(struct dm_pool *mem,
+ const struct lv_segment *seg __attribute__((unused)),
+ struct dm_list *modules)
+{
+ if (!str_list_add(mem, modules, "snapshot")) {
+ log_error("snapshot string list allocation failed");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void _snap_destroy(struct segment_type *segtype)
+{
+ dm_free(segtype);
+}
+
+static struct segtype_handler _snapshot_ops = {
+ .name = _snap_name,
+ .target_name = _snap_target_name,
+ .text_import = _snap_text_import,
+ .text_export = _snap_text_export,
+ .target_status_compatible = _snap_target_status_compatible,
+#ifdef DEVMAPPER_SUPPORT
+ .target_percent = _snap_target_percent,
+ .target_present = _snap_target_present,
+#ifdef DMEVENTD
+ .target_monitored = _target_registered,
+ .target_monitor_events = _target_register_events,
+ .target_unmonitor_events = _target_unregister_events,
+#endif
+#endif
+ .modules_needed = _snap_modules_needed,
+ .destroy = _snap_destroy,
+};
+
+#ifdef SNAPSHOT_INTERNAL
+struct segment_type *init_snapshot_segtype(struct cmd_context *cmd)
+#else /* Shared */
+struct segment_type *init_segtype(struct cmd_context *cmd);
+struct segment_type *init_segtype(struct cmd_context *cmd)
+#endif
+{
+ struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+ if (!segtype)
+ return_NULL;
+
+ segtype->cmd = cmd;
+ segtype->ops = &_snapshot_ops;
+ segtype->name = "snapshot";
+ segtype->private = NULL;
+ segtype->flags = SEG_SNAPSHOT;
+
+#ifdef DMEVENTD
+ if (_get_snapshot_dso_path(cmd))
+ segtype->flags |= SEG_MONITORED;
+#endif
+ log_very_verbose("Initialised segtype: %s", segtype->name);
+
+ return segtype;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "display.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "str_list.h"
+#include "targets.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "pv_alloc.h"
+#include "metadata.h"
+
+static const char *_striped_name(const struct lv_segment *seg)
+{
+ return (seg->area_count == 1) ? "linear" : seg->segtype->name;
+}
+
+static void _striped_display(const struct lv_segment *seg)
+{
+ uint32_t s;
+
+ if (seg->area_count == 1)
+ display_stripe(seg, 0, " ");
+ else {
+ log_print(" Stripes\t\t%u", seg->area_count);
+
+ if (seg->lv->vg->cmd->si_unit_consistency)
+ log_print(" Stripe size\t\t%s",
+ display_size(seg->lv->vg->cmd,
+ (uint64_t) seg->stripe_size));
+ else
+ log_print(" Stripe size\t\t%u KB",
+ seg->stripe_size / 2);
+
+ for (s = 0; s < seg->area_count; s++) {
+ log_print(" Stripe %d:", s);
+ display_stripe(seg, s, " ");
+ }
+ }
+ log_print(" ");
+}
+
+static int _striped_text_import_area_count(const struct config_node *sn, uint32_t *area_count)
+{
+ if (!get_config_uint32(sn, "stripe_count", area_count)) {
+ log_error("Couldn't read 'stripe_count' for "
+ "segment '%s'.", config_parent_name(sn));
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _striped_text_import(struct lv_segment *seg, const struct config_node *sn,
+ struct dm_hash_table *pv_hash)
+{
+ const struct config_node *cn;
+
+ if ((seg->area_count != 1) &&
+ !get_config_uint32(sn, "stripe_size", &seg->stripe_size)) {
+ log_error("Couldn't read stripe_size for segment %s "
+ "of logical volume %s.", config_parent_name(sn), seg->lv->name);
+ return 0;
+ }
+
+ if (!(cn = find_config_node(sn, "stripes"))) {
+ log_error("Couldn't find stripes array for segment %s "
+ "of logical volume %s.", config_parent_name(sn), seg->lv->name);
+ return 0;
+ }
+
+ seg->area_len /= seg->area_count;
+
+ return text_import_areas(seg, sn, cn, pv_hash, 0);
+}
+
+static int _striped_text_export(const struct lv_segment *seg, struct formatter *f)
+{
+
+ outf(f, "stripe_count = %u%s", seg->area_count,
+ (seg->area_count == 1) ? "\t# linear" : "");
+
+ if (seg->area_count > 1)
+ outsize(f, (uint64_t) seg->stripe_size,
+ "stripe_size = %u", seg->stripe_size);
+
+ return out_areas(f, seg, "stripe");
+}
+
+/*
+ * Test whether two segments could be merged by the current merging code
+ */
+static int _striped_segments_compatible(struct lv_segment *first,
+ struct lv_segment *second)
+{
+ uint32_t width;
+ unsigned s;
+
+ if ((first->area_count != second->area_count) ||
+ (first->stripe_size != second->stripe_size))
+ return 0;
+
+ for (s = 0; s < first->area_count; s++) {
+
+ /* FIXME Relax this to first area type != second area type */
+ /* plus the additional AREA_LV checks needed */
+ if ((seg_type(first, s) != AREA_PV) ||
+ (seg_type(second, s) != AREA_PV))
+ return 0;
+
+ width = first->area_len;
+
+ if ((seg_pv(first, s) !=
+ seg_pv(second, s)) ||
+ (seg_pe(first, s) + width !=
+ seg_pe(second, s)))
+ return 0;
+ }
+
+ if (!str_list_lists_equal(&first->tags, &second->tags))
+ return 0;
+
+ return 1;
+}
+
+static int _striped_merge_segments(struct lv_segment *seg1, struct lv_segment *seg2)
+{
+ uint32_t s;
+
+ if (!_striped_segments_compatible(seg1, seg2))
+ return 0;
+
+ seg1->len += seg2->len;
+ seg1->area_len += seg2->area_len;
+
+ for (s = 0; s < seg1->area_count; s++)
+ if (seg_type(seg1, s) == AREA_PV)
+ merge_pv_segments(seg_pvseg(seg1, s),
+ seg_pvseg(seg2, s));
+
+ return 1;
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static int _striped_add_target_line(struct dev_manager *dm,
+ struct dm_pool *mem __attribute__((unused)),
+ struct cmd_context *cmd __attribute__((unused)),
+ void **target_state __attribute__((unused)),
+ struct lv_segment *seg,
+ struct dm_tree_node *node, uint64_t len,
+ uint32_t *pvmove_mirror_count __attribute__((unused)))
+{
+ if (!seg->area_count) {
+ log_error(INTERNAL_ERROR "striped add_target_line called "
+ "with no areas for %s.", seg->lv->name);
+ return 0;
+ }
+ if (seg->area_count == 1) {
+ if (!dm_tree_node_add_linear_target(node, len))
+ return_0;
+ } else if (!dm_tree_node_add_striped_target(node, len,
+ seg->stripe_size))
+ return_0;
+
+ return add_areas_line(dm, seg, node, 0u, seg->area_count);
+}
+
+static int _striped_target_present(struct cmd_context *cmd,
+ const struct lv_segment *seg __attribute__((unused)),
+ unsigned *attributes __attribute__((unused)))
+{
+ static int _striped_checked = 0;
+ static int _striped_present = 0;
+
+ if (!_striped_checked)
+ _striped_present = target_present(cmd, "linear", 0) &&
+ target_present(cmd, "striped", 0);
+
+ _striped_checked = 1;
+
+ return _striped_present;
+}
+#endif
+
+static void _striped_destroy(struct segment_type *segtype)
+{
+ dm_free(segtype);
+}
+
+static struct segtype_handler _striped_ops = {
+ .name = _striped_name,
+ .display = _striped_display,
+ .text_import_area_count = _striped_text_import_area_count,
+ .text_import = _striped_text_import,
+ .text_export = _striped_text_export,
+ .merge_segments = _striped_merge_segments,
+#ifdef DEVMAPPER_SUPPORT
+ .add_target_line = _striped_add_target_line,
+ .target_present = _striped_target_present,
+#endif
+ .destroy = _striped_destroy,
+};
+
+struct segment_type *init_striped_segtype(struct cmd_context *cmd)
+{
+ struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+ if (!segtype)
+ return_NULL;
+
+ segtype->cmd = cmd;
+ segtype->ops = &_striped_ops;
+ segtype->name = "striped";
+ segtype->private = NULL;
+ segtype->flags =
+ SEG_CAN_SPLIT | SEG_AREAS_STRIPED | SEG_FORMAT1_SUPPORT;
+
+ log_very_verbose("Initialised segtype: %s", segtype->name);
+
+ return segtype;
+}
--- /dev/null
+/*
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "display.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "str_list.h"
+#include "targets.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "str_list.h"
+#include "metadata.h"
+
+static const char *_unknown_name(const struct lv_segment *seg)
+{
+
+ return seg->segtype->name;
+}
+
+static int _unknown_text_import(struct lv_segment *seg, const struct config_node *sn,
+ struct dm_hash_table *pv_hash)
+{
+ struct config_node *new, *last = NULL, *head = NULL;
+ const struct config_node *current;
+ log_verbose("importing unknown segment");
+ for (current = sn; current != NULL; current = current->sib) {
+ if (!strcmp(current->key, "type") || !strcmp(current->key, "start_extent") ||
+ !strcmp(current->key, "tags") || !strcmp(current->key, "extent_count"))
+ continue;
+ new = clone_config_node(seg->lv->vg->vgmem, current, 0);
+ if (!new)
+ return_0;
+ if (last)
+ last->sib = new;
+ if (!head)
+ head = new;
+ last = new;
+ }
+ seg->segtype_private = head;
+ return 1;
+}
+
+static int _unknown_text_export(const struct lv_segment *seg, struct formatter *f)
+{
+ struct config_node *cn = seg->segtype_private;
+ return out_config_node(f, cn);
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static int _unknown_add_target_line(struct dev_manager *dm __attribute__((unused)),
+ struct dm_pool *mem __attribute__((unused)),
+ struct cmd_context *cmd __attribute__((unused)),
+ void **target_state __attribute__((unused)),
+ struct lv_segment *seg __attribute__((unused)),
+ struct dm_tree_node *node, uint64_t len,
+ uint32_t *pvmove_mirror_count __attribute__((unused)))
+{
+ return dm_tree_node_add_error_target(node, len);
+}
+#endif
+
+static void _unknown_destroy(struct segment_type *segtype)
+{
+ dm_free(segtype);
+}
+
+static struct segtype_handler _unknown_ops = {
+ .name = _unknown_name,
+ .text_import = _unknown_text_import,
+ .text_export = _unknown_text_export,
+#ifdef DEVMAPPER_SUPPORT
+ .add_target_line = _unknown_add_target_line,
+#endif
+ .destroy = _unknown_destroy,
+};
+
+struct segment_type *init_unknown_segtype(struct cmd_context *cmd, const char *name)
+{
+ struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+ if (!segtype)
+ return_NULL;
+
+ segtype->cmd = cmd;
+ segtype->ops = &_unknown_ops;
+ segtype->name = dm_pool_strdup(cmd->mem, name);
+ segtype->private = NULL;
+ segtype->flags = SEG_UNKNOWN | SEG_VIRTUAL | SEG_CANNOT_BE_ZEROED;
+
+ log_very_verbose("Initialised segtype: %s", segtype->name);
+
+ return segtype;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "uuid.h"
+#include "lvm-wrappers.h"
+
+#include <assert.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+
+static const char _c[] =
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#";
+
+static int _built_inverse;
+static char _inverse_c[256];
+
+int lvid_create(union lvid *lvid, struct id *vgid)
+{
+ memcpy(lvid->id, vgid, sizeof(*lvid->id));
+ return id_create(&lvid->id[1]);
+}
+
+void uuid_from_num(char *uuid, uint32_t num)
+{
+ unsigned i;
+
+ for (i = ID_LEN; i; i--) {
+ uuid[i - 1] = _c[num % (sizeof(_c) - 1)];
+ num /= sizeof(_c) - 1;
+ }
+}
+
+int lvid_from_lvnum(union lvid *lvid, struct id *vgid, uint32_t lv_num)
+{
+ int i;
+
+ memcpy(lvid->id, vgid, sizeof(*lvid->id));
+
+ for (i = ID_LEN; i; i--) {
+ lvid->id[1].uuid[i - 1] = _c[lv_num % (sizeof(_c) - 1)];
+ lv_num /= sizeof(_c) - 1;
+ }
+
+ lvid->s[sizeof(lvid->s) - 1] = '\0';
+
+ return 1;
+}
+
+int lvnum_from_lvid(union lvid *lvid)
+{
+ int i, lv_num = 0;
+ char *c;
+
+ for (i = 0; i < ID_LEN; i++) {
+ lv_num *= sizeof(_c) - 1;
+ if ((c = strchr(_c, lvid->id[1].uuid[i])))
+ lv_num += (int) (c - _c);
+ if (lv_num < 0)
+ lv_num = 0;
+ }
+
+ return lv_num;
+}
+
+int lvid_in_restricted_range(union lvid *lvid)
+{
+ int i;
+
+ for (i = 0; i < ID_LEN - 3; i++)
+ if (lvid->id[1].uuid[i] != '0')
+ return 0;
+
+ for (i = ID_LEN - 3; i < ID_LEN; i++)
+ if (!isdigit(lvid->id[1].uuid[i]))
+ return 0;
+
+ return 1;
+}
+
+
+int id_create(struct id *id)
+{
+ unsigned i;
+ size_t len = sizeof(id->uuid);
+
+ memset(id->uuid, 0, len);
+ if (!read_urandom(&id->uuid, len)) {
+ return 0;
+ }
+
+ /*
+ * Skip out the last 2 chars in randomized creation for LVM1
+ * backwards compatibility.
+ */
+ for (i = 0; i < len; i++)
+ id->uuid[i] = _c[id->uuid[i] % (sizeof(_c) - 3)];
+
+ return 1;
+}
+
+/*
+ * The only validity check we have is that
+ * the uuid just contains characters from
+ * '_c'. A checksum would have been nice :(
+ */
+static void _build_inverse(void)
+{
+ const char *ptr;
+
+ if (_built_inverse)
+ return;
+
+ memset(_inverse_c, 0, sizeof(_inverse_c));
+
+ for (ptr = _c; *ptr; ptr++)
+ _inverse_c[(int) *ptr] = (char) 0x1;
+}
+
+int id_valid(struct id *id)
+{
+ int i;
+
+ _build_inverse();
+
+ for (i = 0; i < ID_LEN; i++)
+ if (!_inverse_c[id->uuid[i]]) {
+ log_error("UUID contains invalid character");
+ return 0;
+ }
+
+ return 1;
+}
+
+int id_equal(const struct id *lhs, const struct id *rhs)
+{
+ return !memcmp(lhs->uuid, rhs->uuid, sizeof(lhs->uuid));
+}
+
+#define GROUPS (ID_LEN / 4)
+
+int id_write_format(const struct id *id, char *buffer, size_t size)
+{
+ int i, tot;
+
+ static unsigned group_size[] = { 6, 4, 4, 4, 4, 4, 6 };
+
+ assert(ID_LEN == 32);
+
+ /* split into groups separated by dashes */
+ if (size < (32 + 6 + 1)) {
+ log_error("Couldn't write uuid, buffer too small.");
+ return 0;
+ }
+
+ for (i = 0, tot = 0; i < 7; i++) {
+ memcpy(buffer, id->uuid + tot, group_size[i]);
+ buffer += group_size[i];
+ tot += group_size[i];
+ *buffer++ = '-';
+ }
+
+ *--buffer = '\0';
+ return 1;
+}
+
+int id_read_format(struct id *id, const char *buffer)
+{
+ int out = 0;
+
+ /* just strip out any dashes */
+ while (*buffer) {
+
+ if (*buffer == '-') {
+ buffer++;
+ continue;
+ }
+
+ if (out >= ID_LEN) {
+ log_error("Too many characters to be uuid.");
+ return 0;
+ }
+
+ id->uuid[out++] = *buffer++;
+ }
+
+ if (out != ID_LEN) {
+ log_error("Couldn't read uuid: incorrect number of "
+ "characters.");
+ return 0;
+ }
+
+ return id_valid(id);
+}
+
+char *id_format_and_copy(struct dm_pool *mem, const struct id *id)
+{
+ char *repstr = NULL;
+
+ if (!(repstr = dm_pool_alloc(mem, 40))) {
+ log_error("dm_pool_alloc failed");
+ return NULL;
+ }
+
+ if (!id_write_format(id, repstr, 40))
+ return_NULL;
+
+ return repstr;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_UUID_H
+#define _LVM_UUID_H
+
+#define ID_LEN 32
+#define ID_LEN_S "32"
+
+struct id {
+ int8_t uuid[ID_LEN];
+};
+
+/*
+ * Unique logical volume identifier
+ * With format1 this is VG uuid + LV uuid + '\0' + padding
+ */
+union lvid {
+ struct id id[2];
+ char s[2 * sizeof(struct id) + 1 + 7];
+};
+
+int lvid_from_lvnum(union lvid *lvid, struct id *vgid, uint32_t lv_num);
+int lvnum_from_lvid(union lvid *lvid);
+int lvid_in_restricted_range(union lvid *lvid);
+
+void uuid_from_num(char *uuid, uint32_t num);
+
+int lvid_create(union lvid *lvid, struct id *vgid);
+int id_create(struct id *id);
+int id_valid(struct id *id);
+int id_equal(const struct id *lhs, const struct id *rhs);
+
+/*
+ * Fills 'buffer' with a more human readable form
+ * of the uuid.
+ */
+int id_write_format(const struct id *id, char *buffer, size_t size);
+
+/*
+ * Reads a formatted uuid.
+ */
+int id_read_format(struct id *id, const char *buffer);
+
+char *id_format_and_copy(struct dm_pool *mem, const struct id *id);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "segtype.h"
+#include "display.h"
+#include "text_export.h"
+#include "text_import.h"
+#include "config.h"
+#include "str_list.h"
+#include "targets.h"
+#include "lvm-string.h"
+#include "activate.h"
+#include "metadata.h"
+
+static const char *_zero_name(const struct lv_segment *seg)
+{
+ return seg->segtype->name;
+}
+
+static int _zero_merge_segments(struct lv_segment *seg1, struct lv_segment *seg2)
+{
+ seg1->len += seg2->len;
+ seg1->area_len += seg2->area_len;
+
+ return 1;
+}
+
+#ifdef DEVMAPPER_SUPPORT
+static int _zero_add_target_line(struct dev_manager *dm __attribute__((unused)),
+ struct dm_pool *mem __attribute__((unused)),
+ struct cmd_context *cmd __attribute__((unused)),
+ void **target_state __attribute__((unused)),
+ struct lv_segment *seg __attribute__((unused)),
+ struct dm_tree_node *node,uint64_t len,
+ uint32_t *pvmove_mirror_count __attribute__((unused)))
+{
+ return dm_tree_node_add_zero_target(node, len);
+}
+
+static int _zero_target_present(struct cmd_context *cmd,
+ const struct lv_segment *seg __attribute__((unused)),
+ unsigned *attributes __attribute__((unused)))
+{
+ static int _zero_checked = 0;
+ static int _zero_present = 0;
+
+ if (!_zero_checked)
+ _zero_present = target_present(cmd, "zero", 1);
+
+ _zero_checked = 1;
+
+ return _zero_present;
+}
+#endif
+
+static int _zero_modules_needed(struct dm_pool *mem,
+ const struct lv_segment *seg __attribute__((unused)),
+ struct dm_list *modules)
+{
+ if (!str_list_add(mem, modules, "zero")) {
+ log_error("zero module string list allocation failed");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void _zero_destroy(struct segment_type *segtype)
+{
+ dm_free(segtype);
+}
+
+static struct segtype_handler _zero_ops = {
+ .name = _zero_name,
+ .merge_segments = _zero_merge_segments,
+#ifdef DEVMAPPER_SUPPORT
+ .add_target_line = _zero_add_target_line,
+ .target_present = _zero_target_present,
+#endif
+ .modules_needed = _zero_modules_needed,
+ .destroy = _zero_destroy,
+};
+
+struct segment_type *init_zero_segtype(struct cmd_context *cmd)
+{
+ struct segment_type *segtype = dm_malloc(sizeof(*segtype));
+
+ if (!segtype)
+ return_NULL;
+
+ segtype->cmd = cmd;
+ segtype->ops = &_zero_ops;
+ segtype->name = "zero";
+ segtype->private = NULL;
+ segtype->flags = SEG_CAN_SPLIT | SEG_VIRTUAL | SEG_CANNOT_BE_ZEROED;
+
+ log_very_verbose("Initialised segtype: %s", segtype->name);
+
+ return segtype;
+}
--- /dev/null
+dm_log
+dm_log_with_errno
--- /dev/null
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES =\
+ datastruct/bitset.c \
+ datastruct/hash.c \
+ datastruct/list.c \
+ libdm-common.c \
+ libdm-file.c \
+ libdm-deptree.c \
+ libdm-string.c \
+ libdm-report.c \
+ mm/dbg_malloc.c \
+ mm/pool.c \
+ regex/matcher.c \
+ regex/parse_rx.c \
+ regex/ttree.c \
+ $(interface)/libdm-iface.c
+
+INCLUDES = -I$(srcdir)/$(interface) -I$(srcdir)
+
+ifeq ("@STATIC_LINK@", "yes")
+LIB_STATIC = $(interface)/libdevmapper.a
+endif
+
+LIB_SHARED = $(interface)/libdevmapper.$(LIB_SUFFIX)
+LIB_VERSION = $(LIB_VERSION_DM)
+TARGETS += libdevmapper.$(LIB_SUFFIX) libdevmapper.$(LIB_SUFFIX).$(LIB_VERSION)
+
+CFLOW_LIST = $(SOURCES)
+CFLOW_LIST_TARGET = libdevmapper.cflow
+
+EXPORTED_HEADER = $(srcdir)/libdevmapper.h
+EXPORTED_FN_PREFIX = dm
+
+include $(top_builddir)/make.tmpl
+
+DEFS += -DDM_DEVICE_UID=@DM_DEVICE_UID@ -DDM_DEVICE_GID=@DM_DEVICE_GID@ \
+ -DDM_DEVICE_MODE=@DM_DEVICE_MODE@
+
+LIBS += $(SELINUX_LIBS) $(UDEV_LIBS)
+
+device-mapper: all
+
+libdevmapper.$(LIB_SUFFIX) libdevmapper.$(LIB_SUFFIX).$(LIB_VERSION): $(LIB_SHARED)
+ $(LN_S) -f $< $@
+
+.PHONY: install_dynamic install_static install_include \
+ install_ioctl install_ioctl_static \
+ install_pkgconfig
+
+INSTALL_TYPE = install_dynamic
+
+ifeq ("@STATIC_LINK@", "yes")
+ INSTALL_TYPE += install_static
+endif
+
+ifeq ("@PKGCONFIG@", "yes")
+ INSTALL_TYPE += install_pkgconfig
+endif
+
+install: $(INSTALL_TYPE) install_include
+
+install_device-mapper: install
+
+install_include: $(srcdir)/libdevmapper.h
+ $(INSTALL_DATA) -D $< $(includedir)/$(<F)
+
+install_dynamic: install_@interface@
+
+install_static: install_@interface@_static
+
+install_ioctl: install_lib_shared
+
+install_pkgconfig: libdevmapper.pc
+ $(INSTALL_DATA) -D $< $(pkgconfigdir)/devmapper.pc
+
+install_ioctl_static: $(LIB_STATIC)
+ $(INSTALL_DATA) -D $< $(usrlibdir)/$(<F)
+
+CLEAN_TARGETS += ioctl/libdevmapper.a
+DISTCLEAN_TARGETS += libdevmapper.pc .exported_symbols_generated
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+
+/* FIXME: calculate this. */
+#define INT_SHIFT 5
+
+dm_bitset_t dm_bitset_create(struct dm_pool *mem, unsigned num_bits)
+{
+ unsigned n = (num_bits / DM_BITS_PER_INT) + 2;
+ size_t size = sizeof(int) * n;
+ dm_bitset_t bs;
+
+ if (mem)
+ bs = dm_pool_zalloc(mem, size);
+ else
+ bs = dm_zalloc(size);
+
+ if (!bs)
+ return NULL;
+
+ *bs = num_bits;
+
+ return bs;
+}
+
+void dm_bitset_destroy(dm_bitset_t bs)
+{
+ dm_free(bs);
+}
+
+int dm_bitset_equal(dm_bitset_t in1, dm_bitset_t in2)
+{
+ int i;
+
+ for (i = (in1[0] / DM_BITS_PER_INT) + 1; i; i--)
+ if (in1[i] != in2[i])
+ return 0;
+
+ return 1;
+}
+
+void dm_bit_and(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2)
+{
+ int i;
+
+ for (i = (in1[0] / DM_BITS_PER_INT) + 1; i; i--)
+ out[i] = in1[i] & in2[i];
+}
+void dm_bit_union(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2)
+{
+ int i;
+ for (i = (in1[0] / DM_BITS_PER_INT) + 1; i; i--)
+ out[i] = in1[i] | in2[i];
+}
+
+static int _test_word(uint32_t test, int bit)
+{
+ uint32_t tb = test >> bit;
+
+ return (tb ? ffs(tb) + bit - 1 : -1);
+}
+
+int dm_bit_get_next(dm_bitset_t bs, int last_bit)
+{
+ int bit, word;
+ uint32_t test;
+
+ last_bit++; /* otherwise we'll return the same bit again */
+
+ /*
+ * bs[0] holds number of bits
+ */
+ while (last_bit < (int) bs[0]) {
+ word = last_bit >> INT_SHIFT;
+ test = bs[word + 1];
+ bit = last_bit & (DM_BITS_PER_INT - 1);
+
+ if ((bit = _test_word(test, bit)) >= 0)
+ return (word * DM_BITS_PER_INT) + bit;
+
+ last_bit = last_bit - (last_bit & (DM_BITS_PER_INT - 1)) +
+ DM_BITS_PER_INT;
+ }
+
+ return -1;
+}
+
+int dm_bit_get_first(dm_bitset_t bs)
+{
+ return dm_bit_get_next(bs, -1);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+
+struct dm_hash_node {
+ struct dm_hash_node *next;
+ void *data;
+ unsigned keylen;
+ char key[0];
+};
+
+struct dm_hash_table {
+ unsigned num_nodes;
+ unsigned num_slots;
+ struct dm_hash_node **slots;
+};
+
+/* Permutation of the Integers 0 through 255 */
+static unsigned char _nums[] = {
+ 1, 14, 110, 25, 97, 174, 132, 119, 138, 170, 125, 118, 27, 233, 140, 51,
+ 87, 197, 177, 107, 234, 169, 56, 68, 30, 7, 173, 73, 188, 40, 36, 65,
+ 49, 213, 104, 190, 57, 211, 148, 223, 48, 115, 15, 2, 67, 186, 210, 28,
+ 12, 181, 103, 70, 22, 58, 75, 78, 183, 167, 238, 157, 124, 147, 172,
+ 144,
+ 176, 161, 141, 86, 60, 66, 128, 83, 156, 241, 79, 46, 168, 198, 41, 254,
+ 178, 85, 253, 237, 250, 154, 133, 88, 35, 206, 95, 116, 252, 192, 54,
+ 221,
+ 102, 218, 255, 240, 82, 106, 158, 201, 61, 3, 89, 9, 42, 155, 159, 93,
+ 166, 80, 50, 34, 175, 195, 100, 99, 26, 150, 16, 145, 4, 33, 8, 189,
+ 121, 64, 77, 72, 208, 245, 130, 122, 143, 55, 105, 134, 29, 164, 185,
+ 194,
+ 193, 239, 101, 242, 5, 171, 126, 11, 74, 59, 137, 228, 108, 191, 232,
+ 139,
+ 6, 24, 81, 20, 127, 17, 91, 92, 251, 151, 225, 207, 21, 98, 113, 112,
+ 84, 226, 18, 214, 199, 187, 13, 32, 94, 220, 224, 212, 247, 204, 196,
+ 43,
+ 249, 236, 45, 244, 111, 182, 153, 136, 129, 90, 217, 202, 19, 165, 231,
+ 71,
+ 230, 142, 96, 227, 62, 179, 246, 114, 162, 53, 160, 215, 205, 180, 47,
+ 109,
+ 44, 38, 31, 149, 135, 0, 216, 52, 63, 23, 37, 69, 39, 117, 146, 184,
+ 163, 200, 222, 235, 248, 243, 219, 10, 152, 131, 123, 229, 203, 76, 120,
+ 209
+};
+
+static struct dm_hash_node *_create_node(const char *str, unsigned len)
+{
+ struct dm_hash_node *n = dm_malloc(sizeof(*n) + len);
+
+ if (n) {
+ memcpy(n->key, str, len);
+ n->keylen = len;
+ }
+
+ return n;
+}
+
+static unsigned long _hash(const char *str, unsigned len)
+{
+ unsigned long h = 0, g;
+ unsigned i;
+
+ for (i = 0; i < len; i++) {
+ h <<= 4;
+ h += _nums[(unsigned char) *str++];
+ g = h & ((unsigned long) 0xf << 16u);
+ if (g) {
+ h ^= g >> 16u;
+ h ^= g >> 5u;
+ }
+ }
+
+ return h;
+}
+
+struct dm_hash_table *dm_hash_create(unsigned size_hint)
+{
+ size_t len;
+ unsigned new_size = 16u;
+ struct dm_hash_table *hc = dm_zalloc(sizeof(*hc));
+
+ if (!hc)
+ return_0;
+
+ /* round size hint up to a power of two */
+ while (new_size < size_hint)
+ new_size = new_size << 1;
+
+ hc->num_slots = new_size;
+ len = sizeof(*(hc->slots)) * new_size;
+ if (!(hc->slots = dm_malloc(len))) {
+ stack;
+ goto bad;
+ }
+ memset(hc->slots, 0, len);
+ return hc;
+
+ bad:
+ dm_free(hc->slots);
+ dm_free(hc);
+ return 0;
+}
+
+static void _free_nodes(struct dm_hash_table *t)
+{
+ struct dm_hash_node *c, *n;
+ unsigned i;
+
+ for (i = 0; i < t->num_slots; i++)
+ for (c = t->slots[i]; c; c = n) {
+ n = c->next;
+ dm_free(c);
+ }
+}
+
+void dm_hash_destroy(struct dm_hash_table *t)
+{
+ _free_nodes(t);
+ dm_free(t->slots);
+ dm_free(t);
+}
+
+static struct dm_hash_node **_find(struct dm_hash_table *t, const char *key,
+ uint32_t len)
+{
+ unsigned h = _hash(key, len) & (t->num_slots - 1);
+ struct dm_hash_node **c;
+
+ for (c = &t->slots[h]; *c; c = &((*c)->next)) {
+ if ((*c)->keylen != len)
+ continue;
+
+ if (!memcmp(key, (*c)->key, len))
+ break;
+ }
+
+ return c;
+}
+
+void *dm_hash_lookup_binary(struct dm_hash_table *t, const char *key,
+ uint32_t len)
+{
+ struct dm_hash_node **c = _find(t, key, len);
+
+ return *c ? (*c)->data : 0;
+}
+
+int dm_hash_insert_binary(struct dm_hash_table *t, const char *key,
+ uint32_t len, void *data)
+{
+ struct dm_hash_node **c = _find(t, key, len);
+
+ if (*c)
+ (*c)->data = data;
+ else {
+ struct dm_hash_node *n = _create_node(key, len);
+
+ if (!n)
+ return 0;
+
+ n->data = data;
+ n->next = 0;
+ *c = n;
+ t->num_nodes++;
+ }
+
+ return 1;
+}
+
+void dm_hash_remove_binary(struct dm_hash_table *t, const char *key,
+ uint32_t len)
+{
+ struct dm_hash_node **c = _find(t, key, len);
+
+ if (*c) {
+ struct dm_hash_node *old = *c;
+ *c = (*c)->next;
+ dm_free(old);
+ t->num_nodes--;
+ }
+}
+
+void *dm_hash_lookup(struct dm_hash_table *t, const char *key)
+{
+ return dm_hash_lookup_binary(t, key, strlen(key) + 1);
+}
+
+int dm_hash_insert(struct dm_hash_table *t, const char *key, void *data)
+{
+ return dm_hash_insert_binary(t, key, strlen(key) + 1, data);
+}
+
+void dm_hash_remove(struct dm_hash_table *t, const char *key)
+{
+ dm_hash_remove_binary(t, key, strlen(key) + 1);
+}
+
+unsigned dm_hash_get_num_entries(struct dm_hash_table *t)
+{
+ return t->num_nodes;
+}
+
+void dm_hash_iter(struct dm_hash_table *t, dm_hash_iterate_fn f)
+{
+ struct dm_hash_node *c, *n;
+ unsigned i;
+
+ for (i = 0; i < t->num_slots; i++)
+ for (c = t->slots[i]; c; c = n) {
+ n = c->next;
+ f(c->data);
+ }
+}
+
+void dm_hash_wipe(struct dm_hash_table *t)
+{
+ _free_nodes(t);
+ memset(t->slots, 0, sizeof(struct dm_hash_node *) * t->num_slots);
+ t->num_nodes = 0u;
+}
+
+char *dm_hash_get_key(struct dm_hash_table *t __attribute__((unused)),
+ struct dm_hash_node *n)
+{
+ return n->key;
+}
+
+void *dm_hash_get_data(struct dm_hash_table *t __attribute__((unused)),
+ struct dm_hash_node *n)
+{
+ return n->data;
+}
+
+static struct dm_hash_node *_next_slot(struct dm_hash_table *t, unsigned s)
+{
+ struct dm_hash_node *c = NULL;
+ unsigned i;
+
+ for (i = s; i < t->num_slots && !c; i++)
+ c = t->slots[i];
+
+ return c;
+}
+
+struct dm_hash_node *dm_hash_get_first(struct dm_hash_table *t)
+{
+ return _next_slot(t, 0);
+}
+
+struct dm_hash_node *dm_hash_get_next(struct dm_hash_table *t, struct dm_hash_node *n)
+{
+ unsigned h = _hash(n->key, n->keylen) & (t->num_slots - 1);
+
+ return n->next ? n->next : _next_slot(t, h + 1);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include <assert.h>
+
+/*
+ * Initialise a list before use.
+ * The list head's next and previous pointers point back to itself.
+ */
+void dm_list_init(struct dm_list *head)
+{
+ head->n = head->p = head;
+}
+
+/*
+ * Insert an element before 'head'.
+ * If 'head' is the list head, this adds an element to the end of the list.
+ */
+void dm_list_add(struct dm_list *head, struct dm_list *elem)
+{
+ assert(head->n);
+
+ elem->n = head;
+ elem->p = head->p;
+
+ head->p->n = elem;
+ head->p = elem;
+}
+
+/*
+ * Insert an element after 'head'.
+ * If 'head' is the list head, this adds an element to the front of the list.
+ */
+void dm_list_add_h(struct dm_list *head, struct dm_list *elem)
+{
+ assert(head->n);
+
+ elem->n = head->n;
+ elem->p = head;
+
+ head->n->p = elem;
+ head->n = elem;
+}
+
+/*
+ * Delete an element from its list.
+ * Note that this doesn't change the element itself - it may still be safe
+ * to follow its pointers.
+ */
+void dm_list_del(struct dm_list *elem)
+{
+ elem->n->p = elem->p;
+ elem->p->n = elem->n;
+}
+
+/*
+ * Remove an element from existing list and insert before 'head'.
+ */
+void dm_list_move(struct dm_list *head, struct dm_list *elem)
+{
+ dm_list_del(elem);
+ dm_list_add(head, elem);
+}
+
+/*
+ * Is the list empty?
+ */
+int dm_list_empty(const struct dm_list *head)
+{
+ return head->n == head;
+}
+
+/*
+ * Is this the first element of the list?
+ */
+int dm_list_start(const struct dm_list *head, const struct dm_list *elem)
+{
+ return elem->p == head;
+}
+
+/*
+ * Is this the last element of the list?
+ */
+int dm_list_end(const struct dm_list *head, const struct dm_list *elem)
+{
+ return elem->n == head;
+}
+
+/*
+ * Return first element of the list or NULL if empty
+ */
+struct dm_list *dm_list_first(const struct dm_list *head)
+{
+ return (dm_list_empty(head) ? NULL : head->n);
+}
+
+/*
+ * Return last element of the list or NULL if empty
+ */
+struct dm_list *dm_list_last(const struct dm_list *head)
+{
+ return (dm_list_empty(head) ? NULL : head->p);
+}
+
+/*
+ * Return the previous element of the list, or NULL if we've reached the start.
+ */
+struct dm_list *dm_list_prev(const struct dm_list *head, const struct dm_list *elem)
+{
+ return (dm_list_start(head, elem) ? NULL : elem->p);
+}
+
+/*
+ * Return the next element of the list, or NULL if we've reached the end.
+ */
+struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *elem)
+{
+ return (dm_list_end(head, elem) ? NULL : elem->n);
+}
+
+/*
+ * Return the number of elements in a list by walking it.
+ */
+unsigned int dm_list_size(const struct dm_list *head)
+{
+ unsigned int s = 0;
+ const struct dm_list *v;
+
+ dm_list_iterate(v, head)
+ s++;
+
+ return s;
+}
+
+/*
+ * Join two lists together.
+ * This moves all the elements of the list 'head1' to the end of the list
+ * 'head', leaving 'head1' empty.
+ */
+void dm_list_splice(struct dm_list *head, struct dm_list *head1)
+{
+ assert(head->n);
+ assert(head1->n);
+
+ if (dm_list_empty(head1))
+ return;
+
+ head1->p->n = head;
+ head1->n->p = head->p;
+
+ head->p->n = head1->n;
+ head->p = head1->p;
+
+ dm_list_init(head1);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LINUX_LIBDM_COMPAT_H
+#define _LINUX_LIBDM_COMPAT_H
+
+#include "kdev_t.h"
+#include "dm-ioctl.h"
+#include <inttypes.h>
+#include <sys/ioctl.h>
+
+struct dm_task;
+struct dm_info;
+
+/*
+ * Old versions of structures for backwards compatibility.
+ */
+
+struct dm_ioctl_v1 {
+ uint32_t version[3]; /* in/out */
+ uint32_t data_size; /* total size of data passed in
+ * including this struct */
+
+ uint32_t data_start; /* offset to start of data
+ * relative to start of this struct */
+
+ int32_t target_count; /* in/out */
+ int32_t open_count; /* out */
+ uint32_t flags; /* in/out */
+
+ __kernel_dev_t dev; /* in/out */
+
+ char name[DM_NAME_LEN]; /* device name */
+ char uuid[DM_UUID_LEN]; /* unique identifier for
+ * the block device */
+};
+
+struct dm_target_spec_v1 {
+ int32_t status; /* used when reading from kernel only */
+ uint64_t sector_start;
+ uint32_t length;
+ uint32_t next;
+
+ char target_type[DM_MAX_TYPE_NAME];
+
+};
+
+struct dm_target_deps_v1 {
+ uint32_t count;
+
+ __kernel_dev_t dev[0]; /* out */
+};
+
+enum {
+ /* Top level cmds */
+ DM_VERSION_CMD_V1 = 0,
+ DM_REMOVE_ALL_CMD_V1,
+
+ /* device level cmds */
+ DM_DEV_CREATE_CMD_V1,
+ DM_DEV_REMOVE_CMD_V1,
+ DM_DEV_RELOAD_CMD_V1,
+ DM_DEV_RENAME_CMD_V1,
+ DM_DEV_SUSPEND_CMD_V1,
+ DM_DEV_DEPS_CMD_V1,
+ DM_DEV_STATUS_CMD_V1,
+
+ /* target level cmds */
+ DM_TARGET_STATUS_CMD_V1,
+ DM_TARGET_WAIT_CMD_V1,
+};
+
+#define DM_VERSION_V1 _IOWR(DM_IOCTL, DM_VERSION_CMD_V1, struct dm_ioctl)
+#define DM_REMOVE_ALL_V1 _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD_V1, struct dm_ioctl)
+
+#define DM_DEV_CREATE_V1 _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD_V1, struct dm_ioctl)
+#define DM_DEV_REMOVE_V1 _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD_V1, struct dm_ioctl)
+#define DM_DEV_RELOAD_V1 _IOWR(DM_IOCTL, DM_DEV_RELOAD_CMD_V1, struct dm_ioctl)
+#define DM_DEV_SUSPEND_V1 _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD_V1, struct dm_ioctl)
+#define DM_DEV_RENAME_V1 _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD_V1, struct dm_ioctl)
+#define DM_DEV_DEPS_V1 _IOWR(DM_IOCTL, DM_DEV_DEPS_CMD_V1, struct dm_ioctl)
+#define DM_DEV_STATUS_V1 _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD_V1, struct dm_ioctl)
+
+#define DM_TARGET_STATUS_V1 _IOWR(DM_IOCTL, DM_TARGET_STATUS_CMD_V1, struct dm_ioctl)
+#define DM_TARGET_WAIT_V1 _IOWR(DM_IOCTL, DM_TARGET_WAIT_CMD_V1, struct dm_ioctl)
+
+/* *INDENT-OFF* */
+static struct cmd_data _cmd_data_v1[] = {
+ { "create", DM_DEV_CREATE_V1, {1, 0, 0} },
+ { "reload", DM_DEV_RELOAD_V1, {1, 0, 0} },
+ { "remove", DM_DEV_REMOVE_V1, {1, 0, 0} },
+ { "remove_all", DM_REMOVE_ALL_V1, {1, 0, 0} },
+ { "suspend", DM_DEV_SUSPEND_V1, {1, 0, 0} },
+ { "resume", DM_DEV_SUSPEND_V1, {1, 0, 0} },
+ { "info", DM_DEV_STATUS_V1, {1, 0, 0} },
+ { "deps", DM_DEV_DEPS_V1, {1, 0, 0} },
+ { "rename", DM_DEV_RENAME_V1, {1, 0, 0} },
+ { "version", DM_VERSION_V1, {1, 0, 0} },
+ { "status", DM_TARGET_STATUS_V1, {1, 0, 0} },
+ { "table", DM_TARGET_STATUS_V1, {1, 0, 0} },
+ { "waitevent", DM_TARGET_WAIT_V1, {1, 0, 0} },
+ { "names", 0, {4, 0, 0} },
+ { "clear", 0, {4, 0, 0} },
+ { "mknodes", 0, {4, 0, 0} },
+ { "versions", 0, {4, 1, 0} },
+ { "message", 0, {4, 2, 0} },
+ { "setgeometry",0, {4, 6, 0} },
+};
+/* *INDENT-ON* */
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+#include "libdm-targets.h"
+#include "libdm-common.h"
+
+#ifdef DM_COMPAT
+# include "libdm-compat.h"
+#endif
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <limits.h>
+
+#ifdef linux
+# include "kdev_t.h"
+# include <linux/limits.h>
+#else
+# define MAJOR(x) major((x))
+# define MINOR(x) minor((x))
+# define MKDEV(x,y) makedev((x),(y))
+#endif
+
+#include "dm-ioctl.h"
+
+/*
+ * Ensure build compatibility.
+ * The hard-coded versions here are the highest present
+ * in the _cmd_data arrays.
+ */
+
+#if !((DM_VERSION_MAJOR == 1 && DM_VERSION_MINOR >= 0) || \
+ (DM_VERSION_MAJOR == 4 && DM_VERSION_MINOR >= 0))
+#error The version of dm-ioctl.h included is incompatible.
+#endif
+
+/* FIXME This should be exported in device-mapper.h */
+#define DM_NAME "device-mapper"
+
+#define PROC_MISC "/proc/misc"
+#define PROC_DEVICES "/proc/devices"
+#define MISC_NAME "misc"
+
+#define NUMBER_OF_MAJORS 4096
+
+/*
+ * Static minor number assigned since kernel version 2.6.36.
+ * The original definition is in kernel's include/linux/miscdevice.h.
+ * This number is also visible in modules.devname exported by depmod
+ * utility (support included in module-init-tools version >= 3.12).
+ */
+#define MAPPER_CTRL_MINOR 236
+#define MISC_MAJOR 10
+
+/* dm major version no for running kernel */
+static unsigned _dm_version = DM_VERSION_MAJOR;
+static unsigned _dm_version_minor = 0;
+static unsigned _dm_version_patchlevel = 0;
+static int _log_suppress = 0;
+
+static int _kernel_major;
+static int _kernel_minor;
+static int _kernel_release;
+
+/*
+ * If the kernel dm driver only supports one major number
+ * we store it in _dm_device_major. Otherwise we indicate
+ * which major numbers have been claimed by device-mapper
+ * in _dm_bitset.
+ */
+static unsigned _dm_multiple_major_support = 1;
+static dm_bitset_t _dm_bitset = NULL;
+static uint32_t _dm_device_major = 0;
+
+static int _control_fd = -1;
+static int _version_checked = 0;
+static int _version_ok = 1;
+static unsigned _ioctl_buffer_double_factor = 0;
+
+/*
+ * Support both old and new major numbers to ease the transition.
+ * Clumsy, but only temporary.
+ */
+#if DM_VERSION_MAJOR == 4 && defined(DM_COMPAT)
+const int _dm_compat = 1;
+#else
+const int _dm_compat = 0;
+#endif
+
+
+/* *INDENT-OFF* */
+static struct cmd_data _cmd_data_v4[] = {
+ {"create", DM_DEV_CREATE, {4, 0, 0}},
+ {"reload", DM_TABLE_LOAD, {4, 0, 0}},
+ {"remove", DM_DEV_REMOVE, {4, 0, 0}},
+ {"remove_all", DM_REMOVE_ALL, {4, 0, 0}},
+ {"suspend", DM_DEV_SUSPEND, {4, 0, 0}},
+ {"resume", DM_DEV_SUSPEND, {4, 0, 0}},
+ {"info", DM_DEV_STATUS, {4, 0, 0}},
+ {"deps", DM_TABLE_DEPS, {4, 0, 0}},
+ {"rename", DM_DEV_RENAME, {4, 0, 0}},
+ {"version", DM_VERSION, {4, 0, 0}},
+ {"status", DM_TABLE_STATUS, {4, 0, 0}},
+ {"table", DM_TABLE_STATUS, {4, 0, 0}},
+ {"waitevent", DM_DEV_WAIT, {4, 0, 0}},
+ {"names", DM_LIST_DEVICES, {4, 0, 0}},
+ {"clear", DM_TABLE_CLEAR, {4, 0, 0}},
+ {"mknodes", DM_DEV_STATUS, {4, 0, 0}},
+#ifdef DM_LIST_VERSIONS
+ {"versions", DM_LIST_VERSIONS, {4, 1, 0}},
+#endif
+#ifdef DM_TARGET_MSG
+ {"message", DM_TARGET_MSG, {4, 2, 0}},
+#endif
+#ifdef DM_DEV_SET_GEOMETRY
+ {"setgeometry", DM_DEV_SET_GEOMETRY, {4, 6, 0}},
+#endif
+};
+/* *INDENT-ON* */
+
+#define ALIGNMENT_V1 sizeof(int)
+#define ALIGNMENT 8
+
+/* FIXME Rejig library to record & use errno instead */
+#ifndef DM_EXISTS_FLAG
+# define DM_EXISTS_FLAG 0x00000004
+#endif
+
+static void *_align(void *ptr, unsigned int a)
+{
+ register unsigned long agn = --a;
+
+ return (void *) (((unsigned long) ptr + agn) & ~agn);
+}
+
+static int _uname(void)
+{
+ static int _uts_set = 0;
+ struct utsname _uts;
+
+ if (_uts_set)
+ return 1;
+
+ if (uname(&_uts)) {
+ log_error("uname failed: %s", strerror(errno));
+ return 0;
+ }
+ if (sscanf(_uts.release, "%d.%d.%d",
+ &_kernel_major,
+ &_kernel_minor,
+ &_kernel_release) != 3) {
+ log_error("Could not determine kernel version used.");
+ return 0;
+ }
+
+ _uts_set = 1;
+ return 1;
+}
+
+#ifdef DM_IOCTLS
+/*
+ * Set number to NULL to populate _dm_bitset - otherwise first
+ * match is returned.
+ */
+static int _get_proc_number(const char *file, const char *name,
+ uint32_t *number)
+{
+ FILE *fl;
+ char nm[256];
+ int c;
+ uint32_t num;
+
+ if (!(fl = fopen(file, "r"))) {
+ log_sys_error("fopen", file);
+ return 0;
+ }
+
+ while (!feof(fl)) {
+ if (fscanf(fl, "%d %255s\n", &num, &nm[0]) == 2) {
+ if (!strcmp(name, nm)) {
+ if (number) {
+ *number = num;
+ if (fclose(fl))
+ log_sys_error("fclose", file);
+ return 1;
+ }
+ dm_bit_set(_dm_bitset, num);
+ }
+ } else do {
+ c = fgetc(fl);
+ } while (c != EOF && c != '\n');
+ }
+ if (fclose(fl))
+ log_sys_error("fclose", file);
+
+ if (number) {
+ log_error("%s: No entry for %s found", file, name);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _control_device_number(uint32_t *major, uint32_t *minor)
+{
+ if (!_get_proc_number(PROC_DEVICES, MISC_NAME, major) ||
+ !_get_proc_number(PROC_MISC, DM_NAME, minor)) {
+ *major = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Returns 1 if exists; 0 if it doesn't; -1 if it's wrong
+ */
+static int _control_exists(const char *control, uint32_t major, uint32_t minor)
+{
+ struct stat buf;
+
+ if (stat(control, &buf) < 0) {
+ if (errno != ENOENT)
+ log_sys_error("stat", control);
+ return 0;
+ }
+
+ if (!S_ISCHR(buf.st_mode)) {
+ log_verbose("%s: Wrong inode type", control);
+ if (!unlink(control))
+ return 0;
+ log_sys_error("unlink", control);
+ return -1;
+ }
+
+ if (major && buf.st_rdev != MKDEV(major, minor)) {
+ log_verbose("%s: Wrong device number: (%u, %u) instead of "
+ "(%u, %u)", control,
+ MAJOR(buf.st_mode), MINOR(buf.st_mode),
+ major, minor);
+ if (!unlink(control))
+ return 0;
+ log_sys_error("unlink", control);
+ return -1;
+ }
+
+ return 1;
+}
+
+static int _create_control(const char *control, uint32_t major, uint32_t minor)
+{
+ int ret;
+ mode_t old_umask;
+
+ if (!major)
+ return 0;
+
+ (void) dm_prepare_selinux_context(dm_dir(), S_IFDIR);
+ old_umask = umask(DM_DEV_DIR_UMASK);
+ ret = dm_create_dir(dm_dir());
+ umask(old_umask);
+ (void) dm_prepare_selinux_context(NULL, 0);
+
+ if (!ret)
+ return 0;
+
+ log_verbose("Creating device %s (%u, %u)", control, major, minor);
+
+ (void) dm_prepare_selinux_context(control, S_IFCHR);
+ if (mknod(control, S_IFCHR | S_IRUSR | S_IWUSR,
+ MKDEV(major, minor)) < 0) {
+ log_sys_error("mknod", control);
+ (void) dm_prepare_selinux_context(NULL, 0);
+ return 0;
+ }
+ (void) dm_prepare_selinux_context(NULL, 0);
+
+ return 1;
+}
+#endif
+
+/*
+ * FIXME Update bitset in long-running process if dm claims new major numbers.
+ */
+static int _create_dm_bitset(void)
+{
+#ifdef DM_IOCTLS
+ if (_dm_bitset || _dm_device_major)
+ return 1;
+
+ if (!_uname())
+ return 0;
+
+ /*
+ * 2.6 kernels are limited to one major number.
+ * Assume 2.4 kernels are patched not to.
+ * FIXME Check _dm_version and _dm_version_minor if 2.6 changes this.
+ */
+ if (KERNEL_VERSION(_kernel_major, _kernel_minor, _kernel_release) >=
+ KERNEL_VERSION(2, 6, 0))
+ _dm_multiple_major_support = 0;
+
+ if (!_dm_multiple_major_support) {
+ if (!_get_proc_number(PROC_DEVICES, DM_NAME, &_dm_device_major))
+ return 0;
+ return 1;
+ }
+
+ /* Multiple major numbers supported */
+ if (!(_dm_bitset = dm_bitset_create(NULL, NUMBER_OF_MAJORS)))
+ return 0;
+
+ if (!_get_proc_number(PROC_DEVICES, DM_NAME, NULL)) {
+ dm_bitset_destroy(_dm_bitset);
+ _dm_bitset = NULL;
+ return 0;
+ }
+
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+int dm_is_dm_major(uint32_t major)
+{
+ if (!_create_dm_bitset())
+ return 0;
+
+ if (_dm_multiple_major_support)
+ return dm_bit(_dm_bitset, major) ? 1 : 0;
+ else
+ return (major == _dm_device_major) ? 1 : 0;
+}
+
+static void _close_control_fd(void)
+{
+ if (_control_fd != -1) {
+ if (close(_control_fd) < 0)
+ log_sys_error("close", "_control_fd");
+ _control_fd = -1;
+ }
+}
+
+static int _open_and_assign_control_fd(const char *control,
+ int ignore_nodev)
+{
+ _close_control_fd();
+
+ if ((_control_fd = open(control, O_RDWR)) < 0) {
+ if (!(ignore_nodev && errno == ENODEV))
+ log_sys_error("open", control);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _open_control(void)
+{
+#ifdef DM_IOCTLS
+ char control[PATH_MAX];
+ uint32_t major = 0, minor;
+ int dm_mod_autoload_support, needs_open;
+
+ if (_control_fd != -1)
+ return 1;
+
+ if (!_uname())
+ return 0;
+
+ snprintf(control, sizeof(control), "%s/%s", dm_dir(), DM_CONTROL_NODE);
+
+ /*
+ * dm-mod autoloading is supported since kernel 2.6.36.
+ * Udev daemon will try to read modules.devname file extracted
+ * by depmod and create any static nodes needed.
+ * The /dev/mapper/control node can be created and prepared this way.
+ * First access to such node should load dm-mod module automatically.
+ */
+ dm_mod_autoload_support = KERNEL_VERSION(_kernel_major, _kernel_minor,
+ _kernel_release) >= KERNEL_VERSION(2, 6, 36);
+
+ /*
+ * If dm-mod autoloading is supported and the control node exists
+ * already try to open it now. This should autoload dm-mod module.
+ */
+ if (dm_mod_autoload_support) {
+ if (!_get_proc_number(PROC_DEVICES, MISC_NAME, &major))
+ /* If major not found, just fallback to hardcoded value. */
+ major = MISC_MAJOR;
+
+ /* Recreate the node with correct major and minor if needed. */
+ if (!_control_exists(control, major, MAPPER_CTRL_MINOR) &&
+ !_create_control(control, major, MAPPER_CTRL_MINOR))
+ goto error;
+
+ _open_and_assign_control_fd(control, 1);
+ }
+
+ /*
+ * Get major and minor number assigned for the control node.
+ * In case we make use of the module autoload support, this
+ * information should be accessible now as well.
+ */
+ if (!_control_device_number(&major, &minor))
+ log_error("Is device-mapper driver missing from kernel?");
+
+ /*
+ * Check the control node and its major and minor number.
+ * If there's anything wrong, remove the old node and create
+ * a correct one.
+ */
+ if ((needs_open = !_control_exists(control, major, minor)) &&
+ !_create_control(control, major, minor)) {
+ _close_control_fd();
+ goto error;
+ }
+
+ /*
+ * For older kernels without dm-mod autoloading support, we always
+ * need to open the control node here - we still haven't done that!
+ * For newer kernels with dm-mod autoloading, we open it only if the
+ * node was recreated and corrected in previous step.
+ */
+ if ((!dm_mod_autoload_support || needs_open) &&
+ !_open_and_assign_control_fd(control, 0))
+ goto error;
+
+ if (!_create_dm_bitset()) {
+ log_error("Failed to set up list of device-mapper major numbers");
+ return 0;
+ }
+
+ return 1;
+
+error:
+ log_error("Failure to communicate with kernel device-mapper driver.");
+ return 0;
+#else
+ return 1;
+#endif
+}
+
+static void _dm_zfree_string(char *string)
+{
+ if (string) {
+ memset(string, 0, strlen(string));
+ dm_free(string);
+ }
+}
+
+static void _dm_zfree_dmi(struct dm_ioctl *dmi)
+{
+ if (dmi) {
+ memset(dmi, 0, dmi->data_size);
+ dm_free(dmi);
+ }
+}
+
+void dm_task_destroy(struct dm_task *dmt)
+{
+ struct target *t, *n;
+
+ for (t = dmt->head; t; t = n) {
+ n = t->next;
+ _dm_zfree_string(t->params);
+ dm_free(t->type);
+ dm_free(t);
+ }
+
+ if (dmt->dev_name)
+ dm_free(dmt->dev_name);
+
+ if (dmt->newname)
+ dm_free(dmt->newname);
+
+ if (dmt->message)
+ dm_free(dmt->message);
+
+ _dm_zfree_dmi(dmt->dmi.v4);
+
+ if (dmt->uuid)
+ dm_free(dmt->uuid);
+
+ dm_free(dmt);
+}
+
+/*
+ * Protocol Version 1 compatibility functions.
+ */
+
+#ifdef DM_COMPAT
+
+static void _dm_zfree_dmi_v1(struct dm_ioctl_v1 *dmi)
+{
+ if (dmi) {
+ memset(dmi, 0, dmi->data_size);
+ dm_free(dmi);
+ }
+}
+
+static int _dm_task_get_driver_version_v1(struct dm_task *dmt, char *version,
+ size_t size)
+{
+ unsigned int *v;
+
+ if (!dmt->dmi.v1) {
+ version[0] = '\0';
+ return 0;
+ }
+
+ v = dmt->dmi.v1->version;
+ snprintf(version, size, "%u.%u.%u", v[0], v[1], v[2]);
+ return 1;
+}
+
+/* Unmarshall the target info returned from a status call */
+static int _unmarshal_status_v1(struct dm_task *dmt, struct dm_ioctl_v1 *dmi)
+{
+ char *outbuf = (char *) dmi + dmi->data_start;
+ char *outptr = outbuf;
+ int32_t i;
+ struct dm_target_spec_v1 *spec;
+
+ for (i = 0; i < dmi->target_count; i++) {
+ spec = (struct dm_target_spec_v1 *) outptr;
+
+ if (!dm_task_add_target(dmt, spec->sector_start,
+ (uint64_t) spec->length,
+ spec->target_type,
+ outptr + sizeof(*spec))) {
+ return 0;
+ }
+
+ outptr = outbuf + spec->next;
+ }
+
+ return 1;
+}
+
+static int _dm_format_dev_v1(char *buf, int bufsize, uint32_t dev_major,
+ uint32_t dev_minor)
+{
+ int r;
+
+ if (bufsize < 8)
+ return 0;
+
+ r = snprintf(buf, bufsize, "%03x:%03x", dev_major, dev_minor);
+ if (r < 0 || r > bufsize - 1)
+ return 0;
+
+ return 1;
+}
+
+static int _dm_task_get_info_v1(struct dm_task *dmt, struct dm_info *info)
+{
+ if (!dmt->dmi.v1)
+ return 0;
+
+ memset(info, 0, sizeof(*info));
+
+ info->exists = dmt->dmi.v1->flags & DM_EXISTS_FLAG ? 1 : 0;
+ if (!info->exists)
+ return 1;
+
+ info->suspended = dmt->dmi.v1->flags & DM_SUSPEND_FLAG ? 1 : 0;
+ info->read_only = dmt->dmi.v1->flags & DM_READONLY_FLAG ? 1 : 0;
+ info->target_count = dmt->dmi.v1->target_count;
+ info->open_count = dmt->dmi.v1->open_count;
+ info->event_nr = 0;
+ info->major = MAJOR(dmt->dmi.v1->dev);
+ info->minor = MINOR(dmt->dmi.v1->dev);
+ info->live_table = 1;
+ info->inactive_table = 0;
+
+ return 1;
+}
+
+static const char *_dm_task_get_name_v1(const struct dm_task *dmt)
+{
+ return (dmt->dmi.v1->name);
+}
+
+static const char *_dm_task_get_uuid_v1(const struct dm_task *dmt)
+{
+ return (dmt->dmi.v1->uuid);
+}
+
+static struct dm_deps *_dm_task_get_deps_v1(struct dm_task *dmt)
+{
+ log_error("deps version 1 no longer supported by libdevmapper");
+ return NULL;
+}
+
+static struct dm_names *_dm_task_get_names_v1(struct dm_task *dmt)
+{
+ return (struct dm_names *) (((void *) dmt->dmi.v1) +
+ dmt->dmi.v1->data_start);
+}
+
+static void *_add_target_v1(struct target *t, void *out, void *end)
+{
+ void *out_sp = out;
+ struct dm_target_spec_v1 sp;
+ size_t sp_size = sizeof(struct dm_target_spec_v1);
+ int len;
+
+ out += sp_size;
+ if (out >= end)
+ return_NULL;
+
+ sp.status = 0;
+ sp.sector_start = t->start;
+ sp.length = t->length;
+ strncpy(sp.target_type, t->type, sizeof(sp.target_type));
+
+ len = strlen(t->params);
+
+ if ((out + len + 1) >= end)
+ return_NULL;
+
+ strcpy((char *) out, t->params);
+ out += len + 1;
+
+ /* align next block */
+ out = _align(out, ALIGNMENT_V1);
+
+ sp.next = out - out_sp;
+
+ memcpy(out_sp, &sp, sp_size);
+
+ return out;
+}
+
+static struct dm_ioctl_v1 *_flatten_v1(struct dm_task *dmt)
+{
+ const size_t min_size = 16 * 1024;
+ const int (*version)[3];
+
+ struct dm_ioctl_v1 *dmi;
+ struct target *t;
+ size_t len = sizeof(struct dm_ioctl_v1);
+ void *b, *e;
+ int count = 0;
+
+ for (t = dmt->head; t; t = t->next) {
+ len += sizeof(struct dm_target_spec_v1);
+ len += strlen(t->params) + 1 + ALIGNMENT_V1;
+ count++;
+ }
+
+ if (count && dmt->newname) {
+ log_error("targets and newname are incompatible");
+ return NULL;
+ }
+
+ if (dmt->newname)
+ len += strlen(dmt->newname) + 1;
+
+ /*
+ * Give len a minimum size so that we have space to store
+ * dependencies or status information.
+ */
+ if (len < min_size)
+ len = min_size;
+
+ if (!(dmi = dm_malloc(len)))
+ return NULL;
+
+ memset(dmi, 0, len);
+
+ version = &_cmd_data_v1[dmt->type].version;
+
+ dmi->version[0] = (*version)[0];
+ dmi->version[1] = (*version)[1];
+ dmi->version[2] = (*version)[2];
+
+ dmi->data_size = len;
+ dmi->data_start = sizeof(struct dm_ioctl_v1);
+
+ if (dmt->dev_name)
+ strncpy(dmi->name, dmt->dev_name, sizeof(dmi->name));
+
+ if (dmt->type == DM_DEVICE_SUSPEND)
+ dmi->flags |= DM_SUSPEND_FLAG;
+ if (dmt->read_only)
+ dmi->flags |= DM_READONLY_FLAG;
+
+ if (dmt->minor >= 0) {
+ if (dmt->major <= 0) {
+ log_error("Missing major number for persistent device");
+ return NULL;
+ }
+ dmi->flags |= DM_PERSISTENT_DEV_FLAG;
+ dmi->dev = MKDEV(dmt->major, dmt->minor);
+ }
+
+ if (dmt->uuid)
+ strncpy(dmi->uuid, dmt->uuid, sizeof(dmi->uuid));
+
+ dmi->target_count = count;
+
+ b = (void *) (dmi + 1);
+ e = (void *) ((char *) dmi + len);
+
+ for (t = dmt->head; t; t = t->next)
+ if (!(b = _add_target_v1(t, b, e))) {
+ log_error("Ran out of memory building ioctl parameter");
+ goto bad;
+ }
+
+ if (dmt->newname)
+ strcpy(b, dmt->newname);
+
+ return dmi;
+
+ bad:
+ _dm_zfree_dmi_v1(dmi);
+ return NULL;
+}
+
+static int _dm_names_v1(struct dm_ioctl_v1 *dmi)
+{
+ const char *dev_dir = dm_dir();
+ int r = 1, len;
+ const char *name;
+ struct dirent *dirent;
+ DIR *d;
+ struct dm_names *names, *old_names = NULL;
+ void *end = (void *) dmi + dmi->data_size;
+ struct stat buf;
+ char path[PATH_MAX];
+
+ log_warn("WARNING: Device list may be incomplete with interface "
+ "version 1.");
+ log_warn("Please upgrade your kernel device-mapper driver.");
+
+ if (!(d = opendir(dev_dir))) {
+ log_sys_error("opendir", dev_dir);
+ return 0;
+ }
+
+ names = (struct dm_names *) ((void *) dmi + dmi->data_start);
+
+ names->dev = 0; /* Flags no data */
+
+ while ((dirent = readdir(d))) {
+ name = dirent->d_name;
+
+ if (name[0] == '.' || !strcmp(name, "control"))
+ continue;
+
+ if (old_names)
+ old_names->next = (uint32_t) ((void *) names -
+ (void *) old_names);
+ snprintf(path, sizeof(path), "%s/%s", dev_dir, name);
+ if (stat(path, &buf)) {
+ log_sys_error("stat", path);
+ continue;
+ }
+ if (!S_ISBLK(buf.st_mode))
+ continue;
+ names->dev = (uint64_t) buf.st_rdev;
+ names->next = 0;
+ len = strlen(name);
+ if (((void *) (names + 1) + len + 1) >= end) {
+ log_error("Insufficient buffer space for device list");
+ r = 0;
+ break;
+ }
+
+ strcpy(names->name, name);
+
+ old_names = names;
+ names = _align((void *) ++names + len + 1, ALIGNMENT);
+ }
+
+ if (closedir(d))
+ log_sys_error("closedir", dev_dir);
+
+ return r;
+}
+
+static int _dm_task_run_v1(struct dm_task *dmt)
+{
+ struct dm_ioctl_v1 *dmi;
+ unsigned int command;
+
+ dmi = _flatten_v1(dmt);
+ if (!dmi) {
+ log_error("Couldn't create ioctl argument.");
+ return 0;
+ }
+
+ if (!_open_control())
+ return 0;
+
+ if ((unsigned) dmt->type >=
+ (sizeof(_cmd_data_v1) / sizeof(*_cmd_data_v1))) {
+ log_error(INTERNAL_ERROR "unknown device-mapper task %d",
+ dmt->type);
+ goto bad;
+ }
+
+ command = _cmd_data_v1[dmt->type].cmd;
+
+ if (dmt->type == DM_DEVICE_TABLE)
+ dmi->flags |= DM_STATUS_TABLE_FLAG;
+
+ if (dmt->new_uuid) {
+ log_error("Changing UUID is not supported by kernel.");
+ goto bad;
+ }
+
+ log_debug("dm %s %s %s%s%s [%u]", _cmd_data_v1[dmt->type].name,
+ dmi->name, dmi->uuid, dmt->newname ? " " : "",
+ dmt->newname ? dmt->newname : "",
+ dmi->data_size);
+ if (dmt->type == DM_DEVICE_LIST) {
+ if (!_dm_names_v1(dmi))
+ goto bad;
+ }
+#ifdef DM_IOCTLS
+ else if (ioctl(_control_fd, command, dmi) < 0) {
+ if (_log_suppress)
+ log_verbose("device-mapper: %s ioctl failed: %s",
+ _cmd_data_v1[dmt->type].name,
+ strerror(errno));
+ else
+ log_error("device-mapper: %s ioctl failed: %s",
+ _cmd_data_v1[dmt->type].name,
+ strerror(errno));
+ goto bad;
+ }
+#else /* Userspace alternative for testing */
+#endif
+
+ if (dmi->flags & DM_BUFFER_FULL_FLAG)
+ /* FIXME Increase buffer size and retry operation (if query) */
+ log_error("WARNING: libdevmapper buffer too small for data");
+
+ switch (dmt->type) {
+ case DM_DEVICE_CREATE:
+ add_dev_node(dmt->dev_name, MAJOR(dmi->dev), MINOR(dmi->dev),
+ dmt->uid, dmt->gid, dmt->mode, 0);
+ break;
+
+ case DM_DEVICE_REMOVE:
+ rm_dev_node(dmt->dev_name, 0);
+ break;
+
+ case DM_DEVICE_RENAME:
+ rename_dev_node(dmt->dev_name, dmt->newname, 0);
+ break;
+
+ case DM_DEVICE_MKNODES:
+ if (dmi->flags & DM_EXISTS_FLAG)
+ add_dev_node(dmt->dev_name, MAJOR(dmi->dev),
+ MINOR(dmi->dev), dmt->uid,
+ dmt->gid, dmt->mode, 0);
+ else
+ rm_dev_node(dmt->dev_name, 0);
+ break;
+
+ case DM_DEVICE_STATUS:
+ case DM_DEVICE_TABLE:
+ if (!_unmarshal_status_v1(dmt, dmi))
+ goto bad;
+ break;
+
+ case DM_DEVICE_SUSPEND:
+ case DM_DEVICE_RESUME:
+ dmt->type = DM_DEVICE_INFO;
+ if (!dm_task_run(dmt))
+ goto bad;
+ _dm_zfree_dmi_v1(dmi); /* We'll use what info returned */
+ return 1;
+ }
+
+ dmt->dmi.v1 = dmi;
+ return 1;
+
+ bad:
+ _dm_zfree_dmi_v1(dmi);
+ return 0;
+}
+
+#endif
+
+/*
+ * Protocol Version 4 functions.
+ */
+
+int dm_task_get_driver_version(struct dm_task *dmt, char *version, size_t size)
+{
+ unsigned *v;
+
+#ifdef DM_COMPAT
+ if (_dm_version == 1)
+ return _dm_task_get_driver_version_v1(dmt, version, size);
+#endif
+
+ if (!dmt->dmi.v4) {
+ version[0] = '\0';
+ return 0;
+ }
+
+ v = dmt->dmi.v4->version;
+ snprintf(version, size, "%u.%u.%u", v[0], v[1], v[2]);
+ _dm_version_minor = v[1];
+ _dm_version_patchlevel = v[2];
+
+ return 1;
+}
+
+static int _check_version(char *version, size_t size, int log_suppress)
+{
+ struct dm_task *task;
+ int r;
+
+ if (!(task = dm_task_create(DM_DEVICE_VERSION))) {
+ log_error("Failed to get device-mapper version");
+ version[0] = '\0';
+ return 0;
+ }
+
+ if (log_suppress)
+ _log_suppress = 1;
+
+ r = dm_task_run(task);
+ dm_task_get_driver_version(task, version, size);
+ dm_task_destroy(task);
+ _log_suppress = 0;
+
+ return r;
+}
+
+/*
+ * Find out device-mapper's major version number the first time
+ * this is called and whether or not we support it.
+ */
+int dm_check_version(void)
+{
+ char libversion[64], dmversion[64];
+ const char *compat = "";
+
+ if (_version_checked)
+ return _version_ok;
+
+ _version_checked = 1;
+
+ if (_check_version(dmversion, sizeof(dmversion), _dm_compat))
+ return 1;
+
+ if (!_dm_compat)
+ goto bad;
+
+ log_verbose("device-mapper ioctl protocol version %u failed. "
+ "Trying protocol version 1.", _dm_version);
+ _dm_version = 1;
+ if (_check_version(dmversion, sizeof(dmversion), 0)) {
+ log_verbose("Using device-mapper ioctl protocol version 1");
+ return 1;
+ }
+
+ compat = "(compat)";
+
+ dm_get_library_version(libversion, sizeof(libversion));
+
+ log_error("Incompatible libdevmapper %s%s and kernel driver %s",
+ libversion, compat, dmversion);
+
+ bad:
+ _version_ok = 0;
+ return 0;
+}
+
+int dm_cookie_supported(void)
+{
+ return (dm_check_version() &&
+ _dm_version >= 4 &&
+ _dm_version_minor >= 15);
+}
+
+void *dm_get_next_target(struct dm_task *dmt, void *next,
+ uint64_t *start, uint64_t *length,
+ char **target_type, char **params)
+{
+ struct target *t = (struct target *) next;
+
+ if (!t)
+ t = dmt->head;
+
+ if (!t)
+ return NULL;
+
+ *start = t->start;
+ *length = t->length;
+ *target_type = t->type;
+ *params = t->params;
+
+ return t->next;
+}
+
+/* Unmarshall the target info returned from a status call */
+static int _unmarshal_status(struct dm_task *dmt, struct dm_ioctl *dmi)
+{
+ char *outbuf = (char *) dmi + dmi->data_start;
+ char *outptr = outbuf;
+ uint32_t i;
+ struct dm_target_spec *spec;
+
+ for (i = 0; i < dmi->target_count; i++) {
+ spec = (struct dm_target_spec *) outptr;
+ if (!dm_task_add_target(dmt, spec->sector_start,
+ spec->length,
+ spec->target_type,
+ outptr + sizeof(*spec))) {
+ return 0;
+ }
+
+ outptr = outbuf + spec->next;
+ }
+
+ return 1;
+}
+
+int dm_format_dev(char *buf, int bufsize, uint32_t dev_major,
+ uint32_t dev_minor)
+{
+ int r;
+
+#ifdef DM_COMPAT
+ if (_dm_version == 1)
+ return _dm_format_dev_v1(buf, bufsize, dev_major, dev_minor);
+#endif
+
+ if (bufsize < 8)
+ return 0;
+
+ r = snprintf(buf, (size_t) bufsize, "%u:%u", dev_major, dev_minor);
+ if (r < 0 || r > bufsize - 1)
+ return 0;
+
+ return 1;
+}
+
+int dm_task_get_info(struct dm_task *dmt, struct dm_info *info)
+{
+#ifdef DM_COMPAT
+ if (_dm_version == 1)
+ return _dm_task_get_info_v1(dmt, info);
+#endif
+
+ if (!dmt->dmi.v4)
+ return 0;
+
+ memset(info, 0, sizeof(*info));
+
+ info->exists = dmt->dmi.v4->flags & DM_EXISTS_FLAG ? 1 : 0;
+ if (!info->exists)
+ return 1;
+
+ info->suspended = dmt->dmi.v4->flags & DM_SUSPEND_FLAG ? 1 : 0;
+ info->read_only = dmt->dmi.v4->flags & DM_READONLY_FLAG ? 1 : 0;
+ info->live_table = dmt->dmi.v4->flags & DM_ACTIVE_PRESENT_FLAG ? 1 : 0;
+ info->inactive_table = dmt->dmi.v4->flags & DM_INACTIVE_PRESENT_FLAG ?
+ 1 : 0;
+ info->target_count = dmt->dmi.v4->target_count;
+ info->open_count = dmt->dmi.v4->open_count;
+ info->event_nr = dmt->dmi.v4->event_nr;
+ info->major = MAJOR(dmt->dmi.v4->dev);
+ info->minor = MINOR(dmt->dmi.v4->dev);
+
+ return 1;
+}
+
+uint32_t dm_task_get_read_ahead(const struct dm_task *dmt, uint32_t *read_ahead)
+{
+ const char *dev_name;
+
+ *read_ahead = 0;
+
+#ifdef DM_COMPAT
+ /* Not supporting this */
+ if (_dm_version == 1)
+ return 1;
+#endif
+
+ if (!dmt->dmi.v4 || !(dmt->dmi.v4->flags & DM_EXISTS_FLAG))
+ return 0;
+
+ if (*dmt->dmi.v4->name)
+ dev_name = dmt->dmi.v4->name;
+ else if (dmt->dev_name)
+ dev_name = dmt->dev_name;
+ else {
+ log_error("Get read ahead request failed: device name unrecorded.");
+ return 0;
+ }
+
+ return get_dev_node_read_ahead(dev_name, read_ahead);
+}
+
+const char *dm_task_get_name(const struct dm_task *dmt)
+{
+#ifdef DM_COMPAT
+ if (_dm_version == 1)
+ return _dm_task_get_name_v1(dmt);
+#endif
+
+ return (dmt->dmi.v4->name);
+}
+
+const char *dm_task_get_uuid(const struct dm_task *dmt)
+{
+#ifdef DM_COMPAT
+ if (_dm_version == 1)
+ return _dm_task_get_uuid_v1(dmt);
+#endif
+
+ return (dmt->dmi.v4->uuid);
+}
+
+struct dm_deps *dm_task_get_deps(struct dm_task *dmt)
+{
+#ifdef DM_COMPAT
+ if (_dm_version == 1)
+ return _dm_task_get_deps_v1(dmt);
+#endif
+
+ return (struct dm_deps *) (((char *) dmt->dmi.v4) +
+ dmt->dmi.v4->data_start);
+}
+
+struct dm_names *dm_task_get_names(struct dm_task *dmt)
+{
+#ifdef DM_COMPAT
+ if (_dm_version == 1)
+ return _dm_task_get_names_v1(dmt);
+#endif
+
+ return (struct dm_names *) (((char *) dmt->dmi.v4) +
+ dmt->dmi.v4->data_start);
+}
+
+struct dm_versions *dm_task_get_versions(struct dm_task *dmt)
+{
+ return (struct dm_versions *) (((char *) dmt->dmi.v4) +
+ dmt->dmi.v4->data_start);
+}
+
+int dm_task_set_ro(struct dm_task *dmt)
+{
+ dmt->read_only = 1;
+ return 1;
+}
+
+int dm_task_set_read_ahead(struct dm_task *dmt, uint32_t read_ahead,
+ uint32_t read_ahead_flags)
+{
+ dmt->read_ahead = read_ahead;
+ dmt->read_ahead_flags = read_ahead_flags;
+
+ return 1;
+}
+
+int dm_task_suppress_identical_reload(struct dm_task *dmt)
+{
+ dmt->suppress_identical_reload = 1;
+ return 1;
+}
+
+int dm_task_set_newuuid(struct dm_task *dmt, const char *newuuid)
+{
+ if (strlen(newuuid) >= DM_UUID_LEN) {
+ log_error("Uuid \"%s\" too long", newuuid);
+ return 0;
+ }
+
+ if (!(dmt->newname = dm_strdup(newuuid))) {
+ log_error("dm_task_set_newuuid: strdup(%s) failed", newuuid);
+ return 0;
+ }
+ dmt->new_uuid = 1;
+
+ return 1;
+}
+
+int dm_task_set_newname(struct dm_task *dmt, const char *newname)
+{
+ if (strchr(newname, '/')) {
+ log_error("Name \"%s\" invalid. It contains \"/\".", newname);
+ return 0;
+ }
+
+ if (strlen(newname) >= DM_NAME_LEN) {
+ log_error("Name \"%s\" too long", newname);
+ return 0;
+ }
+
+ if (!(dmt->newname = dm_strdup(newname))) {
+ log_error("dm_task_set_newname: strdup(%s) failed", newname);
+ return 0;
+ }
+ dmt->new_uuid = 0;
+
+ return 1;
+}
+
+int dm_task_set_message(struct dm_task *dmt, const char *message)
+{
+ if (!(dmt->message = dm_strdup(message))) {
+ log_error("dm_task_set_message: strdup failed");
+ return 0;
+ }
+
+ return 1;
+}
+
+int dm_task_set_sector(struct dm_task *dmt, uint64_t sector)
+{
+ dmt->sector = sector;
+
+ return 1;
+}
+
+int dm_task_set_geometry(struct dm_task *dmt, const char *cylinders, const char *heads, const char *sectors, const char *start)
+{
+ size_t len = strlen(cylinders) + 1 + strlen(heads) + 1 + strlen(sectors) + 1 + strlen(start) + 1;
+
+ if (!(dmt->geometry = dm_malloc(len))) {
+ log_error("dm_task_set_geometry: dm_malloc failed");
+ return 0;
+ }
+
+ if (sprintf(dmt->geometry, "%s %s %s %s", cylinders, heads, sectors, start) < 0) {
+ log_error("dm_task_set_geometry: sprintf failed");
+ return 0;
+ }
+
+ return 1;
+}
+
+int dm_task_no_flush(struct dm_task *dmt)
+{
+ dmt->no_flush = 1;
+
+ return 1;
+}
+
+int dm_task_no_open_count(struct dm_task *dmt)
+{
+ dmt->no_open_count = 1;
+
+ return 1;
+}
+
+int dm_task_skip_lockfs(struct dm_task *dmt)
+{
+ dmt->skip_lockfs = 1;
+
+ return 1;
+}
+
+int dm_task_query_inactive_table(struct dm_task *dmt)
+{
+ dmt->query_inactive_table = 1;
+
+ return 1;
+}
+
+int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr)
+{
+ dmt->event_nr = event_nr;
+
+ return 1;
+}
+
+struct target *create_target(uint64_t start, uint64_t len, const char *type,
+ const char *params)
+{
+ struct target *t = dm_malloc(sizeof(*t));
+
+ if (!t) {
+ log_error("create_target: malloc(%" PRIsize_t ") failed",
+ sizeof(*t));
+ return NULL;
+ }
+
+ memset(t, 0, sizeof(*t));
+
+ if (!(t->params = dm_strdup(params))) {
+ log_error("create_target: strdup(params) failed");
+ goto bad;
+ }
+
+ if (!(t->type = dm_strdup(type))) {
+ log_error("create_target: strdup(type) failed");
+ goto bad;
+ }
+
+ t->start = start;
+ t->length = len;
+ return t;
+
+ bad:
+ _dm_zfree_string(t->params);
+ dm_free(t->type);
+ dm_free(t);
+ return NULL;
+}
+
+static void *_add_target(struct target *t, void *out, void *end)
+{
+ void *out_sp = out;
+ struct dm_target_spec sp;
+ size_t sp_size = sizeof(struct dm_target_spec);
+ int len;
+
+ out += sp_size;
+ if (out >= end)
+ return_NULL;
+
+ sp.status = 0;
+ sp.sector_start = t->start;
+ sp.length = t->length;
+ strncpy(sp.target_type, t->type, sizeof(sp.target_type));
+
+ len = strlen(t->params);
+
+ if ((out + len + 1) >= end)
+ return_NULL;
+
+ strcpy((char *) out, t->params);
+ out += len + 1;
+
+ /* align next block */
+ out = _align(out, ALIGNMENT);
+
+ sp.next = out - out_sp;
+ memcpy(out_sp, &sp, sp_size);
+
+ return out;
+}
+
+static int _lookup_dev_name(uint64_t dev, char *buf, size_t len)
+{
+ struct dm_names *names;
+ unsigned next = 0;
+ struct dm_task *dmt;
+ int r = 0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
+ return 0;
+
+ if (!dm_task_run(dmt))
+ goto out;
+
+ if (!(names = dm_task_get_names(dmt)))
+ goto out;
+
+ if (!names->dev)
+ goto out;
+
+ do {
+ names = (void *) names + next;
+ if (names->dev == dev) {
+ strncpy(buf, names->name, len);
+ r = 1;
+ break;
+ }
+ next = names->next;
+ } while (next);
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static struct dm_ioctl *_flatten(struct dm_task *dmt, unsigned repeat_count)
+{
+ const size_t min_size = 16 * 1024;
+ const int (*version)[3];
+
+ struct dm_ioctl *dmi;
+ struct target *t;
+ struct dm_target_msg *tmsg;
+ size_t len = sizeof(struct dm_ioctl);
+ void *b, *e;
+ int count = 0;
+
+ for (t = dmt->head; t; t = t->next) {
+ len += sizeof(struct dm_target_spec);
+ len += strlen(t->params) + 1 + ALIGNMENT;
+ count++;
+ }
+
+ if (count && (dmt->sector || dmt->message)) {
+ log_error("targets and message are incompatible");
+ return NULL;
+ }
+
+ if (count && dmt->newname) {
+ log_error("targets and rename are incompatible");
+ return NULL;
+ }
+
+ if (count && dmt->geometry) {
+ log_error("targets and geometry are incompatible");
+ return NULL;
+ }
+
+ if (dmt->newname && (dmt->sector || dmt->message)) {
+ log_error("message and rename are incompatible");
+ return NULL;
+ }
+
+ if (dmt->newname && dmt->geometry) {
+ log_error("geometry and rename are incompatible");
+ return NULL;
+ }
+
+ if (dmt->geometry && (dmt->sector || dmt->message)) {
+ log_error("geometry and message are incompatible");
+ return NULL;
+ }
+
+ if (dmt->sector && !dmt->message) {
+ log_error("message is required with sector");
+ return NULL;
+ }
+
+ if (dmt->newname)
+ len += strlen(dmt->newname) + 1;
+
+ if (dmt->message)
+ len += sizeof(struct dm_target_msg) + strlen(dmt->message) + 1;
+
+ if (dmt->geometry)
+ len += strlen(dmt->geometry) + 1;
+
+ /*
+ * Give len a minimum size so that we have space to store
+ * dependencies or status information.
+ */
+ if (len < min_size)
+ len = min_size;
+
+ /* Increase buffer size if repeating because buffer was too small */
+ while (repeat_count--)
+ len *= 2;
+
+ if (!(dmi = dm_malloc(len)))
+ return NULL;
+
+ memset(dmi, 0, len);
+
+ version = &_cmd_data_v4[dmt->type].version;
+
+ dmi->version[0] = (*version)[0];
+ dmi->version[1] = (*version)[1];
+ dmi->version[2] = (*version)[2];
+
+ dmi->data_size = len;
+ dmi->data_start = sizeof(struct dm_ioctl);
+
+ if (dmt->minor >= 0) {
+ if (dmt->major <= 0) {
+ log_error("Missing major number for persistent device.");
+ goto bad;
+ }
+
+ if (!_dm_multiple_major_support && dmt->allow_default_major_fallback &&
+ dmt->major != _dm_device_major) {
+ log_verbose("Overriding major number of %" PRIu32
+ " with %" PRIu32 " for persistent device.",
+ dmt->major, _dm_device_major);
+ dmt->major = _dm_device_major;
+ }
+
+ dmi->flags |= DM_PERSISTENT_DEV_FLAG;
+ dmi->dev = MKDEV(dmt->major, dmt->minor);
+ }
+
+ /* Does driver support device number referencing? */
+ if (_dm_version_minor < 3 && !dmt->dev_name && !dmt->uuid && dmi->dev) {
+ if (!_lookup_dev_name(dmi->dev, dmi->name, sizeof(dmi->name))) {
+ log_error("Unable to find name for device (%" PRIu32
+ ":%" PRIu32 ")", dmt->major, dmt->minor);
+ goto bad;
+ }
+ log_verbose("device (%" PRIu32 ":%" PRIu32 ") is %s "
+ "for compatibility with old kernel",
+ dmt->major, dmt->minor, dmi->name);
+ }
+
+ /* FIXME Until resume ioctl supplies name, use dev_name for readahead */
+ if (dmt->dev_name && (dmt->type != DM_DEVICE_RESUME || dmt->minor < 0 ||
+ dmt->major < 0))
+ strncpy(dmi->name, dmt->dev_name, sizeof(dmi->name));
+
+ if (dmt->uuid)
+ strncpy(dmi->uuid, dmt->uuid, sizeof(dmi->uuid));
+
+ if (dmt->type == DM_DEVICE_SUSPEND)
+ dmi->flags |= DM_SUSPEND_FLAG;
+ if (dmt->no_flush)
+ dmi->flags |= DM_NOFLUSH_FLAG;
+ if (dmt->read_only)
+ dmi->flags |= DM_READONLY_FLAG;
+ if (dmt->skip_lockfs)
+ dmi->flags |= DM_SKIP_LOCKFS_FLAG;
+ if (dmt->query_inactive_table) {
+ if (_dm_version_minor < 16)
+ log_warn("WARNING: Inactive table query unsupported "
+ "by kernel. It will use live table.");
+ dmi->flags |= DM_QUERY_INACTIVE_TABLE_FLAG;
+ }
+ if (dmt->new_uuid) {
+ if (_dm_version_minor < 19) {
+ log_error("WARNING: Setting UUID unsupported by "
+ "kernel. Aborting operation.");
+ goto bad;
+ }
+ dmi->flags |= DM_UUID_FLAG;
+ }
+
+ dmi->target_count = count;
+ dmi->event_nr = dmt->event_nr;
+
+ b = (void *) (dmi + 1);
+ e = (void *) ((char *) dmi + len);
+
+ for (t = dmt->head; t; t = t->next)
+ if (!(b = _add_target(t, b, e))) {
+ log_error("Ran out of memory building ioctl parameter");
+ goto bad;
+ }
+
+ if (dmt->newname)
+ strcpy(b, dmt->newname);
+
+ if (dmt->message) {
+ tmsg = (struct dm_target_msg *) b;
+ tmsg->sector = dmt->sector;
+ strcpy(tmsg->message, dmt->message);
+ }
+
+ if (dmt->geometry)
+ strcpy(b, dmt->geometry);
+
+ return dmi;
+
+ bad:
+ _dm_zfree_dmi(dmi);
+ return NULL;
+}
+
+static int _process_mapper_dir(struct dm_task *dmt)
+{
+ struct dirent *dirent;
+ DIR *d;
+ const char *dir;
+ int r = 1;
+
+ dir = dm_dir();
+ if (!(d = opendir(dir))) {
+ log_sys_error("opendir", dir);
+ return 0;
+ }
+
+ while ((dirent = readdir(d))) {
+ if (!strcmp(dirent->d_name, ".") ||
+ !strcmp(dirent->d_name, "..") ||
+ !strcmp(dirent->d_name, "control"))
+ continue;
+ if (!dm_task_set_name(dmt, dirent->d_name)) {
+ r = 0;
+ stack;
+ continue; /* try next name */
+ }
+ if (!dm_task_run(dmt)) {
+ r = 0;
+ stack; /* keep going */
+ }
+ }
+
+ if (closedir(d))
+ log_sys_error("closedir", dir);
+
+ return r;
+}
+
+static int _process_all_v4(struct dm_task *dmt)
+{
+ struct dm_task *task;
+ struct dm_names *names;
+ unsigned next = 0;
+ int r = 1;
+
+ if (!(task = dm_task_create(DM_DEVICE_LIST)))
+ return 0;
+
+ if (!dm_task_run(task)) {
+ r = 0;
+ goto out;
+ }
+
+ if (!(names = dm_task_get_names(task))) {
+ r = 0;
+ goto out;
+ }
+
+ if (!names->dev)
+ goto out;
+
+ do {
+ names = (void *) names + next;
+ if (!dm_task_set_name(dmt, names->name)) {
+ r = 0;
+ goto out;
+ }
+ if (!dm_task_run(dmt))
+ r = 0;
+ next = names->next;
+ } while (next);
+
+ out:
+ dm_task_destroy(task);
+ return r;
+}
+
+static int _mknodes_v4(struct dm_task *dmt)
+{
+ (void) _process_mapper_dir(dmt);
+
+ return _process_all_v4(dmt);
+}
+
+/*
+ * If an operation that uses a cookie fails, decrement the
+ * semaphore instead of udev.
+ */
+static int _udev_complete(struct dm_task *dmt)
+{
+ uint16_t base;
+
+ if (dmt->cookie_set &&
+ (base = dmt->event_nr & ~DM_UDEV_FLAGS_MASK))
+ /* strip flags from the cookie and use cookie magic instead */
+ return dm_udev_complete(base | (DM_COOKIE_MAGIC <<
+ DM_UDEV_FLAGS_SHIFT));
+
+ return 1;
+}
+
+static int _check_uevent_generated(struct dm_ioctl *dmi)
+{
+ if (!dm_check_version() ||
+ _dm_version < 4 ||
+ _dm_version_minor < 17)
+ /* can't check, assume uevent is generated */
+ return 1;
+
+ return dmi->flags & DM_UEVENT_GENERATED_FLAG;
+}
+
+static int _create_and_load_v4(struct dm_task *dmt)
+{
+ struct dm_task *task;
+ int r;
+ uint32_t cookie;
+
+ /* Use new task struct to create the device */
+ if (!(task = dm_task_create(DM_DEVICE_CREATE))) {
+ log_error("Failed to create device-mapper task struct");
+ _udev_complete(dmt);
+ return 0;
+ }
+
+ /* Copy across relevant fields */
+ if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name)) {
+ dm_task_destroy(task);
+ _udev_complete(dmt);
+ return 0;
+ }
+
+ if (dmt->uuid && !dm_task_set_uuid(task, dmt->uuid)) {
+ dm_task_destroy(task);
+ _udev_complete(dmt);
+ return 0;
+ }
+
+ task->major = dmt->major;
+ task->minor = dmt->minor;
+ task->uid = dmt->uid;
+ task->gid = dmt->gid;
+ task->mode = dmt->mode;
+ /* FIXME: Just for udev_check in dm_task_run. Can we avoid this? */
+ task->event_nr = dmt->event_nr & DM_UDEV_FLAGS_MASK;
+ task->cookie_set = dmt->cookie_set;
+
+ r = dm_task_run(task);
+ dm_task_destroy(task);
+ if (!r) {
+ _udev_complete(dmt);
+ return 0;
+ }
+
+ /* Next load the table */
+ if (!(task = dm_task_create(DM_DEVICE_RELOAD))) {
+ log_error("Failed to create device-mapper task struct");
+ _udev_complete(dmt);
+ r = 0;
+ goto revert;
+ }
+
+ /* Copy across relevant fields */
+ if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name)) {
+ dm_task_destroy(task);
+ _udev_complete(dmt);
+ r = 0;
+ goto revert;
+ }
+
+ task->read_only = dmt->read_only;
+ task->head = dmt->head;
+ task->tail = dmt->tail;
+
+ r = dm_task_run(task);
+
+ task->head = NULL;
+ task->tail = NULL;
+ dm_task_destroy(task);
+ if (!r) {
+ _udev_complete(dmt);
+ goto revert;
+ }
+
+ /* Use the original structure last so the info will be correct */
+ dmt->type = DM_DEVICE_RESUME;
+ dm_free(dmt->uuid);
+ dmt->uuid = NULL;
+
+ r = dm_task_run(dmt);
+
+ if (r)
+ return r;
+
+ revert:
+ dmt->type = DM_DEVICE_REMOVE;
+ dm_free(dmt->uuid);
+ dmt->uuid = NULL;
+
+ /*
+ * Also udev-synchronize "remove" dm task that is a part of this revert!
+ * But only if the original dm task was supposed to be synchronized.
+ */
+ if (dmt->cookie_set) {
+ cookie = (dmt->event_nr & ~DM_UDEV_FLAGS_MASK) |
+ (DM_COOKIE_MAGIC << DM_UDEV_FLAGS_SHIFT);
+ if (!dm_task_set_cookie(dmt, &cookie,
+ (dmt->event_nr & DM_UDEV_FLAGS_MASK) >>
+ DM_UDEV_FLAGS_SHIFT))
+ stack; /* keep going */
+ }
+
+ if (!dm_task_run(dmt))
+ log_error("Failed to revert device creation.");
+
+ return r;
+}
+
+uint64_t dm_task_get_existing_table_size(struct dm_task *dmt)
+{
+ return dmt->existing_table_size;
+}
+
+static int _reload_with_suppression_v4(struct dm_task *dmt)
+{
+ struct dm_task *task;
+ struct target *t1, *t2;
+ int r;
+
+ /* New task to get existing table information */
+ if (!(task = dm_task_create(DM_DEVICE_TABLE))) {
+ log_error("Failed to create device-mapper task struct");
+ return 0;
+ }
+
+ /* Copy across relevant fields */
+ if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name)) {
+ dm_task_destroy(task);
+ return 0;
+ }
+
+ if (dmt->uuid && !dm_task_set_uuid(task, dmt->uuid)) {
+ dm_task_destroy(task);
+ return 0;
+ }
+
+ task->major = dmt->major;
+ task->minor = dmt->minor;
+
+ r = dm_task_run(task);
+
+ if (!r) {
+ dm_task_destroy(task);
+ return r;
+ }
+
+ /* Store existing table size */
+ t2 = task->head;
+ while (t2 && t2->next)
+ t2 = t2->next;
+ dmt->existing_table_size = t2 ? t2->start + t2->length : 0;
+
+ if ((task->dmi.v4->flags & DM_READONLY_FLAG) ? 1 : 0 != dmt->read_only)
+ goto no_match;
+
+ t1 = dmt->head;
+ t2 = task->head;
+
+ while (t1 && t2) {
+ while (t2->params[strlen(t2->params) - 1] == ' ')
+ t2->params[strlen(t2->params) - 1] = '\0';
+ if ((t1->start != t2->start) ||
+ (t1->length != t2->length) ||
+ (strcmp(t1->type, t2->type)) ||
+ (strcmp(t1->params, t2->params)))
+ goto no_match;
+ t1 = t1->next;
+ t2 = t2->next;
+ }
+
+ if (!t1 && !t2) {
+ dmt->dmi.v4 = task->dmi.v4;
+ task->dmi.v4 = NULL;
+ dm_task_destroy(task);
+ return 1;
+ }
+
+no_match:
+ dm_task_destroy(task);
+
+ /* Now do the original reload */
+ dmt->suppress_identical_reload = 0;
+ r = dm_task_run(dmt);
+
+ return r;
+}
+
+static const char *_sanitise_message(char *message)
+{
+ const char *sanitised_message = message ?: "";
+
+ /* FIXME: Check for whitespace variations. */
+ /* This traps what cryptsetup sends us. */
+ if (message && !strncasecmp(message, "key set", 7))
+ sanitised_message = "key set";
+
+ return sanitised_message;
+}
+
+static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command,
+ unsigned repeat_count)
+{
+ struct dm_ioctl *dmi;
+ int ioctl_with_uevent;
+
+ dmi = _flatten(dmt, repeat_count);
+ if (!dmi) {
+ log_error("Couldn't create ioctl argument.");
+ return NULL;
+ }
+
+ if (dmt->type == DM_DEVICE_TABLE)
+ dmi->flags |= DM_STATUS_TABLE_FLAG;
+
+ dmi->flags |= DM_EXISTS_FLAG; /* FIXME */
+
+ if (dmt->no_open_count)
+ dmi->flags |= DM_SKIP_BDGET_FLAG;
+
+ ioctl_with_uevent = dmt->type == DM_DEVICE_RESUME ||
+ dmt->type == DM_DEVICE_REMOVE ||
+ dmt->type == DM_DEVICE_RENAME;
+
+ if (ioctl_with_uevent && dm_cookie_supported()) {
+ /*
+ * Always mark events coming from libdevmapper as
+ * "primary sourced". This is needed to distinguish
+ * any spurious events so we can act appropriately.
+ * This needs to be applied even when udev_sync is
+ * not used because udev flags could be used alone.
+ */
+ dmi->event_nr |= DM_UDEV_PRIMARY_SOURCE_FLAG <<
+ DM_UDEV_FLAGS_SHIFT;
+
+ /*
+ * Prevent udev vs. libdevmapper race when processing nodes
+ * and symlinks. This can happen when the udev rules are
+ * installed and udev synchronisation code is enabled in
+ * libdevmapper but the software using libdevmapper does not
+ * make use of it (by not calling dm_task_set_cookie before).
+ * We need to instruct the udev rules not to be applied at
+ * all in this situation so we can gracefully fallback to
+ * libdevmapper's node and symlink creation code.
+ */
+ if (!dmt->cookie_set && dm_udev_get_sync_support()) {
+ log_debug("Cookie value is not set while trying to call %s "
+ "ioctl. Please, consider using libdevmapper's udev "
+ "synchronisation interface or disable it explicitly "
+ "by calling dm_udev_set_sync_support(0).",
+ dmt->type == DM_DEVICE_RESUME ? "DM_DEVICE_RESUME" :
+ dmt->type == DM_DEVICE_REMOVE ? "DM_DEVICE_REMOVE" :
+ "DM_DEVICE_RENAME");
+ log_debug("Switching off device-mapper and all subsystem related "
+ "udev rules. Falling back to libdevmapper node creation.");
+ /*
+ * Disable general dm and subsystem rules but keep
+ * dm disk rules if not flagged out explicitly before.
+ * We need /dev/disk content for the software that expects it.
+ */
+ dmi->event_nr |= (DM_UDEV_DISABLE_DM_RULES_FLAG |
+ DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG) <<
+ DM_UDEV_FLAGS_SHIFT;
+ }
+ }
+
+ log_debug("dm %s %s%s %s%s%s %s%.0d%s%.0d%s"
+ "%s%c%c%s%s %.0" PRIu64 " %s [%u]",
+ _cmd_data_v4[dmt->type].name,
+ dmt->new_uuid ? "UUID " : "",
+ dmi->name, dmi->uuid, dmt->newname ? " " : "",
+ dmt->newname ? dmt->newname : "",
+ dmt->major > 0 ? "(" : "",
+ dmt->major > 0 ? dmt->major : 0,
+ dmt->major > 0 ? ":" : "",
+ dmt->minor > 0 ? dmt->minor : 0,
+ dmt->major > 0 && dmt->minor == 0 ? "0" : "",
+ dmt->major > 0 ? ") " : "",
+ dmt->no_open_count ? 'N' : 'O',
+ dmt->no_flush ? 'N' : 'F',
+ dmt->skip_lockfs ? "S " : "",
+ dmt->query_inactive_table ? "I " : "",
+ dmt->sector, _sanitise_message(dmt->message),
+ dmi->data_size);
+#ifdef DM_IOCTLS
+ if (ioctl(_control_fd, command, dmi) < 0) {
+ if (errno == ENXIO && ((dmt->type == DM_DEVICE_INFO) ||
+ (dmt->type == DM_DEVICE_MKNODES) ||
+ (dmt->type == DM_DEVICE_STATUS)))
+ dmi->flags &= ~DM_EXISTS_FLAG; /* FIXME */
+ else {
+ if (_log_suppress)
+ log_verbose("device-mapper: %s ioctl "
+ "failed: %s",
+ _cmd_data_v4[dmt->type].name,
+ strerror(errno));
+ else
+ log_error("device-mapper: %s ioctl "
+ "failed: %s",
+ _cmd_data_v4[dmt->type].name,
+ strerror(errno));
+ _dm_zfree_dmi(dmi);
+ return NULL;
+ }
+ }
+
+ if (ioctl_with_uevent && !_check_uevent_generated(dmi))
+ _udev_complete(dmt);
+
+#else /* Userspace alternative for testing */
+#endif
+ return dmi;
+}
+
+void dm_task_update_nodes(void)
+{
+ update_devs();
+}
+
+int dm_task_run(struct dm_task *dmt)
+{
+ struct dm_ioctl *dmi;
+ unsigned command;
+ int check_udev;
+ int udev_only;
+
+#ifdef DM_COMPAT
+ if (_dm_version == 1)
+ return _dm_task_run_v1(dmt);
+#endif
+
+ if ((unsigned) dmt->type >=
+ (sizeof(_cmd_data_v4) / sizeof(*_cmd_data_v4))) {
+ log_error(INTERNAL_ERROR "unknown device-mapper task %d",
+ dmt->type);
+ return 0;
+ }
+
+ command = _cmd_data_v4[dmt->type].cmd;
+
+ /* Old-style creation had a table supplied */
+ if (dmt->type == DM_DEVICE_CREATE && dmt->head)
+ return _create_and_load_v4(dmt);
+
+ if (dmt->type == DM_DEVICE_MKNODES && !dmt->dev_name &&
+ !dmt->uuid && dmt->major <= 0)
+ return _mknodes_v4(dmt);
+
+ if ((dmt->type == DM_DEVICE_RELOAD) && dmt->suppress_identical_reload)
+ return _reload_with_suppression_v4(dmt);
+
+ if (!_open_control()) {
+ _udev_complete(dmt);
+ return 0;
+ }
+
+ /* FIXME Detect and warn if cookie set but should not be. */
+repeat_ioctl:
+ if (!(dmi = _do_dm_ioctl(dmt, command, _ioctl_buffer_double_factor))) {
+ _udev_complete(dmt);
+ return 0;
+ }
+
+ if (dmi->flags & DM_BUFFER_FULL_FLAG) {
+ switch (dmt->type) {
+ case DM_DEVICE_LIST_VERSIONS:
+ case DM_DEVICE_LIST:
+ case DM_DEVICE_DEPS:
+ case DM_DEVICE_STATUS:
+ case DM_DEVICE_TABLE:
+ case DM_DEVICE_WAITEVENT:
+ _ioctl_buffer_double_factor++;
+ _dm_zfree_dmi(dmi);
+ goto repeat_ioctl;
+ default:
+ log_error("WARNING: libdevmapper buffer too small for data");
+ }
+ }
+
+ check_udev = dmt->cookie_set &&
+ !(dmt->event_nr >> DM_UDEV_FLAGS_SHIFT &
+ DM_UDEV_DISABLE_DM_RULES_FLAG);
+
+ udev_only = dmt->cookie_set ? (dmt->event_nr >> DM_UDEV_FLAGS_SHIFT &
+ DM_UDEV_DISABLE_LIBRARY_FALLBACK) : 0;
+
+ switch (dmt->type) {
+ case DM_DEVICE_CREATE:
+ if (dmt->dev_name && *dmt->dev_name && !udev_only)
+ add_dev_node(dmt->dev_name, MAJOR(dmi->dev),
+ MINOR(dmi->dev), dmt->uid, dmt->gid,
+ dmt->mode, check_udev);
+ break;
+ case DM_DEVICE_REMOVE:
+ /* FIXME Kernel needs to fill in dmi->name */
+ if (dmt->dev_name && !udev_only)
+ rm_dev_node(dmt->dev_name, check_udev);
+ break;
+
+ case DM_DEVICE_RENAME:
+ /* FIXME Kernel needs to fill in dmi->name */
+ if (!dmt->new_uuid && dmt->dev_name && !udev_only)
+ rename_dev_node(dmt->dev_name, dmt->newname,
+ check_udev);
+ break;
+
+ case DM_DEVICE_RESUME:
+ /* FIXME Kernel needs to fill in dmi->name */
+ set_dev_node_read_ahead(dmt->dev_name, dmt->read_ahead,
+ dmt->read_ahead_flags);
+ break;
+
+ case DM_DEVICE_MKNODES:
+ if (dmi->flags & DM_EXISTS_FLAG)
+ add_dev_node(dmi->name, MAJOR(dmi->dev),
+ MINOR(dmi->dev), dmt->uid,
+ dmt->gid, dmt->mode, 0);
+ else if (dmt->dev_name)
+ rm_dev_node(dmt->dev_name, 0);
+ break;
+
+ case DM_DEVICE_STATUS:
+ case DM_DEVICE_TABLE:
+ case DM_DEVICE_WAITEVENT:
+ if (!_unmarshal_status(dmt, dmi))
+ goto bad;
+ break;
+ }
+
+ /* Was structure reused? */
+ _dm_zfree_dmi(dmt->dmi.v4);
+ dmt->dmi.v4 = dmi;
+ return 1;
+
+ bad:
+ _dm_zfree_dmi(dmi);
+ return 0;
+}
+
+void dm_lib_release(void)
+{
+ _close_control_fd();
+ update_devs();
+}
+
+void dm_pools_check_leaks(void);
+
+void dm_lib_exit(void)
+{
+ dm_lib_release();
+ selinux_release();
+ if (_dm_bitset)
+ dm_bitset_destroy(_dm_bitset);
+ _dm_bitset = NULL;
+ dm_pools_check_leaks();
+ dm_dump_memory();
+ _version_ok = 1;
+ _version_checked = 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIB_DMTARGETS_H
+#define LIB_DMTARGETS_H
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+struct dm_ioctl;
+struct dm_ioctl_v1;
+
+struct target {
+ uint64_t start;
+ uint64_t length;
+ char *type;
+ char *params;
+
+ struct target *next;
+};
+
+struct dm_task {
+ int type;
+ char *dev_name;
+
+ struct target *head, *tail;
+
+ int read_only;
+ uint32_t event_nr;
+ int major;
+ int minor;
+ int allow_default_major_fallback;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ uint32_t read_ahead;
+ uint32_t read_ahead_flags;
+ union {
+ struct dm_ioctl *v4;
+ struct dm_ioctl_v1 *v1;
+ } dmi;
+ char *newname;
+ char *message;
+ char *geometry;
+ uint64_t sector;
+ int no_flush;
+ int no_open_count;
+ int skip_lockfs;
+ int query_inactive_table;
+ int suppress_identical_reload;
+ uint64_t existing_table_size;
+ int cookie_set;
+ int new_uuid;
+
+ char *uuid;
+};
+
+struct cmd_data {
+ const char *name;
+ const int cmd;
+ const int version[3];
+};
+
+int dm_check_version(void);
+uint64_t dm_task_get_existing_table_size(struct dm_task *dmt);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIB_DEVICE_MAPPER_H
+#define LIB_DEVICE_MAPPER_H
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <sys/types.h>
+
+#ifdef linux
+# include <linux/types.h>
+#endif
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifndef __GNUC__
+# define __typeof__ typeof
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************
+ * The first section of this file provides direct access to the
+ * individual device-mapper ioctls. Since it is quite laborious to
+ * build the ioctl arguments for the device-mapper, people are
+ * encouraged to use this library.
+ ****************************************************************/
+
+/*
+ * The library user may wish to register their own
+ * logging function. By default errors go to stderr.
+ * Use dm_log_with_errno_init(NULL) to restore the default log fn.
+ */
+
+typedef void (*dm_log_with_errno_fn) (int level, const char *file, int line,
+ int dm_errno, const char *f, ...)
+ __attribute__ ((format(printf, 5, 6)));
+
+void dm_log_with_errno_init(dm_log_with_errno_fn fn);
+void dm_log_init_verbose(int level);
+
+/*
+ * Original version of this function.
+ * dm_errno is set to 0.
+ *
+ * Deprecated: Use the _with_errno_ versions above instead.
+ */
+typedef void (*dm_log_fn) (int level, const char *file, int line,
+ const char *f, ...)
+ __attribute__ ((format(printf, 4, 5)));
+
+void dm_log_init(dm_log_fn fn);
+/*
+ * For backward-compatibility, indicate that dm_log_init() was used
+ * to set a non-default value of dm_log().
+ */
+int dm_log_is_non_default(void);
+
+enum {
+ DM_DEVICE_CREATE,
+ DM_DEVICE_RELOAD,
+ DM_DEVICE_REMOVE,
+ DM_DEVICE_REMOVE_ALL,
+
+ DM_DEVICE_SUSPEND,
+ DM_DEVICE_RESUME,
+
+ DM_DEVICE_INFO,
+ DM_DEVICE_DEPS,
+ DM_DEVICE_RENAME,
+
+ DM_DEVICE_VERSION,
+
+ DM_DEVICE_STATUS,
+ DM_DEVICE_TABLE,
+ DM_DEVICE_WAITEVENT,
+
+ DM_DEVICE_LIST,
+
+ DM_DEVICE_CLEAR,
+
+ DM_DEVICE_MKNODES,
+
+ DM_DEVICE_LIST_VERSIONS,
+
+ DM_DEVICE_TARGET_MSG,
+
+ DM_DEVICE_SET_GEOMETRY
+};
+
+/*
+ * You will need to build a struct dm_task for
+ * each ioctl command you want to execute.
+ */
+
+struct dm_task;
+
+struct dm_task *dm_task_create(int type);
+void dm_task_destroy(struct dm_task *dmt);
+
+int dm_task_set_name(struct dm_task *dmt, const char *name);
+int dm_task_set_uuid(struct dm_task *dmt, const char *uuid);
+
+/*
+ * Retrieve attributes after an info.
+ */
+struct dm_info {
+ int exists;
+ int suspended;
+ int live_table;
+ int inactive_table;
+ int32_t open_count;
+ uint32_t event_nr;
+ uint32_t major;
+ uint32_t minor; /* minor device number */
+ int read_only; /* 0:read-write; 1:read-only */
+
+ int32_t target_count;
+};
+
+struct dm_deps {
+ uint32_t count;
+ uint32_t filler;
+ uint64_t device[0];
+};
+
+struct dm_names {
+ uint64_t dev;
+ uint32_t next; /* Offset to next struct from start of this struct */
+ char name[0];
+};
+
+struct dm_versions {
+ uint32_t next; /* Offset to next struct from start of this struct */
+ uint32_t version[3];
+
+ char name[0];
+};
+
+int dm_get_library_version(char *version, size_t size);
+int dm_task_get_driver_version(struct dm_task *dmt, char *version, size_t size);
+int dm_task_get_info(struct dm_task *dmt, struct dm_info *dmi);
+const char *dm_task_get_name(const struct dm_task *dmt);
+const char *dm_task_get_uuid(const struct dm_task *dmt);
+
+struct dm_deps *dm_task_get_deps(struct dm_task *dmt);
+struct dm_names *dm_task_get_names(struct dm_task *dmt);
+struct dm_versions *dm_task_get_versions(struct dm_task *dmt);
+
+int dm_task_set_ro(struct dm_task *dmt);
+int dm_task_set_newname(struct dm_task *dmt, const char *newname);
+int dm_task_set_newuuid(struct dm_task *dmt, const char *newuuid);
+int dm_task_set_minor(struct dm_task *dmt, int minor);
+int dm_task_set_major(struct dm_task *dmt, int major);
+int dm_task_set_major_minor(struct dm_task *dmt, int major, int minor, int allow_default_major_fallback);
+int dm_task_set_uid(struct dm_task *dmt, uid_t uid);
+int dm_task_set_gid(struct dm_task *dmt, gid_t gid);
+int dm_task_set_mode(struct dm_task *dmt, mode_t mode);
+int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t flags);
+int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr);
+int dm_task_set_geometry(struct dm_task *dmt, const char *cylinders, const char *heads, const char *sectors, const char *start);
+int dm_task_set_message(struct dm_task *dmt, const char *message);
+int dm_task_set_sector(struct dm_task *dmt, uint64_t sector);
+int dm_task_no_flush(struct dm_task *dmt);
+int dm_task_no_open_count(struct dm_task *dmt);
+int dm_task_skip_lockfs(struct dm_task *dmt);
+int dm_task_query_inactive_table(struct dm_task *dmt);
+int dm_task_suppress_identical_reload(struct dm_task *dmt);
+
+/*
+ * Control read_ahead.
+ */
+#define DM_READ_AHEAD_AUTO UINT32_MAX /* Use kernel default readahead */
+#define DM_READ_AHEAD_NONE 0 /* Disable readahead */
+
+#define DM_READ_AHEAD_MINIMUM_FLAG 0x1 /* Value supplied is minimum */
+
+/*
+ * Read ahead is set with DM_DEVICE_CREATE with a table or DM_DEVICE_RESUME.
+ */
+int dm_task_set_read_ahead(struct dm_task *dmt, uint32_t read_ahead,
+ uint32_t read_ahead_flags);
+uint32_t dm_task_get_read_ahead(const struct dm_task *dmt,
+ uint32_t *read_ahead);
+
+/*
+ * Use these to prepare for a create or reload.
+ */
+int dm_task_add_target(struct dm_task *dmt,
+ uint64_t start,
+ uint64_t size, const char *ttype, const char *params);
+
+/*
+ * Format major/minor numbers correctly for input to driver.
+ */
+#define DM_FORMAT_DEV_BUFSIZE 13 /* Minimum bufsize to handle worst case. */
+int dm_format_dev(char *buf, int bufsize, uint32_t dev_major, uint32_t dev_minor);
+
+/* Use this to retrive target information returned from a STATUS call */
+void *dm_get_next_target(struct dm_task *dmt,
+ void *next, uint64_t *start, uint64_t *length,
+ char **target_type, char **params);
+
+/*
+ * Call this to actually run the ioctl.
+ */
+int dm_task_run(struct dm_task *dmt);
+
+/*
+ * Call this to make or remove the device nodes associated with previously
+ * issued commands.
+ */
+void dm_task_update_nodes(void);
+
+/*
+ * Configure the device-mapper directory
+ */
+int dm_set_dev_dir(const char *dir);
+const char *dm_dir(void);
+
+/*
+ * Determine whether a major number belongs to device-mapper or not.
+ */
+int dm_is_dm_major(uint32_t major);
+
+/*
+ * Release library resources
+ */
+void dm_lib_release(void);
+void dm_lib_exit(void) __attribute__((destructor));
+
+/*
+ * Use NULL for all devices.
+ */
+int dm_mknodes(const char *name);
+int dm_driver_version(char *version, size_t size);
+
+/******************************************************
+ * Functions to build and manipulate trees of devices *
+ ******************************************************/
+struct dm_tree;
+struct dm_tree_node;
+
+/*
+ * Initialise an empty dependency tree.
+ *
+ * The tree consists of a root node together with one node for each mapped
+ * device which has child nodes for each device referenced in its table.
+ *
+ * Every node in the tree has one or more children and one or more parents.
+ *
+ * The root node is the parent/child of every node that doesn't have other
+ * parents/children.
+ */
+struct dm_tree *dm_tree_create(void);
+void dm_tree_free(struct dm_tree *tree);
+
+/*
+ * Add nodes to the tree for a given device and all the devices it uses.
+ */
+int dm_tree_add_dev(struct dm_tree *tree, uint32_t major, uint32_t minor);
+int dm_tree_add_dev_with_udev_flags(struct dm_tree *tree, uint32_t major,
+ uint32_t minor, uint16_t udev_flags);
+
+/*
+ * Add a new node to the tree if it doesn't already exist.
+ */
+struct dm_tree_node *dm_tree_add_new_dev(struct dm_tree *tree,
+ const char *name,
+ const char *uuid,
+ uint32_t major, uint32_t minor,
+ int read_only,
+ int clear_inactive,
+ void *context);
+struct dm_tree_node *dm_tree_add_new_dev_with_udev_flags(struct dm_tree *tree,
+ const char *name,
+ const char *uuid,
+ uint32_t major,
+ uint32_t minor,
+ int read_only,
+ int clear_inactive,
+ void *context,
+ uint16_t udev_flags);
+
+/*
+ * Search for a node in the tree.
+ * Set major and minor to 0 or uuid to NULL to get the root node.
+ */
+struct dm_tree_node *dm_tree_find_node(struct dm_tree *tree,
+ uint32_t major,
+ uint32_t minor);
+struct dm_tree_node *dm_tree_find_node_by_uuid(struct dm_tree *tree,
+ const char *uuid);
+
+/*
+ * Use this to walk through all children of a given node.
+ * Set handle to NULL in first call.
+ * Returns NULL after the last child.
+ * Set inverted to use inverted tree.
+ */
+struct dm_tree_node *dm_tree_next_child(void **handle,
+ const struct dm_tree_node *parent,
+ uint32_t inverted);
+
+/*
+ * Get properties of a node.
+ */
+const char *dm_tree_node_get_name(const struct dm_tree_node *node);
+const char *dm_tree_node_get_uuid(const struct dm_tree_node *node);
+const struct dm_info *dm_tree_node_get_info(const struct dm_tree_node *node);
+void *dm_tree_node_get_context(const struct dm_tree_node *node);
+int dm_tree_node_size_changed(const struct dm_tree_node *dnode);
+
+/*
+ * Returns the number of children of the given node (excluding the root node).
+ * Set inverted for the number of parents.
+ */
+int dm_tree_node_num_children(const struct dm_tree_node *node, uint32_t inverted);
+
+/*
+ * Deactivate a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_deactivate_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+/*
+ * Preload/create a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_preload_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+
+/*
+ * Resume a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_activate_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+
+/*
+ * Suspend a device plus all dependencies.
+ * Ignores devices that don't have a uuid starting with uuid_prefix.
+ */
+int dm_tree_suspend_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+
+/*
+ * Skip the filesystem sync when suspending.
+ * Does nothing with other functions.
+ * Use this when no snapshots are involved.
+ */
+void dm_tree_skip_lockfs(struct dm_tree_node *dnode);
+
+/*
+ * Set the 'noflush' flag when suspending devices.
+ * If the kernel supports it, instead of erroring outstanding I/O that
+ * cannot be completed, the I/O is queued and resubmitted when the
+ * device is resumed. This affects multipath devices when all paths
+ * have failed and queue_if_no_path is set, and mirror devices when
+ * block_on_error is set and the mirror log has failed.
+ */
+void dm_tree_use_no_flush_suspend(struct dm_tree_node *dnode);
+
+/*
+ * Is the uuid prefix present in the tree?
+ * Only returns 0 if every node was checked successfully.
+ * Returns 1 if the tree walk has to be aborted.
+ */
+int dm_tree_children_use_uuid(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len);
+
+/*
+ * Construct tables for new nodes before activating them.
+ */
+int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode,
+ uint64_t size,
+ const char *origin_uuid);
+int dm_tree_node_add_snapshot_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *origin_uuid,
+ const char *cow_uuid,
+ int persistent,
+ uint32_t chunk_size);
+int dm_tree_node_add_snapshot_merge_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *origin_uuid,
+ const char *cow_uuid,
+ const char *merge_uuid,
+ uint32_t chunk_size);
+int dm_tree_node_add_error_target(struct dm_tree_node *node,
+ uint64_t size);
+int dm_tree_node_add_zero_target(struct dm_tree_node *node,
+ uint64_t size);
+int dm_tree_node_add_linear_target(struct dm_tree_node *node,
+ uint64_t size);
+int dm_tree_node_add_striped_target(struct dm_tree_node *node,
+ uint64_t size,
+ uint32_t stripe_size);
+
+#define DM_CRYPT_IV_DEFAULT UINT64_C(-1) /* iv_offset == seg offset */
+/*
+ * Function accepts one string in cipher specification
+ * (chainmode and iv should be NULL because included in cipher string)
+ * or
+ * separate arguments which will be joined to "cipher-chainmode-iv"
+ */
+int dm_tree_node_add_crypt_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *cipher,
+ const char *chainmode,
+ const char *iv,
+ uint64_t iv_offset,
+ const char *key);
+int dm_tree_node_add_mirror_target(struct dm_tree_node *node,
+ uint64_t size);
+
+/* Mirror log flags */
+#define DM_NOSYNC 0x00000001 /* Known already in sync */
+#define DM_FORCESYNC 0x00000002 /* Force resync */
+#define DM_BLOCK_ON_ERROR 0x00000004 /* On error, suspend I/O */
+#define DM_CORELOG 0x00000008 /* In-memory log */
+
+int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node,
+ uint32_t region_size,
+ unsigned clustered,
+ const char *log_uuid,
+ unsigned area_count,
+ uint32_t flags);
+
+/*
+ * Replicator operation mode
+ * Note: API for Replicator is not yet stable
+ */
+typedef enum {
+ DM_REPLICATOR_SYNC, /* Synchronous replication */
+ DM_REPLICATOR_ASYNC_WARN, /* Warn if async replicator is slow */
+ DM_REPLICATOR_ASYNC_STALL, /* Stall replicator if not fast enough */
+ DM_REPLICATOR_ASYNC_DROP, /* Drop sites out of sync */
+ DM_REPLICATOR_ASYNC_FAIL, /* Fail replicator if slow */
+ NUM_DM_REPLICATOR_MODES
+} dm_replicator_mode_t;
+
+int dm_tree_node_add_replicator_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *rlog_uuid,
+ const char *rlog_type,
+ unsigned rsite_index,
+ dm_replicator_mode_t mode,
+ uint32_t async_timeout,
+ uint64_t fall_behind_data,
+ uint32_t fall_behind_ios);
+
+int dm_tree_node_add_replicator_dev_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *replicator_uuid, /* Replicator control device */
+ uint64_t rdevice_index,
+ const char *rdev_uuid, /* Rimage device name/uuid */
+ unsigned rsite_index,
+ const char *slog_uuid,
+ uint32_t slog_flags, /* Mirror log flags */
+ uint32_t slog_region_size);
+/* End of Replicator API */
+
+void dm_tree_node_set_presuspend_node(struct dm_tree_node *node,
+ struct dm_tree_node *presuspend_node);
+
+int dm_tree_node_add_target_area(struct dm_tree_node *node,
+ const char *dev_name,
+ const char *dlid,
+ uint64_t offset);
+
+/*
+ * Set readahead (in sectors) after loading the node.
+ */
+void dm_tree_node_set_read_ahead(struct dm_tree_node *dnode,
+ uint32_t read_ahead,
+ uint32_t read_ahead_flags);
+
+void dm_tree_set_cookie(struct dm_tree_node *node, uint32_t cookie);
+uint32_t dm_tree_get_cookie(struct dm_tree_node *node);
+
+/*****************************************************************************
+ * Library functions
+ *****************************************************************************/
+
+/*******************
+ * Memory management
+ *******************/
+
+void *dm_malloc_aux(size_t s, const char *file, int line);
+void *dm_malloc_aux_debug(size_t s, const char *file, int line);
+void *dm_zalloc_aux(size_t s, const char *file, int line);
+void *dm_zalloc_aux_debug(size_t s, const char *file, int line);
+char *dm_strdup_aux(const char *str, const char *file, int line);
+void dm_free_aux(void *p);
+void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line);
+int dm_dump_memory_debug(void);
+void dm_bounds_check_debug(void);
+
+#ifdef DEBUG_MEM
+
+# define dm_malloc(s) dm_malloc_aux_debug((s), __FILE__, __LINE__)
+# define dm_zalloc(s) dm_zalloc_aux_debug((s), __FILE__, __LINE__)
+# define dm_strdup(s) dm_strdup_aux((s), __FILE__, __LINE__)
+# define dm_free(p) dm_free_aux(p)
+# define dm_realloc(p, s) dm_realloc_aux(p, s, __FILE__, __LINE__)
+# define dm_dump_memory() dm_dump_memory_debug()
+# define dm_bounds_check() dm_bounds_check_debug()
+
+#else
+
+# define dm_malloc(s) dm_malloc_aux((s), __FILE__, __LINE__)
+# define dm_zalloc(s) dm_zalloc_aux((s), __FILE__, __LINE__)
+# define dm_strdup(s) strdup(s)
+# define dm_free(p) free(p)
+# define dm_realloc(p, s) realloc(p, s)
+# define dm_dump_memory() {}
+# define dm_bounds_check() {}
+
+#endif
+
+
+/*
+ * The pool allocator is useful when you are going to allocate
+ * lots of memory, use the memory for a bit, and then free the
+ * memory in one go. A surprising amount of code has this usage
+ * profile.
+ *
+ * You should think of the pool as an infinite, contiguous chunk
+ * of memory. The front of this chunk of memory contains
+ * allocated objects, the second half is free. dm_pool_alloc grabs
+ * the next 'size' bytes from the free half, in effect moving it
+ * into the allocated half. This operation is very efficient.
+ *
+ * dm_pool_free frees the allocated object *and* all objects
+ * allocated after it. It is important to note this semantic
+ * difference from malloc/free. This is also extremely
+ * efficient, since a single dm_pool_free can dispose of a large
+ * complex object.
+ *
+ * dm_pool_destroy frees all allocated memory.
+ *
+ * eg, If you are building a binary tree in your program, and
+ * know that you are only ever going to insert into your tree,
+ * and not delete (eg, maintaining a symbol table for a
+ * compiler). You can create yourself a pool, allocate the nodes
+ * from it, and when the tree becomes redundant call dm_pool_destroy
+ * (no nasty iterating through the tree to free nodes).
+ *
+ * eg, On the other hand if you wanted to repeatedly insert and
+ * remove objects into the tree, you would be better off
+ * allocating the nodes from a free list; you cannot free a
+ * single arbitrary node with pool.
+ */
+
+struct dm_pool;
+
+/* constructor and destructor */
+struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint);
+void dm_pool_destroy(struct dm_pool *p);
+
+/* simple allocation/free routines */
+void *dm_pool_alloc(struct dm_pool *p, size_t s);
+void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment);
+void dm_pool_empty(struct dm_pool *p);
+void dm_pool_free(struct dm_pool *p, void *ptr);
+
+/*
+ * Object building routines:
+ *
+ * These allow you to 'grow' an object, useful for
+ * building strings, or filling in dynamic
+ * arrays.
+ *
+ * It's probably best explained with an example:
+ *
+ * char *build_string(struct dm_pool *mem)
+ * {
+ * int i;
+ * char buffer[16];
+ *
+ * if (!dm_pool_begin_object(mem, 128))
+ * return NULL;
+ *
+ * for (i = 0; i < 50; i++) {
+ * snprintf(buffer, sizeof(buffer), "%d, ", i);
+ * if (!dm_pool_grow_object(mem, buffer, 0))
+ * goto bad;
+ * }
+ *
+ * // add null
+ * if (!dm_pool_grow_object(mem, "\0", 1))
+ * goto bad;
+ *
+ * return dm_pool_end_object(mem);
+ *
+ * bad:
+ *
+ * dm_pool_abandon_object(mem);
+ * return NULL;
+ *}
+ *
+ * So start an object by calling dm_pool_begin_object
+ * with a guess at the final object size - if in
+ * doubt make the guess too small.
+ *
+ * Then append chunks of data to your object with
+ * dm_pool_grow_object. Finally get your object with
+ * a call to dm_pool_end_object.
+ *
+ * Setting delta to 0 means it will use strlen(extra).
+ */
+int dm_pool_begin_object(struct dm_pool *p, size_t hint);
+int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta);
+void *dm_pool_end_object(struct dm_pool *p);
+void dm_pool_abandon_object(struct dm_pool *p);
+
+/* utilities */
+char *dm_pool_strdup(struct dm_pool *p, const char *str);
+char *dm_pool_strndup(struct dm_pool *p, const char *str, size_t n);
+void *dm_pool_zalloc(struct dm_pool *p, size_t s);
+
+/******************
+ * bitset functions
+ ******************/
+
+typedef uint32_t *dm_bitset_t;
+
+dm_bitset_t dm_bitset_create(struct dm_pool *mem, unsigned num_bits);
+void dm_bitset_destroy(dm_bitset_t bs);
+
+int dm_bitset_equal(dm_bitset_t in1, dm_bitset_t in2);
+
+void dm_bit_and(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2);
+void dm_bit_union(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2);
+int dm_bit_get_first(dm_bitset_t bs);
+int dm_bit_get_next(dm_bitset_t bs, int last_bit);
+
+#define DM_BITS_PER_INT (sizeof(int) * CHAR_BIT)
+
+#define dm_bit(bs, i) \
+ ((bs)[((i) / DM_BITS_PER_INT) + 1] & (0x1 << ((i) & (DM_BITS_PER_INT - 1))))
+
+#define dm_bit_set(bs, i) \
+ ((bs)[((i) / DM_BITS_PER_INT) + 1] |= (0x1 << ((i) & (DM_BITS_PER_INT - 1))))
+
+#define dm_bit_clear(bs, i) \
+ ((bs)[((i) / DM_BITS_PER_INT) + 1] &= ~(0x1 << ((i) & (DM_BITS_PER_INT - 1))))
+
+#define dm_bit_set_all(bs) \
+ memset((bs) + 1, -1, ((*(bs) / DM_BITS_PER_INT) + 1) * sizeof(int))
+
+#define dm_bit_clear_all(bs) \
+ memset((bs) + 1, 0, ((*(bs) / DM_BITS_PER_INT) + 1) * sizeof(int))
+
+#define dm_bit_copy(bs1, bs2) \
+ memcpy((bs1) + 1, (bs2) + 1, ((*(bs1) / DM_BITS_PER_INT) + 1) * sizeof(int))
+
+/* Returns number of set bits */
+static inline unsigned hweight32(uint32_t i)
+{
+ unsigned r = (i & 0x55555555) + ((i >> 1) & 0x55555555);
+
+ r = (r & 0x33333333) + ((r >> 2) & 0x33333333);
+ r = (r & 0x0F0F0F0F) + ((r >> 4) & 0x0F0F0F0F);
+ r = (r & 0x00FF00FF) + ((r >> 8) & 0x00FF00FF);
+ return (r & 0x0000FFFF) + ((r >> 16) & 0x0000FFFF);
+}
+
+/****************
+ * hash functions
+ ****************/
+
+struct dm_hash_table;
+struct dm_hash_node;
+
+typedef void (*dm_hash_iterate_fn) (void *data);
+
+struct dm_hash_table *dm_hash_create(unsigned size_hint);
+void dm_hash_destroy(struct dm_hash_table *t);
+void dm_hash_wipe(struct dm_hash_table *t);
+
+void *dm_hash_lookup(struct dm_hash_table *t, const char *key);
+int dm_hash_insert(struct dm_hash_table *t, const char *key, void *data);
+void dm_hash_remove(struct dm_hash_table *t, const char *key);
+
+void *dm_hash_lookup_binary(struct dm_hash_table *t, const char *key, uint32_t len);
+int dm_hash_insert_binary(struct dm_hash_table *t, const char *key, uint32_t len,
+ void *data);
+void dm_hash_remove_binary(struct dm_hash_table *t, const char *key, uint32_t len);
+
+unsigned dm_hash_get_num_entries(struct dm_hash_table *t);
+void dm_hash_iter(struct dm_hash_table *t, dm_hash_iterate_fn f);
+
+char *dm_hash_get_key(struct dm_hash_table *t, struct dm_hash_node *n);
+void *dm_hash_get_data(struct dm_hash_table *t, struct dm_hash_node *n);
+struct dm_hash_node *dm_hash_get_first(struct dm_hash_table *t);
+struct dm_hash_node *dm_hash_get_next(struct dm_hash_table *t, struct dm_hash_node *n);
+
+#define dm_hash_iterate(v, h) \
+ for (v = dm_hash_get_first((h)); v; \
+ v = dm_hash_get_next((h), v))
+
+/****************
+ * list functions
+ ****************/
+
+/*
+ * A list consists of a list head plus elements.
+ * Each element has 'next' and 'previous' pointers.
+ * The list head's pointers point to the first and the last element.
+ */
+
+struct dm_list {
+ struct dm_list *n, *p;
+};
+
+/*
+ * Initialise a list before use.
+ * The list head's next and previous pointers point back to itself.
+ */
+#define DM_LIST_INIT(name) struct dm_list name = { &(name), &(name) }
+void dm_list_init(struct dm_list *head);
+
+/*
+ * Insert an element before 'head'.
+ * If 'head' is the list head, this adds an element to the end of the list.
+ */
+void dm_list_add(struct dm_list *head, struct dm_list *elem);
+
+/*
+ * Insert an element after 'head'.
+ * If 'head' is the list head, this adds an element to the front of the list.
+ */
+void dm_list_add_h(struct dm_list *head, struct dm_list *elem);
+
+/*
+ * Delete an element from its list.
+ * Note that this doesn't change the element itself - it may still be safe
+ * to follow its pointers.
+ */
+void dm_list_del(struct dm_list *elem);
+
+/*
+ * Remove an element from existing list and insert before 'head'.
+ */
+void dm_list_move(struct dm_list *head, struct dm_list *elem);
+
+/*
+ * Join 'head1' to the of 'head'.
+ */
+void dm_list_splice(struct dm_list *head, struct dm_list *head1);
+
+/*
+ * Is the list empty?
+ */
+int dm_list_empty(const struct dm_list *head);
+
+/*
+ * Is this the first element of the list?
+ */
+int dm_list_start(const struct dm_list *head, const struct dm_list *elem);
+
+/*
+ * Is this the last element of the list?
+ */
+int dm_list_end(const struct dm_list *head, const struct dm_list *elem);
+
+/*
+ * Return first element of the list or NULL if empty
+ */
+struct dm_list *dm_list_first(const struct dm_list *head);
+
+/*
+ * Return last element of the list or NULL if empty
+ */
+struct dm_list *dm_list_last(const struct dm_list *head);
+
+/*
+ * Return the previous element of the list, or NULL if we've reached the start.
+ */
+struct dm_list *dm_list_prev(const struct dm_list *head, const struct dm_list *elem);
+
+/*
+ * Return the next element of the list, or NULL if we've reached the end.
+ */
+struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *elem);
+
+/*
+ * Given the address v of an instance of 'struct dm_list' called 'head'
+ * contained in a structure of type t, return the containing structure.
+ */
+#define dm_list_struct_base(v, t, head) \
+ ((t *)((const char *)(v) - (const char *)&((t *) 0)->head))
+
+/*
+ * Given the address v of an instance of 'struct dm_list list' contained in
+ * a structure of type t, return the containing structure.
+ */
+#define dm_list_item(v, t) dm_list_struct_base((v), t, list)
+
+/*
+ * Given the address v of one known element e in a known structure of type t,
+ * return another element f.
+ */
+#define dm_struct_field(v, t, e, f) \
+ (((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->e))->f)
+
+/*
+ * Given the address v of a known element e in a known structure of type t,
+ * return the list head 'list'
+ */
+#define dm_list_head(v, t, e) dm_struct_field(v, t, e, list)
+
+/*
+ * Set v to each element of a list in turn.
+ */
+#define dm_list_iterate(v, head) \
+ for (v = (head)->n; v != head; v = v->n)
+
+/*
+ * Set v to each element in a list in turn, starting from the element
+ * in front of 'start'.
+ * You can use this to 'unwind' a list_iterate and back out actions on
+ * already-processed elements.
+ * If 'start' is 'head' it walks the list backwards.
+ */
+#define dm_list_uniterate(v, head, start) \
+ for (v = (start)->p; v != head; v = v->p)
+
+/*
+ * A safe way to walk a list and delete and free some elements along
+ * the way.
+ * t must be defined as a temporary variable of the same type as v.
+ */
+#define dm_list_iterate_safe(v, t, head) \
+ for (v = (head)->n, t = v->n; v != head; v = t, t = v->n)
+
+/*
+ * Walk a list, setting 'v' in turn to the containing structure of each item.
+ * The containing structure should be the same type as 'v'.
+ * The 'struct dm_list' variable within the containing structure is 'field'.
+ */
+#define dm_list_iterate_items_gen(v, head, field) \
+ for (v = dm_list_struct_base((head)->n, __typeof__(*v), field); \
+ &v->field != (head); \
+ v = dm_list_struct_base(v->field.n, __typeof__(*v), field))
+
+/*
+ * Walk a list, setting 'v' in turn to the containing structure of each item.
+ * The containing structure should be the same type as 'v'.
+ * The list should be 'struct dm_list list' within the containing structure.
+ */
+#define dm_list_iterate_items(v, head) dm_list_iterate_items_gen(v, (head), list)
+
+/*
+ * Walk a list, setting 'v' in turn to the containing structure of each item.
+ * The containing structure should be the same type as 'v'.
+ * The 'struct dm_list' variable within the containing structure is 'field'.
+ * t must be defined as a temporary variable of the same type as v.
+ */
+#define dm_list_iterate_items_gen_safe(v, t, head, field) \
+ for (v = dm_list_struct_base((head)->n, __typeof__(*v), field), \
+ t = dm_list_struct_base(v->field.n, __typeof__(*v), field); \
+ &v->field != (head); \
+ v = t, t = dm_list_struct_base(v->field.n, __typeof__(*v), field))
+/*
+ * Walk a list, setting 'v' in turn to the containing structure of each item.
+ * The containing structure should be the same type as 'v'.
+ * The list should be 'struct dm_list list' within the containing structure.
+ * t must be defined as a temporary variable of the same type as v.
+ */
+#define dm_list_iterate_items_safe(v, t, head) \
+ dm_list_iterate_items_gen_safe(v, t, (head), list)
+
+/*
+ * Walk a list backwards, setting 'v' in turn to the containing structure
+ * of each item.
+ * The containing structure should be the same type as 'v'.
+ * The 'struct dm_list' variable within the containing structure is 'field'.
+ */
+#define dm_list_iterate_back_items_gen(v, head, field) \
+ for (v = dm_list_struct_base((head)->p, __typeof__(*v), field); \
+ &v->field != (head); \
+ v = dm_list_struct_base(v->field.p, __typeof__(*v), field))
+
+/*
+ * Walk a list backwards, setting 'v' in turn to the containing structure
+ * of each item.
+ * The containing structure should be the same type as 'v'.
+ * The list should be 'struct dm_list list' within the containing structure.
+ */
+#define dm_list_iterate_back_items(v, head) dm_list_iterate_back_items_gen(v, (head), list)
+
+/*
+ * Return the number of elements in a list by walking it.
+ */
+unsigned int dm_list_size(const struct dm_list *head);
+
+/*********
+ * selinux
+ *********/
+
+/*
+ * Obtain SELinux security context assigned for the path and set this
+ * context for creating a new file system object. This security context
+ * is global and it is used until reset to default policy behaviour
+ * by calling 'dm_prepare_selinux_context(NULL, 0)'.
+ */
+int dm_prepare_selinux_context(const char *path, mode_t mode);
+/*
+ * Set SELinux context for existing file system object.
+ */
+int dm_set_selinux_context(const char *path, mode_t mode);
+
+/*********************
+ * string manipulation
+ *********************/
+
+/*
+ * Break up the name of a mapped device into its constituent
+ * Volume Group, Logical Volume and Layer (if present).
+ * If mem is supplied, the result is allocated from the mempool.
+ * Otherwise the strings are changed in situ.
+ */
+int dm_split_lvm_name(struct dm_pool *mem, const char *dmname,
+ char **vgname, char **lvname, char **layer);
+
+/*
+ * Destructively split buffer into NULL-separated words in argv.
+ * Returns number of words.
+ */
+int dm_split_words(char *buffer, unsigned max,
+ unsigned ignore_comments, /* Not implemented */
+ char **argv);
+
+/*
+ * Returns -1 if buffer too small
+ */
+int dm_snprintf(char *buf, size_t bufsize, const char *format, ...)
+ __attribute__ ((format(printf, 3, 4)));
+
+/*
+ * Returns pointer to the last component of the path.
+ */
+const char *dm_basename(const char *path);
+
+/**************************
+ * file/stream manipulation
+ **************************/
+
+/*
+ * Create a directory (with parent directories if necessary).
+ * Returns 1 on success, 0 on failure.
+ */
+int dm_create_dir(const char *dir);
+
+/*
+ * Close a stream, with nicer error checking than fclose's.
+ * Derived from gnulib's close-stream.c.
+ *
+ * Close "stream". Return 0 if successful, and EOF (setting errno)
+ * otherwise. Upon failure, set errno to 0 if the error number
+ * cannot be determined. Useful mainly for writable streams.
+ */
+int dm_fclose(FILE *stream);
+
+/*
+ * Returns size of a buffer which is allocated with dm_malloc.
+ * Pointer to the buffer is stored in *buf.
+ * Returns -1 on failure leaving buf undefined.
+ */
+int dm_asprintf(char **buf, const char *format, ...)
+ __attribute__ ((format(printf, 2, 3)));
+
+/*
+ * create lockfile (pidfile) - create and lock a lock file
+ * @lockfile: location of lock file
+ *
+ * Returns: 1 on success, 0 otherwise, errno is handled internally
+ */
+int dm_create_lockfile(const char* lockfile);
+
+/*
+ * Query whether a daemon is running based on its lockfile
+ *
+ * Returns: 1 if running, 0 if not
+ */
+int dm_daemon_is_running(const char* lockfile);
+
+/*********************
+ * regular expressions
+ *********************/
+struct dm_regex;
+
+/*
+ * Initialise an array of num patterns for matching.
+ * Uses memory from mem.
+ */
+struct dm_regex *dm_regex_create(struct dm_pool *mem, const char * const *patterns,
+ unsigned num_patterns);
+
+/*
+ * Match string s against the patterns.
+ * Returns the index of the highest pattern in the array that matches,
+ * or -1 if none match.
+ */
+int dm_regex_match(struct dm_regex *regex, const char *s);
+
+/*
+ * This is useful for regression testing only. The idea is if two
+ * fingerprints are different, then the two dfas are certainly not
+ * isomorphic. If two fingerprints _are_ the same then it's very likely
+ * that the dfas are isomorphic.
+ *
+ * This function must be called before any matching is done.
+ */
+uint32_t dm_regex_fingerprint(struct dm_regex *regex);
+
+/*********************
+ * reporting functions
+ *********************/
+
+struct dm_report_object_type {
+ uint32_t id; /* Powers of 2 */
+ const char *desc;
+ const char *prefix; /* field id string prefix (optional) */
+ void *(*data_fn)(void *object); /* callback from report_object() */
+};
+
+struct dm_report_field;
+
+/*
+ * dm_report_field_type flags
+ */
+#define DM_REPORT_FIELD_MASK 0x000000FF
+#define DM_REPORT_FIELD_ALIGN_MASK 0x0000000F
+#define DM_REPORT_FIELD_ALIGN_LEFT 0x00000001
+#define DM_REPORT_FIELD_ALIGN_RIGHT 0x00000002
+#define DM_REPORT_FIELD_TYPE_MASK 0x000000F0
+#define DM_REPORT_FIELD_TYPE_STRING 0x00000010
+#define DM_REPORT_FIELD_TYPE_NUMBER 0x00000020
+
+#define DM_REPORT_FIELD_TYPE_ID_LEN 32
+#define DM_REPORT_FIELD_TYPE_HEADING_LEN 32
+
+struct dm_report;
+struct dm_report_field_type {
+ uint32_t type; /* object type id */
+ uint32_t flags; /* DM_REPORT_FIELD_* */
+ uint32_t offset; /* byte offset in the object */
+ int32_t width; /* default width */
+ /* string used to specify the field */
+ const char id[DM_REPORT_FIELD_TYPE_ID_LEN];
+ /* string printed in header */
+ const char heading[DM_REPORT_FIELD_TYPE_HEADING_LEN];
+ int (*report_fn)(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field, const void *data,
+ void *private_data);
+ const char *desc; /* description of the field */
+};
+
+/*
+ * dm_report_init output_flags
+ */
+#define DM_REPORT_OUTPUT_MASK 0x000000FF
+#define DM_REPORT_OUTPUT_ALIGNED 0x00000001
+#define DM_REPORT_OUTPUT_BUFFERED 0x00000002
+#define DM_REPORT_OUTPUT_HEADINGS 0x00000004
+#define DM_REPORT_OUTPUT_FIELD_NAME_PREFIX 0x00000008
+#define DM_REPORT_OUTPUT_FIELD_UNQUOTED 0x00000010
+#define DM_REPORT_OUTPUT_COLUMNS_AS_ROWS 0x00000020
+
+struct dm_report *dm_report_init(uint32_t *report_types,
+ const struct dm_report_object_type *types,
+ const struct dm_report_field_type *fields,
+ const char *output_fields,
+ const char *output_separator,
+ uint32_t output_flags,
+ const char *sort_keys,
+ void *private_data);
+int dm_report_object(struct dm_report *rh, void *object);
+int dm_report_output(struct dm_report *rh);
+void dm_report_free(struct dm_report *rh);
+
+/*
+ * Prefix added to each field name with DM_REPORT_OUTPUT_FIELD_NAME_PREFIX
+ */
+int dm_report_set_output_field_name_prefix(struct dm_report *rh,
+ const char *report_prefix);
+
+/*
+ * Report functions are provided for simple data types.
+ * They take care of allocating copies of the data.
+ */
+int dm_report_field_string(struct dm_report *rh, struct dm_report_field *field,
+ const char **data);
+int dm_report_field_int32(struct dm_report *rh, struct dm_report_field *field,
+ const int32_t *data);
+int dm_report_field_uint32(struct dm_report *rh, struct dm_report_field *field,
+ const uint32_t *data);
+int dm_report_field_int(struct dm_report *rh, struct dm_report_field *field,
+ const int *data);
+int dm_report_field_uint64(struct dm_report *rh, struct dm_report_field *field,
+ const uint64_t *data);
+
+/*
+ * For custom fields, allocate the data in 'mem' and use
+ * dm_report_field_set_value().
+ * 'sortvalue' may be NULL if it matches 'value'
+ */
+void dm_report_field_set_value(struct dm_report_field *field, const void *value,
+ const void *sortvalue);
+
+/* Cookie prefixes.
+ * The cookie value consists of a prefix (16 bits) and a base (16 bits).
+ * We can use the prefix to store the flags. These flags are sent to
+ * kernel within given dm task. When returned back to userspace in
+ * DM_COOKIE udev environment variable, we can control several aspects
+ * of udev rules we use by decoding the cookie prefix. When doing the
+ * notification, we replace the cookie prefix with DM_COOKIE_MAGIC,
+ * so we notify the right semaphore.
+ * It is still possible to use cookies for passing the flags to udev
+ * rules even when udev_sync is disabled. The base part of the cookie
+ * will be zero (there's no notification semaphore) and prefix will be
+ * set then. However, having udev_sync enabled is highly recommended.
+ */
+#define DM_COOKIE_MAGIC 0x0D4D
+#define DM_UDEV_FLAGS_MASK 0xFFFF0000
+#define DM_UDEV_FLAGS_SHIFT 16
+
+/*
+ * DM_UDEV_DISABLE_DM_RULES_FLAG is set in case we need to disable
+ * basic device-mapper udev rules that create symlinks in /dev/<DM_DIR>
+ * directory. However, we can't reliably prevent creating default
+ * nodes by udev (commonly /dev/dm-X, where X is a number).
+ */
+#define DM_UDEV_DISABLE_DM_RULES_FLAG 0x0001
+/*
+ * DM_UDEV_DISABLE_SUBSYTEM_RULES_FLAG is set in case we need to disable
+ * subsystem udev rules, but still we need the general DM udev rules to
+ * be applied (to create the nodes and symlinks under /dev and /dev/disk).
+ */
+#define DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG 0x0002
+/*
+ * DM_UDEV_DISABLE_DISK_RULES_FLAG is set in case we need to disable
+ * general DM rules that set symlinks in /dev/disk directory.
+ */
+#define DM_UDEV_DISABLE_DISK_RULES_FLAG 0x0004
+/*
+ * DM_UDEV_DISABLE_OTHER_RULES_FLAG is set in case we need to disable
+ * all the other rules that are not general device-mapper nor subsystem
+ * related (the rules belong to other software or packages). All foreign
+ * rules should check this flag directly and they should ignore further
+ * rule processing for such event.
+ */
+#define DM_UDEV_DISABLE_OTHER_RULES_FLAG 0x0008
+/*
+ * DM_UDEV_LOW_PRIORITY_FLAG is set in case we need to instruct the
+ * udev rules to give low priority to the device that is currently
+ * processed. For example, this provides a way to select which symlinks
+ * could be overwritten by high priority ones if their names are equal.
+ * Common situation is a name based on FS UUID while using origin and
+ * snapshot devices.
+ */
+#define DM_UDEV_LOW_PRIORITY_FLAG 0x0010
+/*
+ * DM_UDEV_DISABLE_LIBRARY_FALLBACK is set in case we need to disable
+ * libdevmapper's node management. We will rely on udev completely
+ * and there will be no fallback action provided by libdevmapper if
+ * udev does something improperly.
+ */
+#define DM_UDEV_DISABLE_LIBRARY_FALLBACK 0x0020
+/*
+ * DM_UDEV_PRIMARY_SOURCE_FLAG is automatically appended by
+ * libdevmapper for all ioctls generating udev uevents. Once used in
+ * udev rules, we know if this is a real "primary sourced" event or not.
+ * We need to distinguish real events originated in libdevmapper from
+ * any spurious events to gather all missing information (e.g. events
+ * generated as a result of "udevadm trigger" command or as a result
+ * of the "watch" udev rule).
+ */
+#define DM_UDEV_PRIMARY_SOURCE_FLAG 0x0040
+
+int dm_cookie_supported(void);
+
+/*
+ * Udev synchronisation functions.
+ */
+void dm_udev_set_sync_support(int sync_with_udev);
+int dm_udev_get_sync_support(void);
+void dm_udev_set_checking(int checking);
+int dm_udev_get_checking(void);
+int dm_udev_create_cookie(uint32_t *cookie);
+int dm_udev_complete(uint32_t cookie);
+int dm_udev_wait(uint32_t cookie);
+
+#define DM_DEV_DIR_UMASK 0022
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* LIB_DEVICE_MAPPER_H */
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: devmapper
+Description: device-mapper library
+Version: @DM_LIB_PATCHLEVEL@
+Cflags: -I${includedir}
+Libs: -L${libdir} -ldevmapper
+Requires.private: @SELINUX_PC@ @UDEV_PC@
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+#include "libdm-targets.h"
+#include "libdm-common.h"
+#include "kdev_t.h"
+#include "dm-ioctl.h"
+
+#include <stdarg.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#ifdef UDEV_SYNC_SUPPORT
+# include <sys/types.h>
+# include <sys/ipc.h>
+# include <sys/sem.h>
+# define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
+# include <libudev.h>
+#endif
+
+#ifdef linux
+# include <linux/fs.h>
+#endif
+
+#ifdef HAVE_SELINUX
+# include <selinux/selinux.h>
+#endif
+#ifdef HAVE_SELINUX_LABEL_H
+# include <selinux/label.h>
+#endif
+
+#define DEV_DIR "/dev/"
+
+#ifdef UDEV_SYNC_SUPPORT
+#ifdef _SEM_SEMUN_UNDEFINED
+union semun
+{
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
+ unsigned short int *array; /* array for GETALL & SETALL */
+ struct seminfo *__buf; /* buffer for IPC_INFO */
+};
+#endif
+#endif
+
+static char _dm_dir[PATH_MAX] = DEV_DIR DM_DIR;
+
+static int _verbose = 0;
+
+#ifdef HAVE_SELINUX_LABEL_H
+static struct selabel_handle *_selabel_handle = NULL;
+#endif
+
+#ifdef UDEV_SYNC_SUPPORT
+static int _semaphore_supported = -1;
+static int _udev_running = -1;
+static int _sync_with_udev = 1;
+static int _udev_checking = 1;
+#endif
+
+/*
+ * Library users can provide their own logging
+ * function.
+ */
+
+static void _default_log_line(int level,
+ const char *file __attribute__((unused)),
+ int line __attribute__((unused)), int dm_errno,
+ const char *f, va_list ap)
+{
+ int use_stderr = level & _LOG_STDERR;
+
+ level &= ~_LOG_STDERR;
+
+ if (level > _LOG_WARN && !_verbose)
+ return;
+
+ if (level < _LOG_WARN)
+ vfprintf(stderr, f, ap);
+ else
+ vfprintf(use_stderr ? stderr : stdout, f, ap);
+
+ if (level < _LOG_WARN)
+ fprintf(stderr, "\n");
+ else
+ fprintf(use_stderr ? stderr : stdout, "\n");
+}
+
+static void _default_log_with_errno(int level,
+ const char *file __attribute__((unused)),
+ int line __attribute__((unused)), int dm_errno,
+ const char *f, ...)
+{
+ va_list ap;
+
+ va_start(ap, f);
+ _default_log_line(level, file, line, dm_errno, f, ap);
+ va_end(ap);
+}
+
+static void _default_log(int level, const char *file,
+ int line, const char *f, ...)
+{
+ va_list ap;
+
+ va_start(ap, f);
+ _default_log_line(level, file, line, 0, f, ap);
+ va_end(ap);
+}
+
+dm_log_fn dm_log = _default_log;
+dm_log_with_errno_fn dm_log_with_errno = _default_log_with_errno;
+
+void dm_log_init(dm_log_fn fn)
+{
+ if (fn)
+ dm_log = fn;
+ else
+ dm_log = _default_log;
+
+ dm_log_with_errno = _default_log_with_errno;
+}
+
+int dm_log_is_non_default(void)
+{
+ return (dm_log == _default_log) ? 0 : 1;
+}
+
+void dm_log_with_errno_init(dm_log_with_errno_fn fn)
+{
+ if (fn)
+ dm_log_with_errno = fn;
+ else
+ dm_log_with_errno = _default_log_with_errno;
+
+ dm_log = _default_log;
+}
+
+void dm_log_init_verbose(int level)
+{
+ _verbose = level;
+}
+
+static void _build_dev_path(char *buffer, size_t len, const char *dev_name)
+{
+ /* If there's a /, assume caller knows what they're doing */
+ if (strchr(dev_name, '/'))
+ snprintf(buffer, len, "%s", dev_name);
+ else
+ snprintf(buffer, len, "%s/%s", _dm_dir, dev_name);
+}
+
+int dm_get_library_version(char *version, size_t size)
+{
+ strncpy(version, DM_LIB_VERSION, size);
+ return 1;
+}
+
+struct dm_task *dm_task_create(int type)
+{
+ struct dm_task *dmt = dm_zalloc(sizeof(*dmt));
+
+ if (!dmt) {
+ log_error("dm_task_create: malloc(%" PRIsize_t ") failed",
+ sizeof(*dmt));
+ return NULL;
+ }
+
+ if (!dm_check_version()) {
+ dm_free(dmt);
+ return NULL;
+ }
+
+ dmt->type = type;
+ dmt->minor = -1;
+ dmt->major = -1;
+ dmt->allow_default_major_fallback = 1;
+ dmt->uid = DM_DEVICE_UID;
+ dmt->gid = DM_DEVICE_GID;
+ dmt->mode = DM_DEVICE_MODE;
+ dmt->no_open_count = 0;
+ dmt->read_ahead = DM_READ_AHEAD_AUTO;
+ dmt->read_ahead_flags = 0;
+ dmt->event_nr = 0;
+ dmt->cookie_set = 0;
+ dmt->query_inactive_table = 0;
+ dmt->new_uuid = 0;
+
+ return dmt;
+}
+
+/*
+ * Find the name associated with a given device number by scanning _dm_dir.
+ */
+static char *_find_dm_name_of_device(dev_t st_rdev)
+{
+ const char *name;
+ char path[PATH_MAX];
+ struct dirent *dirent;
+ DIR *d;
+ struct stat buf;
+ char *new_name = NULL;
+
+ if (!(d = opendir(_dm_dir))) {
+ log_sys_error("opendir", _dm_dir);
+ return NULL;
+ }
+
+ while ((dirent = readdir(d))) {
+ name = dirent->d_name;
+
+ if (!strcmp(name, ".") || !strcmp(name, ".."))
+ continue;
+
+ if (dm_snprintf(path, sizeof(path), "%s/%s", _dm_dir,
+ name) == -1) {
+ log_error("Couldn't create path for %s", name);
+ continue;
+ }
+
+ if (stat(path, &buf))
+ continue;
+
+ if (buf.st_rdev == st_rdev) {
+ if (!(new_name = dm_strdup(name)))
+ log_error("dm_task_set_name: strdup(%s) failed",
+ name);
+ break;
+ }
+ }
+
+ if (closedir(d))
+ log_sys_error("closedir", _dm_dir);
+
+ return new_name;
+}
+
+int dm_task_set_name(struct dm_task *dmt, const char *name)
+{
+ char *pos;
+ char *new_name = NULL;
+ char path[PATH_MAX];
+ struct stat st1, st2;
+
+ dm_free(dmt->dev_name);
+ dmt->dev_name = NULL;
+
+ /*
+ * Path supplied for existing device?
+ */
+ if ((pos = strrchr(name, '/'))) {
+ if (dmt->type == DM_DEVICE_CREATE) {
+ log_error("Name \"%s\" invalid. It contains \"/\".", name);
+ return 0;
+ }
+
+ if (stat(name, &st1)) {
+ log_error("Device %s not found", name);
+ return 0;
+ }
+
+ /*
+ * If supplied path points to same device as last component
+ * under /dev/mapper, use that name directly. Otherwise call
+ * _find_dm_name_of_device() to scan _dm_dir for a match.
+ */
+ if (dm_snprintf(path, sizeof(path), "%s/%s", _dm_dir,
+ pos + 1) == -1) {
+ log_error("Couldn't create path for %s", pos + 1);
+ return 0;
+ }
+
+ if (!stat(path, &st2) && (st1.st_rdev == st2.st_rdev))
+ name = pos + 1;
+ else if ((new_name = _find_dm_name_of_device(st1.st_rdev)))
+ name = new_name;
+ else {
+ log_error("Device %s not found", name);
+ return 0;
+ }
+ }
+
+ if (strlen(name) >= DM_NAME_LEN) {
+ log_error("Name \"%s\" too long", name);
+ dm_free(new_name);
+ return 0;
+ }
+
+ if (new_name)
+ dmt->dev_name = new_name;
+ else if (!(dmt->dev_name = dm_strdup(name))) {
+ log_error("dm_task_set_name: strdup(%s) failed", name);
+ return 0;
+ }
+
+ return 1;
+}
+
+int dm_task_set_uuid(struct dm_task *dmt, const char *uuid)
+{
+ dm_free(dmt->uuid);
+
+ if (!(dmt->uuid = dm_strdup(uuid))) {
+ log_error("dm_task_set_uuid: strdup(%s) failed", uuid);
+ return 0;
+ }
+
+ return 1;
+}
+
+int dm_task_set_major(struct dm_task *dmt, int major)
+{
+ dmt->major = major;
+ dmt->allow_default_major_fallback = 0;
+
+ return 1;
+}
+
+int dm_task_set_minor(struct dm_task *dmt, int minor)
+{
+ dmt->minor = minor;
+
+ return 1;
+}
+
+int dm_task_set_major_minor(struct dm_task *dmt, int major, int minor,
+ int allow_default_major_fallback)
+{
+ dmt->major = major;
+ dmt->minor = minor;
+ dmt->allow_default_major_fallback = allow_default_major_fallback;
+
+ return 1;
+}
+
+int dm_task_set_uid(struct dm_task *dmt, uid_t uid)
+{
+ dmt->uid = uid;
+
+ return 1;
+}
+
+int dm_task_set_gid(struct dm_task *dmt, gid_t gid)
+{
+ dmt->gid = gid;
+
+ return 1;
+}
+
+int dm_task_set_mode(struct dm_task *dmt, mode_t mode)
+{
+ dmt->mode = mode;
+
+ return 1;
+}
+
+int dm_task_add_target(struct dm_task *dmt, uint64_t start, uint64_t size,
+ const char *ttype, const char *params)
+{
+ struct target *t = create_target(start, size, ttype, params);
+
+ if (!t)
+ return 0;
+
+ if (!dmt->head)
+ dmt->head = dmt->tail = t;
+ else {
+ dmt->tail->next = t;
+ dmt->tail = t;
+ }
+
+ return 1;
+}
+
+#ifdef HAVE_SELINUX
+static int _selabel_lookup(const char *path, mode_t mode,
+ security_context_t *scontext)
+{
+#ifdef HAVE_SELINUX_LABEL_H
+ if (!_selabel_handle &&
+ !(_selabel_handle = selabel_open(SELABEL_CTX_FILE, NULL, 0))) {
+ log_error("selabel_open failed: %s", strerror(errno));
+ return 0;
+ }
+
+ if (selabel_lookup(_selabel_handle, scontext, path, mode)) {
+ log_error("selabel_lookup failed: %s", strerror(errno));
+ return 0;
+ }
+#else
+ if (matchpathcon(path, mode, scontext)) {
+ log_error("matchpathcon failed: %s", strerror(errno));
+ return 0;
+ }
+#endif
+ return 1;
+}
+#endif
+
+int dm_prepare_selinux_context(const char *path, mode_t mode)
+{
+#ifdef HAVE_SELINUX
+ security_context_t scontext = NULL;
+
+ if (is_selinux_enabled() <= 0)
+ return 1;
+
+ if (path) {
+ if (!_selabel_lookup(path, mode, &scontext))
+ return_0;
+
+ log_debug("Preparing SELinux context for %s to %s.", path, scontext);
+ }
+ else
+ log_debug("Resetting SELinux context to default value.");
+
+ if (setfscreatecon(scontext) < 0) {
+ log_sys_error("setfscreatecon", path);
+ freecon(scontext);
+ return 0;
+ }
+
+ freecon(scontext);
+#endif
+ return 1;
+}
+
+int dm_set_selinux_context(const char *path, mode_t mode)
+{
+#ifdef HAVE_SELINUX
+ security_context_t scontext;
+
+ if (is_selinux_enabled() <= 0)
+ return 1;
+
+ if (!_selabel_lookup(path, mode, &scontext))
+ return_0;
+
+ log_debug("Setting SELinux context for %s to %s.", path, scontext);
+
+ if ((lsetfilecon(path, scontext) < 0) && (errno != ENOTSUP)) {
+ log_sys_error("lsetfilecon", path);
+ freecon(scontext);
+ return 0;
+ }
+
+ freecon(scontext);
+#endif
+ return 1;
+}
+
+void selinux_release(void)
+{
+#ifdef HAVE_SELINUX_LABEL_H
+ if (_selabel_handle)
+ selabel_close(_selabel_handle);
+ _selabel_handle = NULL;
+#endif
+}
+
+static int _add_dev_node(const char *dev_name, uint32_t major, uint32_t minor,
+ uid_t uid, gid_t gid, mode_t mode, int check_udev)
+{
+ char path[PATH_MAX];
+ struct stat info;
+ dev_t dev = MKDEV(major, minor);
+ mode_t old_mask;
+
+ _build_dev_path(path, sizeof(path), dev_name);
+
+ if (stat(path, &info) >= 0) {
+ if (!S_ISBLK(info.st_mode)) {
+ log_error("A non-block device file at '%s' "
+ "is already present", path);
+ return 0;
+ }
+
+ /* If right inode already exists we don't touch uid etc. */
+ if (info.st_rdev == dev)
+ return 1;
+
+ if (unlink(path) < 0) {
+ log_error("Unable to unlink device node for '%s'",
+ dev_name);
+ return 0;
+ }
+ } else if (dm_udev_get_sync_support() && dm_udev_get_checking() &&
+ check_udev)
+ log_warn("%s not set up by udev: Falling back to direct "
+ "node creation.", path);
+
+ (void) dm_prepare_selinux_context(path, S_IFBLK);
+ old_mask = umask(0);
+ if (mknod(path, S_IFBLK | mode, dev) < 0) {
+ log_error("Unable to make device node for '%s'", dev_name);
+ umask(old_mask);
+ (void) dm_prepare_selinux_context(NULL, 0);
+ return 0;
+ }
+ umask(old_mask);
+ (void) dm_prepare_selinux_context(NULL, 0);
+
+ if (chown(path, uid, gid) < 0) {
+ log_sys_error("chown", path);
+ return 0;
+ }
+
+ log_debug("Created %s", path);
+
+ return 1;
+}
+
+static int _rm_dev_node(const char *dev_name, int check_udev)
+{
+ char path[PATH_MAX];
+ struct stat info;
+
+ _build_dev_path(path, sizeof(path), dev_name);
+
+ if (stat(path, &info) < 0)
+ return 1;
+ else if (dm_udev_get_sync_support() && dm_udev_get_checking() &&
+ check_udev)
+ log_warn("Node %s was not removed by udev. "
+ "Falling back to direct node removal.", path);
+
+ if (unlink(path) < 0) {
+ log_error("Unable to unlink device node for '%s'", dev_name);
+ return 0;
+ }
+
+ log_debug("Removed %s", path);
+
+ return 1;
+}
+
+static int _rename_dev_node(const char *old_name, const char *new_name,
+ int check_udev)
+{
+ char oldpath[PATH_MAX];
+ char newpath[PATH_MAX];
+ struct stat info;
+
+ _build_dev_path(oldpath, sizeof(oldpath), old_name);
+ _build_dev_path(newpath, sizeof(newpath), new_name);
+
+ if (stat(newpath, &info) == 0) {
+ if (!S_ISBLK(info.st_mode)) {
+ log_error("A non-block device file at '%s' "
+ "is already present", newpath);
+ return 0;
+ }
+ else if (dm_udev_get_sync_support() && dm_udev_get_checking() &&
+ check_udev) {
+ if (stat(oldpath, &info) < 0 &&
+ errno == ENOENT)
+ /* assume udev already deleted this */
+ return 1;
+ else {
+ log_warn("The node %s should have been renamed to %s "
+ "by udev but old node is still present. "
+ "Falling back to direct old node removal.",
+ oldpath, newpath);
+ return _rm_dev_node(old_name, 0);
+ }
+ }
+
+ if (unlink(newpath) < 0) {
+ if (errno == EPERM) {
+ /* devfs, entry has already been renamed */
+ return 1;
+ }
+ log_error("Unable to unlink device node for '%s'",
+ new_name);
+ return 0;
+ }
+ }
+ else if (dm_udev_get_sync_support() && dm_udev_get_checking() &&
+ check_udev)
+ log_warn("The node %s should have been renamed to %s "
+ "by udev but new node is not present. "
+ "Falling back to direct node rename.",
+ oldpath, newpath);
+
+ if (rename(oldpath, newpath) < 0) {
+ log_error("Unable to rename device node from '%s' to '%s'",
+ old_name, new_name);
+ return 0;
+ }
+
+ log_debug("Renamed %s to %s", oldpath, newpath);
+
+ return 1;
+}
+
+#ifdef linux
+static int _open_dev_node(const char *dev_name)
+{
+ int fd = -1;
+ char path[PATH_MAX];
+
+ _build_dev_path(path, sizeof(path), dev_name);
+
+ if ((fd = open(path, O_RDONLY, 0)) < 0)
+ log_sys_error("open", path);
+
+ return fd;
+}
+
+int get_dev_node_read_ahead(const char *dev_name, uint32_t *read_ahead)
+{
+ int r = 1;
+ int fd;
+ long read_ahead_long;
+
+ if (!*dev_name) {
+ log_error("Empty device name passed to BLKRAGET");
+ return 0;
+ }
+
+ if ((fd = _open_dev_node(dev_name)) < 0)
+ return_0;
+
+ if (ioctl(fd, BLKRAGET, &read_ahead_long)) {
+ log_sys_error("BLKRAGET", dev_name);
+ *read_ahead = 0;
+ r = 0;
+ } else {
+ *read_ahead = (uint32_t) read_ahead_long;
+ log_debug("%s: read ahead is %" PRIu32, dev_name, *read_ahead);
+ }
+
+ if (close(fd))
+ stack;
+
+ return r;
+}
+
+static int _set_read_ahead(const char *dev_name, uint32_t read_ahead)
+{
+ int r = 1;
+ int fd;
+ long read_ahead_long = (long) read_ahead;
+
+ if (!*dev_name) {
+ log_error("Empty device name passed to BLKRAGET");
+ return 0;
+ }
+
+ if ((fd = _open_dev_node(dev_name)) < 0)
+ return_0;
+
+ log_debug("%s: Setting read ahead to %" PRIu32, dev_name, read_ahead);
+
+ if (ioctl(fd, BLKRASET, read_ahead_long)) {
+ log_sys_error("BLKRASET", dev_name);
+ r = 0;
+ }
+
+ if (close(fd))
+ stack;
+
+ return r;
+}
+
+static int _set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
+ uint32_t read_ahead_flags)
+{
+ uint32_t current_read_ahead;
+
+ if (read_ahead == DM_READ_AHEAD_AUTO)
+ return 1;
+
+ if (read_ahead == DM_READ_AHEAD_NONE)
+ read_ahead = 0;
+
+ if (read_ahead_flags & DM_READ_AHEAD_MINIMUM_FLAG) {
+ if (!get_dev_node_read_ahead(dev_name, ¤t_read_ahead))
+ return_0;
+
+ if (current_read_ahead > read_ahead) {
+ log_debug("%s: retaining kernel read ahead of %" PRIu32
+ " (requested %" PRIu32 ")",
+ dev_name, current_read_ahead, read_ahead);
+ return 1;
+ }
+ }
+
+ return _set_read_ahead(dev_name, read_ahead);
+}
+
+#else
+
+int get_dev_node_read_ahead(const char *dev_name, uint32_t *read_ahead)
+{
+ *read_ahead = 0;
+
+ return 1;
+}
+
+static int _set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
+ uint32_t read_ahead_flags)
+{
+ return 1;
+}
+#endif
+
+typedef enum {
+ NODE_ADD,
+ NODE_DEL,
+ NODE_RENAME,
+ NODE_READ_AHEAD
+} node_op_t;
+
+static int _do_node_op(node_op_t type, const char *dev_name, uint32_t major,
+ uint32_t minor, uid_t uid, gid_t gid, mode_t mode,
+ const char *old_name, uint32_t read_ahead,
+ uint32_t read_ahead_flags, int check_udev)
+{
+ switch (type) {
+ case NODE_ADD:
+ return _add_dev_node(dev_name, major, minor, uid, gid,
+ mode, check_udev);
+ case NODE_DEL:
+ return _rm_dev_node(dev_name, check_udev);
+ case NODE_RENAME:
+ return _rename_dev_node(old_name, dev_name, check_udev);
+ case NODE_READ_AHEAD:
+ return _set_dev_node_read_ahead(dev_name, read_ahead,
+ read_ahead_flags);
+ }
+
+ return 1;
+}
+
+static DM_LIST_INIT(_node_ops);
+
+struct node_op_parms {
+ struct dm_list list;
+ node_op_t type;
+ char *dev_name;
+ uint32_t major;
+ uint32_t minor;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ uint32_t read_ahead;
+ uint32_t read_ahead_flags;
+ char *old_name;
+ int check_udev;
+ char names[0];
+};
+
+static void _store_str(char **pos, char **ptr, const char *str)
+{
+ strcpy(*pos, str);
+ *ptr = *pos;
+ *pos += strlen(*ptr) + 1;
+}
+
+static int _stack_node_op(node_op_t type, const char *dev_name, uint32_t major,
+ uint32_t minor, uid_t uid, gid_t gid, mode_t mode,
+ const char *old_name, uint32_t read_ahead,
+ uint32_t read_ahead_flags, int check_udev)
+{
+ struct node_op_parms *nop;
+ struct dm_list *noph, *nopht;
+ size_t len = strlen(dev_name) + strlen(old_name) + 2;
+ char *pos;
+
+ /*
+ * Ignore any outstanding operations on the node if deleting it
+ */
+ if (type == NODE_DEL) {
+ dm_list_iterate_safe(noph, nopht, &_node_ops) {
+ nop = dm_list_item(noph, struct node_op_parms);
+ if (!strcmp(dev_name, nop->dev_name)) {
+ dm_list_del(&nop->list);
+ dm_free(nop);
+ }
+ }
+ }
+
+ if (!(nop = dm_malloc(sizeof(*nop) + len))) {
+ log_error("Insufficient memory to stack mknod operation");
+ return 0;
+ }
+
+ pos = nop->names;
+ nop->type = type;
+ nop->major = major;
+ nop->minor = minor;
+ nop->uid = uid;
+ nop->gid = gid;
+ nop->mode = mode;
+ nop->read_ahead = read_ahead;
+ nop->read_ahead_flags = read_ahead_flags;
+ nop->check_udev = check_udev;
+
+ _store_str(&pos, &nop->dev_name, dev_name);
+ _store_str(&pos, &nop->old_name, old_name);
+
+ dm_list_add(&_node_ops, &nop->list);
+
+ return 1;
+}
+
+static void _pop_node_ops(void)
+{
+ struct dm_list *noph, *nopht;
+ struct node_op_parms *nop;
+
+ dm_list_iterate_safe(noph, nopht, &_node_ops) {
+ nop = dm_list_item(noph, struct node_op_parms);
+ _do_node_op(nop->type, nop->dev_name, nop->major, nop->minor,
+ nop->uid, nop->gid, nop->mode, nop->old_name,
+ nop->read_ahead, nop->read_ahead_flags,
+ nop->check_udev);
+ dm_list_del(&nop->list);
+ dm_free(nop);
+ }
+}
+
+int add_dev_node(const char *dev_name, uint32_t major, uint32_t minor,
+ uid_t uid, gid_t gid, mode_t mode, int check_udev)
+{
+ log_debug("%s: Stacking NODE_ADD (%" PRIu32 ",%" PRIu32 ") %u:%u 0%o",
+ dev_name, major, minor, uid, gid, mode);
+
+ return _stack_node_op(NODE_ADD, dev_name, major, minor, uid,
+ gid, mode, "", 0, 0, check_udev);
+}
+
+int rename_dev_node(const char *old_name, const char *new_name, int check_udev)
+{
+ log_debug("%s: Stacking NODE_RENAME to %s", old_name, new_name);
+
+ return _stack_node_op(NODE_RENAME, new_name, 0, 0, 0,
+ 0, 0, old_name, 0, 0, check_udev);
+}
+
+int rm_dev_node(const char *dev_name, int check_udev)
+{
+ log_debug("%s: Stacking NODE_DEL (replaces other stacked ops)", dev_name);
+
+ return _stack_node_op(NODE_DEL, dev_name, 0, 0, 0,
+ 0, 0, "", 0, 0, check_udev);
+}
+
+int set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
+ uint32_t read_ahead_flags)
+{
+ if (read_ahead == DM_READ_AHEAD_AUTO)
+ return 1;
+
+ log_debug("%s: Stacking NODE_READ_AHEAD %" PRIu32 " (flags=%" PRIu32
+ ")", dev_name, read_ahead, read_ahead_flags);
+
+ return _stack_node_op(NODE_READ_AHEAD, dev_name, 0, 0, 0, 0,
+ 0, "", read_ahead, read_ahead_flags, 0);
+}
+
+void update_devs(void)
+{
+ _pop_node_ops();
+}
+
+int dm_set_dev_dir(const char *dev_dir)
+{
+ size_t len;
+ const char *slash;
+ if (*dev_dir != '/') {
+ log_debug("Invalid dev_dir value, %s: "
+ "not an absolute name.", dev_dir);
+ return 0;
+ }
+
+ len = strlen(dev_dir);
+ slash = dev_dir[len-1] == '/' ? "" : "/";
+
+ if (snprintf(_dm_dir, sizeof _dm_dir, "%s%s%s", dev_dir, slash, DM_DIR)
+ >= sizeof _dm_dir) {
+ log_debug("Invalid dev_dir value, %s: name too long.", dev_dir);
+ return 0;
+ }
+
+ return 1;
+}
+
+const char *dm_dir(void)
+{
+ return _dm_dir;
+}
+
+int dm_mknodes(const char *name)
+{
+ struct dm_task *dmt;
+ int r = 0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_MKNODES)))
+ return 0;
+
+ if (name && !dm_task_set_name(dmt, name))
+ goto out;
+
+ if (!dm_task_no_open_count(dmt))
+ goto out;
+
+ r = dm_task_run(dmt);
+
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+int dm_driver_version(char *version, size_t size)
+{
+ struct dm_task *dmt;
+ int r = 0;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_VERSION)))
+ return 0;
+
+ if (!dm_task_run(dmt))
+ log_error("Failed to get driver version");
+
+ if (!dm_task_get_driver_version(dmt, version, size))
+ goto out;
+
+ r = 1;
+
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+#ifndef UDEV_SYNC_SUPPORT
+void dm_udev_set_sync_support(int sync_with_udev)
+{
+}
+
+int dm_udev_get_sync_support(void)
+{
+ return 0;
+}
+
+void dm_udev_set_checking(int checking)
+{
+}
+
+int dm_udev_get_checking(void)
+{
+ return 0;
+}
+
+int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t flags)
+{
+ if (dm_cookie_supported())
+ dmt->event_nr = flags << DM_UDEV_FLAGS_SHIFT;
+ *cookie = 0;
+
+ return 1;
+}
+
+int dm_udev_complete(uint32_t cookie)
+{
+ return 1;
+}
+
+int dm_udev_wait(uint32_t cookie)
+{
+ return 1;
+}
+
+#else /* UDEV_SYNC_SUPPORT */
+
+static int _check_semaphore_is_supported(void)
+{
+ int maxid;
+ union semun arg;
+ struct seminfo seminfo;
+
+ arg.__buf = &seminfo;
+ maxid = semctl(0, 0, SEM_INFO, arg);
+
+ if (maxid < 0) {
+ log_warn("Kernel not configured for semaphores (System V IPC). "
+ "Not using udev synchronisation code.");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _check_udev_is_running(void)
+{
+ struct udev *udev;
+ struct udev_queue *udev_queue;
+ int r;
+
+ if (!(udev = udev_new()))
+ goto_bad;
+
+ if (!(udev_queue = udev_queue_new(udev))) {
+ udev_unref(udev);
+ goto_bad;
+ }
+
+ if (!(r = udev_queue_get_udev_is_active(udev_queue)))
+ log_debug("Udev is not running. "
+ "Not using udev synchronisation code.");
+
+ udev_queue_unref(udev_queue);
+ udev_unref(udev);
+
+ return r;
+
+bad:
+ log_error("Could not get udev state. Assuming udev is not running.");
+ return 0;
+}
+
+static void _check_udev_sync_requirements_once(void)
+{
+ if (_semaphore_supported < 0)
+ _semaphore_supported = _check_semaphore_is_supported();
+
+ if (_udev_running < 0)
+ _udev_running = _check_udev_is_running();
+}
+
+void dm_udev_set_sync_support(int sync_with_udev)
+{
+ _check_udev_sync_requirements_once();
+ _sync_with_udev = sync_with_udev;
+}
+
+int dm_udev_get_sync_support(void)
+{
+ _check_udev_sync_requirements_once();
+
+ return _semaphore_supported && dm_cookie_supported() &&
+ _udev_running && _sync_with_udev;
+}
+
+void dm_udev_set_checking(int checking)
+{
+ if ((_udev_checking = checking))
+ log_debug("DM udev checking enabled");
+ else
+ log_debug("DM udev checking disabled");
+}
+
+int dm_udev_get_checking(void)
+{
+ return _udev_checking;
+}
+
+static int _get_cookie_sem(uint32_t cookie, int *semid)
+{
+ if (cookie >> 16 != DM_COOKIE_MAGIC) {
+ log_error("Could not continue to access notification "
+ "semaphore identified by cookie value %"
+ PRIu32 " (0x%x). Incorrect cookie prefix.",
+ cookie, cookie);
+ return 0;
+ }
+
+ if ((*semid = semget((key_t) cookie, 1, 0)) >= 0)
+ return 1;
+
+ switch (errno) {
+ case ENOENT:
+ log_error("Could not find notification "
+ "semaphore identified by cookie "
+ "value %" PRIu32 " (0x%x)",
+ cookie, cookie);
+ break;
+ case EACCES:
+ log_error("No permission to access "
+ "notificaton semaphore identified "
+ "by cookie value %" PRIu32 " (0x%x)",
+ cookie, cookie);
+ break;
+ default:
+ log_error("Failed to access notification "
+ "semaphore identified by cookie "
+ "value %" PRIu32 " (0x%x): %s",
+ cookie, cookie, strerror(errno));
+ break;
+ }
+
+ return 0;
+}
+
+static int _udev_notify_sem_inc(uint32_t cookie, int semid)
+{
+ struct sembuf sb = {0, 1, 0};
+
+ if (semop(semid, &sb, 1) < 0) {
+ log_error("semid %d: semop failed for cookie 0x%" PRIx32 ": %s",
+ semid, cookie, strerror(errno));
+ return 0;
+ }
+
+ log_debug("Udev cookie 0x%" PRIx32 " (semid %d) incremented",
+ cookie, semid);
+
+ return 1;
+}
+
+static int _udev_notify_sem_dec(uint32_t cookie, int semid)
+{
+ struct sembuf sb = {0, -1, IPC_NOWAIT};
+
+ if (semop(semid, &sb, 1) < 0) {
+ switch (errno) {
+ case EAGAIN:
+ log_error("semid %d: semop failed for cookie "
+ "0x%" PRIx32 ": "
+ "incorrect semaphore state",
+ semid, cookie);
+ break;
+ default:
+ log_error("semid %d: semop failed for cookie "
+ "0x%" PRIx32 ": %s",
+ semid, cookie, strerror(errno));
+ break;
+ }
+ return 0;
+ }
+
+ log_debug("Udev cookie 0x%" PRIx32 " (semid %d) decremented",
+ cookie, semid);
+
+ return 1;
+}
+
+static int _udev_notify_sem_destroy(uint32_t cookie, int semid)
+{
+ if (semctl(semid, 0, IPC_RMID, 0) < 0) {
+ log_error("Could not cleanup notification semaphore "
+ "identified by cookie value %" PRIu32 " (0x%x): %s",
+ cookie, cookie, strerror(errno));
+ return 0;
+ }
+
+ log_debug("Udev cookie 0x%" PRIx32 " (semid %d) destroyed", cookie,
+ semid);
+
+ return 1;
+}
+
+static int _udev_notify_sem_create(uint32_t *cookie, int *semid)
+{
+ int fd;
+ int gen_semid;
+ uint16_t base_cookie;
+ uint32_t gen_cookie;
+ union semun sem_arg;
+
+ if ((fd = open("/dev/urandom", O_RDONLY)) < 0) {
+ log_error("Failed to open /dev/urandom "
+ "to create random cookie value");
+ *cookie = 0;
+ return 0;
+ }
+
+ /* Generate random cookie value. Be sure it is unique and non-zero. */
+ do {
+ /* FIXME Handle non-error returns from read(). Move _io() into libdm? */
+ if (read(fd, &base_cookie, sizeof(base_cookie)) != sizeof(base_cookie)) {
+ log_error("Failed to initialize notification cookie");
+ goto bad;
+ }
+
+ gen_cookie = DM_COOKIE_MAGIC << 16 | base_cookie;
+
+ if (base_cookie && (gen_semid = semget((key_t) gen_cookie,
+ 1, 0600 | IPC_CREAT | IPC_EXCL)) < 0) {
+ switch (errno) {
+ case EEXIST:
+ /* if the semaphore key exists, we
+ * simply generate another random one */
+ base_cookie = 0;
+ break;
+ case ENOMEM:
+ log_error("Not enough memory to create "
+ "notification semaphore");
+ goto bad;
+ case ENOSPC:
+ log_error("Limit for the maximum number "
+ "of semaphores reached. You can "
+ "check and set the limits in "
+ "/proc/sys/kernel/sem.");
+ goto bad;
+ default:
+ log_error("Failed to create notification "
+ "semaphore: %s", strerror(errno));
+ goto bad;
+ }
+ }
+ } while (!base_cookie);
+
+ log_debug("Udev cookie 0x%" PRIx32 " (semid %d) created",
+ gen_cookie, gen_semid);
+
+ sem_arg.val = 1;
+
+ if (semctl(gen_semid, 0, SETVAL, sem_arg) < 0) {
+ log_error("semid %d: semctl failed: %s", gen_semid, strerror(errno));
+ /* We have to destroy just created semaphore
+ * so it won't stay in the system. */
+ (void) _udev_notify_sem_destroy(gen_cookie, gen_semid);
+ goto bad;
+ }
+
+ log_debug("Udev cookie 0x%" PRIx32 " (semid %d) incremented",
+ gen_cookie, gen_semid);
+
+ if (close(fd))
+ stack;
+
+ *semid = gen_semid;
+ *cookie = gen_cookie;
+
+ return 1;
+
+bad:
+ if (close(fd))
+ stack;
+
+ *cookie = 0;
+
+ return 0;
+}
+
+int dm_udev_create_cookie(uint32_t *cookie)
+{
+ int semid;
+
+ if (!dm_udev_get_sync_support()) {
+ *cookie = 0;
+ return 1;
+ }
+
+ return _udev_notify_sem_create(cookie, &semid);
+}
+
+int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t flags)
+{
+ int semid;
+
+ if (dm_cookie_supported())
+ dmt->event_nr = flags << DM_UDEV_FLAGS_SHIFT;
+
+ if (!dm_udev_get_sync_support()) {
+ *cookie = 0;
+ return 1;
+ }
+
+ if (*cookie) {
+ if (!_get_cookie_sem(*cookie, &semid))
+ goto_bad;
+ } else if (!_udev_notify_sem_create(cookie, &semid))
+ goto_bad;
+
+ if (!_udev_notify_sem_inc(*cookie, semid)) {
+ log_error("Could not set notification semaphore "
+ "identified by cookie value %" PRIu32 " (0x%x)",
+ *cookie, *cookie);
+ goto bad;
+ }
+
+ dmt->event_nr |= ~DM_UDEV_FLAGS_MASK & *cookie;
+ dmt->cookie_set = 1;
+
+ log_debug("Udev cookie 0x%" PRIx32 " (semid %d) assigned to dm_task "
+ "type %d with flags 0x%" PRIx16, *cookie, semid, dmt->type, flags);
+
+ return 1;
+
+bad:
+ dmt->event_nr = 0;
+ return 0;
+}
+
+int dm_udev_complete(uint32_t cookie)
+{
+ int semid;
+
+ if (!cookie || !dm_udev_get_sync_support())
+ return 1;
+
+ if (!_get_cookie_sem(cookie, &semid))
+ return_0;
+
+ if (!_udev_notify_sem_dec(cookie, semid)) {
+ log_error("Could not signal waiting process using notification "
+ "semaphore identified by cookie value %" PRIu32 " (0x%x)",
+ cookie, cookie);
+ return 0;
+ }
+
+ return 1;
+}
+
+int dm_udev_wait(uint32_t cookie)
+{
+ int semid;
+ struct sembuf sb = {0, 0, 0};
+
+ if (!cookie || !dm_udev_get_sync_support())
+ return 1;
+
+ if (!_get_cookie_sem(cookie, &semid))
+ return_0;
+
+ if (!_udev_notify_sem_dec(cookie, semid)) {
+ log_error("Failed to set a proper state for notification "
+ "semaphore identified by cookie value %" PRIu32 " (0x%x) "
+ "to initialize waiting for incoming notifications.",
+ cookie, cookie);
+ (void) _udev_notify_sem_destroy(cookie, semid);
+ return 0;
+ }
+
+ log_debug("Udev cookie 0x%" PRIx32 " (semid %d): Waiting for zero",
+ cookie, semid);
+
+repeat_wait:
+ if (semop(semid, &sb, 1) < 0) {
+ if (errno == EINTR)
+ goto repeat_wait;
+ else if (errno == EIDRM)
+ return 1;
+
+ log_error("Could not set wait state for notification semaphore "
+ "identified by cookie value %" PRIu32 " (0x%x): %s",
+ cookie, cookie, strerror(errno));
+ (void) _udev_notify_sem_destroy(cookie, semid);
+ return 0;
+ }
+
+ return _udev_notify_sem_destroy(cookie, semid);
+}
+
+#endif /* UDEV_SYNC_SUPPORT */
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIB_DMCOMMON_H
+#define LIB_DMCOMMON_H
+
+#include "libdevmapper.h"
+
+struct target *create_target(uint64_t start,
+ uint64_t len,
+ const char *type, const char *params);
+
+int add_dev_node(const char *dev_name, uint32_t minor, uint32_t major,
+ uid_t uid, gid_t gid, mode_t mode, int check_udev);
+int rm_dev_node(const char *dev_name, int check_udev);
+int rename_dev_node(const char *old_name, const char *new_name,
+ int check_udev);
+int get_dev_node_read_ahead(const char *dev_name, uint32_t *read_ahead);
+int set_dev_node_read_ahead(const char *dev_name, uint32_t read_ahead,
+ uint32_t read_ahead_flags);
+void update_devs(void);
+void selinux_release(void);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2005-2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+#include "libdm-targets.h"
+#include "libdm-common.h"
+#include "kdev_t.h"
+#include "dm-ioctl.h"
+
+#include <stdarg.h>
+#include <sys/param.h>
+#include <sys/utsname.h>
+
+#define MAX_TARGET_PARAMSIZE 500000
+
+/* FIXME Fix interface so this is used only by LVM */
+#define UUID_PREFIX "LVM-"
+
+#define REPLICATOR_LOCAL_SITE 0
+
+/* Supported segment types */
+enum {
+ SEG_CRYPT,
+ SEG_ERROR,
+ SEG_LINEAR,
+ SEG_MIRRORED,
+ SEG_REPLICATOR,
+ SEG_REPLICATOR_DEV,
+ SEG_SNAPSHOT,
+ SEG_SNAPSHOT_ORIGIN,
+ SEG_SNAPSHOT_MERGE,
+ SEG_STRIPED,
+ SEG_ZERO,
+};
+
+/* FIXME Add crypt and multipath support */
+
+struct {
+ unsigned type;
+ const char *target;
+} dm_segtypes[] = {
+ { SEG_CRYPT, "crypt" },
+ { SEG_ERROR, "error" },
+ { SEG_LINEAR, "linear" },
+ { SEG_MIRRORED, "mirror" },
+ { SEG_REPLICATOR, "replicator" },
+ { SEG_REPLICATOR_DEV, "replicator-dev" },
+ { SEG_SNAPSHOT, "snapshot" },
+ { SEG_SNAPSHOT_ORIGIN, "snapshot-origin" },
+ { SEG_SNAPSHOT_MERGE, "snapshot-merge" },
+ { SEG_STRIPED, "striped" },
+ { SEG_ZERO, "zero"},
+};
+
+/* Some segment types have a list of areas of other devices attached */
+struct seg_area {
+ struct dm_list list;
+
+ struct dm_tree_node *dev_node;
+
+ uint64_t offset;
+
+ unsigned rsite_index; /* Replicator site index */
+ struct dm_tree_node *slog; /* Replicator sync log node */
+ uint64_t region_size; /* Replicator sync log size */
+ uint32_t flags; /* Replicator sync log flags */
+};
+
+/* Replicator-log has a list of sites */
+/* FIXME: maybe move to seg_area too? */
+struct replicator_site {
+ struct dm_list list;
+
+ unsigned rsite_index;
+ dm_replicator_mode_t mode;
+ uint32_t async_timeout;
+ uint32_t fall_behind_ios;
+ uint64_t fall_behind_data;
+};
+
+/* Per-segment properties */
+struct load_segment {
+ struct dm_list list;
+
+ unsigned type;
+
+ uint64_t size;
+
+ unsigned area_count; /* Linear + Striped + Mirrored + Crypt + Replicator */
+ struct dm_list areas; /* Linear + Striped + Mirrored + Crypt + Replicator */
+
+ uint32_t stripe_size; /* Striped */
+
+ int persistent; /* Snapshot */
+ uint32_t chunk_size; /* Snapshot */
+ struct dm_tree_node *cow; /* Snapshot */
+ struct dm_tree_node *origin; /* Snapshot + Snapshot origin */
+ struct dm_tree_node *merge; /* Snapshot */
+
+ struct dm_tree_node *log; /* Mirror + Replicator */
+ uint32_t region_size; /* Mirror */
+ unsigned clustered; /* Mirror */
+ unsigned mirror_area_count; /* Mirror */
+ uint32_t flags; /* Mirror log */
+ char *uuid; /* Clustered mirror log */
+
+ const char *cipher; /* Crypt */
+ const char *chainmode; /* Crypt */
+ const char *iv; /* Crypt */
+ uint64_t iv_offset; /* Crypt */
+ const char *key; /* Crypt */
+
+ const char *rlog_type; /* Replicator */
+ struct dm_list rsites; /* Replicator */
+ unsigned rsite_count; /* Replicator */
+ unsigned rdevice_count; /* Replicator */
+ struct dm_tree_node *replicator;/* Replicator-dev */
+ uint64_t rdevice_index; /* Replicator-dev */
+};
+
+/* Per-device properties */
+struct load_properties {
+ int read_only;
+ uint32_t major;
+ uint32_t minor;
+
+ uint32_t read_ahead;
+ uint32_t read_ahead_flags;
+
+ unsigned segment_count;
+ unsigned size_changed;
+ struct dm_list segs;
+
+ const char *new_name;
+
+ /* If immediate_dev_node is set to 1, try to create the dev node
+ * as soon as possible (e.g. in preload stage even during traversal
+ * and processing of dm tree). This will also flush all stacked dev
+ * node operations, synchronizing with udev.
+ */
+ int immediate_dev_node;
+};
+
+/* Two of these used to join two nodes with uses and used_by. */
+struct dm_tree_link {
+ struct dm_list list;
+ struct dm_tree_node *node;
+};
+
+struct dm_tree_node {
+ struct dm_tree *dtree;
+
+ const char *name;
+ const char *uuid;
+ struct dm_info info;
+
+ struct dm_list uses; /* Nodes this node uses */
+ struct dm_list used_by; /* Nodes that use this node */
+
+ int activation_priority; /* 0 gets activated first */
+
+ uint16_t udev_flags; /* Udev control flags */
+
+ void *context; /* External supplied context */
+
+ struct load_properties props; /* For creation/table (re)load */
+
+ /*
+ * If presuspend of child node is needed
+ * Note: only direct child is allowed
+ */
+ struct dm_tree_node *presuspend_node;
+};
+
+struct dm_tree {
+ struct dm_pool *mem;
+ struct dm_hash_table *devs;
+ struct dm_hash_table *uuids;
+ struct dm_tree_node root;
+ int skip_lockfs; /* 1 skips lockfs (for non-snapshots) */
+ int no_flush; /* 1 sets noflush (mirrors/multipath) */
+ uint32_t cookie;
+};
+
+struct dm_tree *dm_tree_create(void)
+{
+ struct dm_tree *dtree;
+
+ if (!(dtree = dm_zalloc(sizeof(*dtree)))) {
+ log_error("dm_tree_create malloc failed");
+ return NULL;
+ }
+
+ dtree->root.dtree = dtree;
+ dm_list_init(&dtree->root.uses);
+ dm_list_init(&dtree->root.used_by);
+ dtree->skip_lockfs = 0;
+ dtree->no_flush = 0;
+
+ if (!(dtree->mem = dm_pool_create("dtree", 1024))) {
+ log_error("dtree pool creation failed");
+ dm_free(dtree);
+ return NULL;
+ }
+
+ if (!(dtree->devs = dm_hash_create(8))) {
+ log_error("dtree hash creation failed");
+ dm_pool_destroy(dtree->mem);
+ dm_free(dtree);
+ return NULL;
+ }
+
+ if (!(dtree->uuids = dm_hash_create(32))) {
+ log_error("dtree uuid hash creation failed");
+ dm_hash_destroy(dtree->devs);
+ dm_pool_destroy(dtree->mem);
+ dm_free(dtree);
+ return NULL;
+ }
+
+ return dtree;
+}
+
+void dm_tree_free(struct dm_tree *dtree)
+{
+ if (!dtree)
+ return;
+
+ dm_hash_destroy(dtree->uuids);
+ dm_hash_destroy(dtree->devs);
+ dm_pool_destroy(dtree->mem);
+ dm_free(dtree);
+}
+
+static int _nodes_are_linked(const struct dm_tree_node *parent,
+ const struct dm_tree_node *child)
+{
+ struct dm_tree_link *dlink;
+
+ dm_list_iterate_items(dlink, &parent->uses)
+ if (dlink->node == child)
+ return 1;
+
+ return 0;
+}
+
+static int _link(struct dm_list *list, struct dm_tree_node *node)
+{
+ struct dm_tree_link *dlink;
+
+ if (!(dlink = dm_pool_alloc(node->dtree->mem, sizeof(*dlink)))) {
+ log_error("dtree link allocation failed");
+ return 0;
+ }
+
+ dlink->node = node;
+ dm_list_add(list, &dlink->list);
+
+ return 1;
+}
+
+static int _link_nodes(struct dm_tree_node *parent,
+ struct dm_tree_node *child)
+{
+ if (_nodes_are_linked(parent, child))
+ return 1;
+
+ if (!_link(&parent->uses, child))
+ return 0;
+
+ if (!_link(&child->used_by, parent))
+ return 0;
+
+ return 1;
+}
+
+static void _unlink(struct dm_list *list, struct dm_tree_node *node)
+{
+ struct dm_tree_link *dlink;
+
+ dm_list_iterate_items(dlink, list)
+ if (dlink->node == node) {
+ dm_list_del(&dlink->list);
+ break;
+ }
+}
+
+static void _unlink_nodes(struct dm_tree_node *parent,
+ struct dm_tree_node *child)
+{
+ if (!_nodes_are_linked(parent, child))
+ return;
+
+ _unlink(&parent->uses, child);
+ _unlink(&child->used_by, parent);
+}
+
+static int _add_to_toplevel(struct dm_tree_node *node)
+{
+ return _link_nodes(&node->dtree->root, node);
+}
+
+static void _remove_from_toplevel(struct dm_tree_node *node)
+{
+ _unlink_nodes(&node->dtree->root, node);
+}
+
+static int _add_to_bottomlevel(struct dm_tree_node *node)
+{
+ return _link_nodes(node, &node->dtree->root);
+}
+
+static void _remove_from_bottomlevel(struct dm_tree_node *node)
+{
+ _unlink_nodes(node, &node->dtree->root);
+}
+
+static int _link_tree_nodes(struct dm_tree_node *parent, struct dm_tree_node *child)
+{
+ /* Don't link to root node if child already has a parent */
+ if ((parent == &parent->dtree->root)) {
+ if (dm_tree_node_num_children(child, 1))
+ return 1;
+ } else
+ _remove_from_toplevel(child);
+
+ if ((child == &child->dtree->root)) {
+ if (dm_tree_node_num_children(parent, 0))
+ return 1;
+ } else
+ _remove_from_bottomlevel(parent);
+
+ return _link_nodes(parent, child);
+}
+
+static struct dm_tree_node *_create_dm_tree_node(struct dm_tree *dtree,
+ const char *name,
+ const char *uuid,
+ struct dm_info *info,
+ void *context,
+ uint16_t udev_flags)
+{
+ struct dm_tree_node *node;
+ uint64_t dev;
+
+ if (!(node = dm_pool_zalloc(dtree->mem, sizeof(*node)))) {
+ log_error("_create_dm_tree_node alloc failed");
+ return NULL;
+ }
+
+ node->dtree = dtree;
+
+ node->name = name;
+ node->uuid = uuid;
+ node->info = *info;
+ node->context = context;
+ node->udev_flags = udev_flags;
+ node->activation_priority = 0;
+
+ dm_list_init(&node->uses);
+ dm_list_init(&node->used_by);
+ dm_list_init(&node->props.segs);
+
+ dev = MKDEV(info->major, info->minor);
+
+ if (!dm_hash_insert_binary(dtree->devs, (const char *) &dev,
+ sizeof(dev), node)) {
+ log_error("dtree node hash insertion failed");
+ dm_pool_free(dtree->mem, node);
+ return NULL;
+ }
+
+ if (uuid && *uuid &&
+ !dm_hash_insert(dtree->uuids, uuid, node)) {
+ log_error("dtree uuid hash insertion failed");
+ dm_hash_remove_binary(dtree->devs, (const char *) &dev,
+ sizeof(dev));
+ dm_pool_free(dtree->mem, node);
+ return NULL;
+ }
+
+ return node;
+}
+
+static struct dm_tree_node *_find_dm_tree_node(struct dm_tree *dtree,
+ uint32_t major, uint32_t minor)
+{
+ uint64_t dev = MKDEV(major, minor);
+
+ return dm_hash_lookup_binary(dtree->devs, (const char *) &dev,
+ sizeof(dev));
+}
+
+static struct dm_tree_node *_find_dm_tree_node_by_uuid(struct dm_tree *dtree,
+ const char *uuid)
+{
+ struct dm_tree_node *node;
+
+ if ((node = dm_hash_lookup(dtree->uuids, uuid)))
+ return node;
+
+ if (strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1))
+ return NULL;
+
+ return dm_hash_lookup(dtree->uuids, uuid + sizeof(UUID_PREFIX) - 1);
+}
+
+static int _deps(struct dm_task **dmt, struct dm_pool *mem, uint32_t major, uint32_t minor,
+ const char **name, const char **uuid,
+ struct dm_info *info, struct dm_deps **deps)
+{
+ memset(info, 0, sizeof(*info));
+
+ if (!dm_is_dm_major(major)) {
+ *name = "";
+ *uuid = "";
+ *deps = NULL;
+ info->major = major;
+ info->minor = minor;
+ info->exists = 0;
+ info->live_table = 0;
+ info->inactive_table = 0;
+ info->read_only = 0;
+ return 1;
+ }
+
+ if (!(*dmt = dm_task_create(DM_DEVICE_DEPS))) {
+ log_error("deps dm_task creation failed");
+ return 0;
+ }
+
+ if (!dm_task_set_major(*dmt, major)) {
+ log_error("_deps: failed to set major for (%" PRIu32 ":%" PRIu32 ")",
+ major, minor);
+ goto failed;
+ }
+
+ if (!dm_task_set_minor(*dmt, minor)) {
+ log_error("_deps: failed to set minor for (%" PRIu32 ":%" PRIu32 ")",
+ major, minor);
+ goto failed;
+ }
+
+ if (!dm_task_run(*dmt)) {
+ log_error("_deps: task run failed for (%" PRIu32 ":%" PRIu32 ")",
+ major, minor);
+ goto failed;
+ }
+
+ if (!dm_task_get_info(*dmt, info)) {
+ log_error("_deps: failed to get info for (%" PRIu32 ":%" PRIu32 ")",
+ major, minor);
+ goto failed;
+ }
+
+ if (!info->exists) {
+ *name = "";
+ *uuid = "";
+ *deps = NULL;
+ } else {
+ if (info->major != major) {
+ log_error("Inconsistent dtree major number: %u != %u",
+ major, info->major);
+ goto failed;
+ }
+ if (info->minor != minor) {
+ log_error("Inconsistent dtree minor number: %u != %u",
+ minor, info->minor);
+ goto failed;
+ }
+ if (!(*name = dm_pool_strdup(mem, dm_task_get_name(*dmt)))) {
+ log_error("name pool_strdup failed");
+ goto failed;
+ }
+ if (!(*uuid = dm_pool_strdup(mem, dm_task_get_uuid(*dmt)))) {
+ log_error("uuid pool_strdup failed");
+ goto failed;
+ }
+ *deps = dm_task_get_deps(*dmt);
+ }
+
+ return 1;
+
+failed:
+ dm_task_destroy(*dmt);
+ return 0;
+}
+
+static struct dm_tree_node *_add_dev(struct dm_tree *dtree,
+ struct dm_tree_node *parent,
+ uint32_t major, uint32_t minor,
+ uint16_t udev_flags)
+{
+ struct dm_task *dmt = NULL;
+ struct dm_info info;
+ struct dm_deps *deps = NULL;
+ const char *name = NULL;
+ const char *uuid = NULL;
+ struct dm_tree_node *node = NULL;
+ uint32_t i;
+ int new = 0;
+
+ /* Already in tree? */
+ if (!(node = _find_dm_tree_node(dtree, major, minor))) {
+ if (!_deps(&dmt, dtree->mem, major, minor, &name, &uuid, &info, &deps))
+ return_NULL;
+
+ if (!(node = _create_dm_tree_node(dtree, name, uuid, &info,
+ NULL, udev_flags)))
+ goto_out;
+ new = 1;
+ }
+
+ if (!_link_tree_nodes(parent, node)) {
+ node = NULL;
+ goto_out;
+ }
+
+ /* If node was already in tree, no need to recurse. */
+ if (!new)
+ goto out;
+
+ /* Can't recurse if not a mapped device or there are no dependencies */
+ if (!node->info.exists || !deps->count) {
+ if (!_add_to_bottomlevel(node)) {
+ stack;
+ node = NULL;
+ }
+ goto out;
+ }
+
+ /* Add dependencies to tree */
+ for (i = 0; i < deps->count; i++)
+ if (!_add_dev(dtree, node, MAJOR(deps->device[i]),
+ MINOR(deps->device[i]), udev_flags)) {
+ node = NULL;
+ goto_out;
+ }
+
+out:
+ if (dmt)
+ dm_task_destroy(dmt);
+
+ return node;
+}
+
+static int _node_clear_table(struct dm_tree_node *dnode)
+{
+ struct dm_task *dmt;
+ struct dm_info *info;
+ const char *name;
+ int r;
+
+ if (!(info = &dnode->info)) {
+ log_error("_node_clear_table failed: missing info");
+ return 0;
+ }
+
+ if (!(name = dm_tree_node_get_name(dnode))) {
+ log_error("_node_clear_table failed: missing name");
+ return 0;
+ }
+
+ /* Is there a table? */
+ if (!info->exists || !info->inactive_table)
+ return 1;
+
+ log_verbose("Clearing inactive table %s (%" PRIu32 ":%" PRIu32 ")",
+ name, info->major, info->minor);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_CLEAR))) {
+ log_error("Table clear dm_task creation failed for %s", name);
+ return 0;
+ }
+
+ if (!dm_task_set_major(dmt, info->major) ||
+ !dm_task_set_minor(dmt, info->minor)) {
+ log_error("Failed to set device number for %s table clear", name);
+ dm_task_destroy(dmt);
+ return 0;
+ }
+
+ r = dm_task_run(dmt);
+
+ if (!dm_task_get_info(dmt, info)) {
+ log_error("_node_clear_table failed: info missing after running task for %s", name);
+ r = 0;
+ }
+
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+struct dm_tree_node *dm_tree_add_new_dev(struct dm_tree *dtree,
+ const char *name,
+ const char *uuid,
+ uint32_t major, uint32_t minor,
+ int read_only,
+ int clear_inactive,
+ void *context)
+{
+ struct dm_tree_node *dnode;
+ struct dm_info info;
+ const char *name2;
+ const char *uuid2;
+
+ /* Do we need to add node to tree? */
+ if (!(dnode = dm_tree_find_node_by_uuid(dtree, uuid))) {
+ if (!(name2 = dm_pool_strdup(dtree->mem, name))) {
+ log_error("name pool_strdup failed");
+ return NULL;
+ }
+ if (!(uuid2 = dm_pool_strdup(dtree->mem, uuid))) {
+ log_error("uuid pool_strdup failed");
+ return NULL;
+ }
+
+ info.major = 0;
+ info.minor = 0;
+ info.exists = 0;
+ info.live_table = 0;
+ info.inactive_table = 0;
+ info.read_only = 0;
+
+ if (!(dnode = _create_dm_tree_node(dtree, name2, uuid2, &info,
+ context, 0)))
+ return_NULL;
+
+ /* Attach to root node until a table is supplied */
+ if (!_add_to_toplevel(dnode) || !_add_to_bottomlevel(dnode))
+ return_NULL;
+
+ dnode->props.major = major;
+ dnode->props.minor = minor;
+ dnode->props.new_name = NULL;
+ dnode->props.size_changed = 0;
+ } else if (strcmp(name, dnode->name)) {
+ /* Do we need to rename node? */
+ if (!(dnode->props.new_name = dm_pool_strdup(dtree->mem, name))) {
+ log_error("name pool_strdup failed");
+ return 0;
+ }
+ }
+
+ dnode->props.read_only = read_only ? 1 : 0;
+ dnode->props.read_ahead = DM_READ_AHEAD_AUTO;
+ dnode->props.read_ahead_flags = 0;
+
+ if (clear_inactive && !_node_clear_table(dnode))
+ return_NULL;
+
+ dnode->context = context;
+ dnode->udev_flags = 0;
+
+ return dnode;
+}
+
+struct dm_tree_node *dm_tree_add_new_dev_with_udev_flags(struct dm_tree *dtree,
+ const char *name,
+ const char *uuid,
+ uint32_t major,
+ uint32_t minor,
+ int read_only,
+ int clear_inactive,
+ void *context,
+ uint16_t udev_flags)
+{
+ struct dm_tree_node *node;
+
+ if ((node = dm_tree_add_new_dev(dtree, name, uuid, major, minor, read_only,
+ clear_inactive, context)))
+ node->udev_flags = udev_flags;
+
+ return node;
+}
+
+
+void dm_tree_node_set_read_ahead(struct dm_tree_node *dnode,
+ uint32_t read_ahead,
+ uint32_t read_ahead_flags)
+{
+ dnode->props.read_ahead = read_ahead;
+ dnode->props.read_ahead_flags = read_ahead_flags;
+}
+
+void dm_tree_node_set_presuspend_node(struct dm_tree_node *node,
+ struct dm_tree_node *presuspend_node)
+{
+ node->presuspend_node = presuspend_node;
+}
+
+int dm_tree_add_dev(struct dm_tree *dtree, uint32_t major, uint32_t minor)
+{
+ return _add_dev(dtree, &dtree->root, major, minor, 0) ? 1 : 0;
+}
+
+int dm_tree_add_dev_with_udev_flags(struct dm_tree *dtree, uint32_t major,
+ uint32_t minor, uint16_t udev_flags)
+{
+ return _add_dev(dtree, &dtree->root, major, minor, udev_flags) ? 1 : 0;
+}
+
+const char *dm_tree_node_get_name(const struct dm_tree_node *node)
+{
+ return node->info.exists ? node->name : "";
+}
+
+const char *dm_tree_node_get_uuid(const struct dm_tree_node *node)
+{
+ return node->info.exists ? node->uuid : "";
+}
+
+const struct dm_info *dm_tree_node_get_info(const struct dm_tree_node *node)
+{
+ return &node->info;
+}
+
+void *dm_tree_node_get_context(const struct dm_tree_node *node)
+{
+ return node->context;
+}
+
+int dm_tree_node_size_changed(const struct dm_tree_node *dnode)
+{
+ return dnode->props.size_changed;
+}
+
+int dm_tree_node_num_children(const struct dm_tree_node *node, uint32_t inverted)
+{
+ if (inverted) {
+ if (_nodes_are_linked(&node->dtree->root, node))
+ return 0;
+ return dm_list_size(&node->used_by);
+ }
+
+ if (_nodes_are_linked(node, &node->dtree->root))
+ return 0;
+
+ return dm_list_size(&node->uses);
+}
+
+/*
+ * Returns 1 if no prefix supplied
+ */
+static int _uuid_prefix_matches(const char *uuid, const char *uuid_prefix, size_t uuid_prefix_len)
+{
+ if (!uuid_prefix)
+ return 1;
+
+ if (!strncmp(uuid, uuid_prefix, uuid_prefix_len))
+ return 1;
+
+ /* Handle transition: active device uuids might be missing the prefix */
+ if (uuid_prefix_len <= 4)
+ return 0;
+
+ if (!strncmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1))
+ return 0;
+
+ if (strncmp(uuid_prefix, UUID_PREFIX, sizeof(UUID_PREFIX) - 1))
+ return 0;
+
+ if (!strncmp(uuid, uuid_prefix + sizeof(UUID_PREFIX) - 1, uuid_prefix_len - (sizeof(UUID_PREFIX) - 1)))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Returns 1 if no children.
+ */
+static int _children_suspended(struct dm_tree_node *node,
+ uint32_t inverted,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ struct dm_list *list;
+ struct dm_tree_link *dlink;
+ const struct dm_info *dinfo;
+ const char *uuid;
+
+ if (inverted) {
+ if (_nodes_are_linked(&node->dtree->root, node))
+ return 1;
+ list = &node->used_by;
+ } else {
+ if (_nodes_are_linked(node, &node->dtree->root))
+ return 1;
+ list = &node->uses;
+ }
+
+ dm_list_iterate_items(dlink, list) {
+ if (!(uuid = dm_tree_node_get_uuid(dlink->node))) {
+ stack;
+ continue;
+ }
+
+ /* Ignore if it doesn't belong to this VG */
+ if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ /* Ignore if parent node wants to presuspend this node */
+ if (dlink->node->presuspend_node == node)
+ continue;
+
+ if (!(dinfo = dm_tree_node_get_info(dlink->node))) {
+ stack; /* FIXME Is this normal? */
+ return 0;
+ }
+
+ if (!dinfo->suspended)
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Set major and minor to zero for root of tree.
+ */
+struct dm_tree_node *dm_tree_find_node(struct dm_tree *dtree,
+ uint32_t major,
+ uint32_t minor)
+{
+ if (!major && !minor)
+ return &dtree->root;
+
+ return _find_dm_tree_node(dtree, major, minor);
+}
+
+/*
+ * Set uuid to NULL for root of tree.
+ */
+struct dm_tree_node *dm_tree_find_node_by_uuid(struct dm_tree *dtree,
+ const char *uuid)
+{
+ if (!uuid || !*uuid)
+ return &dtree->root;
+
+ return _find_dm_tree_node_by_uuid(dtree, uuid);
+}
+
+/*
+ * First time set *handle to NULL.
+ * Set inverted to invert the tree.
+ */
+struct dm_tree_node *dm_tree_next_child(void **handle,
+ const struct dm_tree_node *parent,
+ uint32_t inverted)
+{
+ struct dm_list **dlink = (struct dm_list **) handle;
+ const struct dm_list *use_list;
+
+ if (inverted)
+ use_list = &parent->used_by;
+ else
+ use_list = &parent->uses;
+
+ if (!*dlink)
+ *dlink = dm_list_first(use_list);
+ else
+ *dlink = dm_list_next(use_list, *dlink);
+
+ return (*dlink) ? dm_list_item(*dlink, struct dm_tree_link)->node : NULL;
+}
+
+/*
+ * Deactivate a device with its dependencies if the uuid prefix matches.
+ */
+static int _info_by_dev(uint32_t major, uint32_t minor, int with_open_count,
+ struct dm_info *info)
+{
+ struct dm_task *dmt;
+ int r;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
+ log_error("_info_by_dev: dm_task creation failed");
+ return 0;
+ }
+
+ if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
+ log_error("_info_by_dev: Failed to set device number");
+ dm_task_destroy(dmt);
+ return 0;
+ }
+
+ if (!with_open_count && !dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ if ((r = dm_task_run(dmt)))
+ r = dm_task_get_info(dmt, info);
+
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+/* Check if all parent nodes of given node have open_count == 0 */
+static int _node_has_closed_parents(struct dm_tree_node *node,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ struct dm_tree_link *dlink;
+ const struct dm_info *dinfo;
+ struct dm_info info;
+ const char *uuid;
+
+ /* Iterate through parents of this node */
+ dm_list_iterate_items(dlink, &node->used_by) {
+ if (!(uuid = dm_tree_node_get_uuid(dlink->node))) {
+ stack;
+ continue;
+ }
+
+ /* Ignore if it doesn't belong to this VG */
+ if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ if (!(dinfo = dm_tree_node_get_info(dlink->node))) {
+ stack; /* FIXME Is this normal? */
+ return 0;
+ }
+
+ /* Refresh open_count */
+ if (!_info_by_dev(dinfo->major, dinfo->minor, 1, &info) ||
+ !info.exists)
+ continue;
+
+ if (info.open_count)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _deactivate_node(const char *name, uint32_t major, uint32_t minor,
+ uint32_t *cookie, uint16_t udev_flags)
+{
+ struct dm_task *dmt;
+ int r = 0;
+
+ log_verbose("Removing %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_REMOVE))) {
+ log_error("Deactivation dm_task creation failed for %s", name);
+ return 0;
+ }
+
+ if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
+ log_error("Failed to set device number for %s deactivation", name);
+ goto out;
+ }
+
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ if (!dm_task_set_cookie(dmt, cookie, udev_flags))
+ goto out;
+
+ r = dm_task_run(dmt);
+
+ /* FIXME Until kernel returns actual name so dm-ioctl.c can handle it */
+ rm_dev_node(name, dmt->cookie_set &&
+ !(udev_flags & DM_UDEV_DISABLE_DM_RULES_FLAG));
+
+ /* FIXME Remove node from tree or mark invalid? */
+
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _rename_node(const char *old_name, const char *new_name, uint32_t major,
+ uint32_t minor, uint32_t *cookie, uint16_t udev_flags)
+{
+ struct dm_task *dmt;
+ int r = 0;
+
+ log_verbose("Renaming %s (%" PRIu32 ":%" PRIu32 ") to %s", old_name, major, minor, new_name);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_RENAME))) {
+ log_error("Rename dm_task creation failed for %s", old_name);
+ return 0;
+ }
+
+ if (!dm_task_set_name(dmt, old_name)) {
+ log_error("Failed to set name for %s rename.", old_name);
+ goto out;
+ }
+
+ if (!dm_task_set_newname(dmt, new_name))
+ goto_out;
+
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ if (!dm_task_set_cookie(dmt, cookie, udev_flags))
+ goto out;
+
+ r = dm_task_run(dmt);
+
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+/* FIXME Merge with _suspend_node? */
+static int _resume_node(const char *name, uint32_t major, uint32_t minor,
+ uint32_t read_ahead, uint32_t read_ahead_flags,
+ struct dm_info *newinfo, uint32_t *cookie,
+ uint16_t udev_flags)
+{
+ struct dm_task *dmt;
+ int r = 0;
+
+ log_verbose("Resuming %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_RESUME))) {
+ log_error("Suspend dm_task creation failed for %s", name);
+ return 0;
+ }
+
+ /* FIXME Kernel should fill in name on return instead */
+ if (!dm_task_set_name(dmt, name)) {
+ log_error("Failed to set readahead device name for %s", name);
+ goto out;
+ }
+
+ if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
+ log_error("Failed to set device number for %s resumption.", name);
+ goto out;
+ }
+
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ if (!dm_task_set_read_ahead(dmt, read_ahead, read_ahead_flags))
+ log_error("Failed to set read ahead");
+
+ if (!dm_task_set_cookie(dmt, cookie, udev_flags))
+ goto out;
+
+ if ((r = dm_task_run(dmt)))
+ r = dm_task_get_info(dmt, newinfo);
+
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _suspend_node(const char *name, uint32_t major, uint32_t minor,
+ int skip_lockfs, int no_flush, struct dm_info *newinfo)
+{
+ struct dm_task *dmt;
+ int r;
+
+ log_verbose("Suspending %s (%" PRIu32 ":%" PRIu32 ")%s%s",
+ name, major, minor,
+ skip_lockfs ? "" : " with filesystem sync",
+ no_flush ? "" : " with device flush");
+
+ if (!(dmt = dm_task_create(DM_DEVICE_SUSPEND))) {
+ log_error("Suspend dm_task creation failed for %s", name);
+ return 0;
+ }
+
+ if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
+ log_error("Failed to set device number for %s suspension.", name);
+ dm_task_destroy(dmt);
+ return 0;
+ }
+
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ if (skip_lockfs && !dm_task_skip_lockfs(dmt))
+ log_error("Failed to set skip_lockfs flag.");
+
+ if (no_flush && !dm_task_no_flush(dmt))
+ log_error("Failed to set no_flush flag.");
+
+ if ((r = dm_task_run(dmt)))
+ r = dm_task_get_info(dmt, newinfo);
+
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+/*
+ * FIXME Don't attempt to deactivate known internal dependencies.
+ */
+static int _dm_tree_deactivate_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len,
+ unsigned level)
+{
+ int r = 1;
+ void *handle = NULL;
+ struct dm_tree_node *child = dnode;
+ struct dm_info info;
+ const struct dm_info *dinfo;
+ const char *name;
+ const char *uuid;
+
+ while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ if (!(dinfo = dm_tree_node_get_info(child))) {
+ stack;
+ continue;
+ }
+
+ if (!(name = dm_tree_node_get_name(child))) {
+ stack;
+ continue;
+ }
+
+ if (!(uuid = dm_tree_node_get_uuid(child))) {
+ stack;
+ continue;
+ }
+
+ /* Ignore if it doesn't belong to this VG */
+ if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ /* Refresh open_count */
+ if (!_info_by_dev(dinfo->major, dinfo->minor, 1, &info) ||
+ !info.exists)
+ continue;
+
+ /* Also checking open_count in parent nodes of presuspend_node */
+ if (info.open_count ||
+ (child->presuspend_node &&
+ !_node_has_closed_parents(child->presuspend_node,
+ uuid_prefix, uuid_prefix_len))) {
+ /* Only report error from (likely non-internal) dependency at top level */
+ if (!level) {
+ log_error("Unable to deactivate open %s (%" PRIu32
+ ":%" PRIu32 ")", name, info.major,
+ info.minor);
+ r = 0;
+ }
+ continue;
+ }
+
+ /* Suspend child node first if requested */
+ if (child->presuspend_node &&
+ !dm_tree_suspend_children(child, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ if (!_deactivate_node(name, info.major, info.minor,
+ &child->dtree->cookie, child->udev_flags)) {
+ log_error("Unable to deactivate %s (%" PRIu32
+ ":%" PRIu32 ")", name, info.major,
+ info.minor);
+ r = 0;
+ continue;
+ }
+
+ if (dm_tree_node_num_children(child, 0)) {
+ if (!_dm_tree_deactivate_children(child, uuid_prefix, uuid_prefix_len, level + 1))
+ return_0;
+ }
+ }
+
+ return r;
+}
+
+int dm_tree_deactivate_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ return _dm_tree_deactivate_children(dnode, uuid_prefix, uuid_prefix_len, 0);
+}
+
+void dm_tree_skip_lockfs(struct dm_tree_node *dnode)
+{
+ dnode->dtree->skip_lockfs = 1;
+}
+
+void dm_tree_use_no_flush_suspend(struct dm_tree_node *dnode)
+{
+ dnode->dtree->no_flush = 1;
+}
+
+int dm_tree_suspend_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ int r = 1;
+ void *handle = NULL;
+ struct dm_tree_node *child = dnode;
+ struct dm_info info, newinfo;
+ const struct dm_info *dinfo;
+ const char *name;
+ const char *uuid;
+
+ /* Suspend nodes at this level of the tree */
+ while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ if (!(dinfo = dm_tree_node_get_info(child))) {
+ stack;
+ continue;
+ }
+
+ if (!(name = dm_tree_node_get_name(child))) {
+ stack;
+ continue;
+ }
+
+ if (!(uuid = dm_tree_node_get_uuid(child))) {
+ stack;
+ continue;
+ }
+
+ /* Ignore if it doesn't belong to this VG */
+ if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ /* Ensure immediate parents are already suspended */
+ if (!_children_suspended(child, 1, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ if (!_info_by_dev(dinfo->major, dinfo->minor, 0, &info) ||
+ !info.exists || info.suspended)
+ continue;
+
+ if (!_suspend_node(name, info.major, info.minor,
+ child->dtree->skip_lockfs,
+ child->dtree->no_flush, &newinfo)) {
+ log_error("Unable to suspend %s (%" PRIu32
+ ":%" PRIu32 ")", name, info.major,
+ info.minor);
+ r = 0;
+ continue;
+ }
+
+ /* Update cached info */
+ child->info = newinfo;
+ }
+
+ /* Then suspend any child nodes */
+ handle = NULL;
+
+ while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ if (!(uuid = dm_tree_node_get_uuid(child))) {
+ stack;
+ continue;
+ }
+
+ /* Ignore if it doesn't belong to this VG */
+ if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ if (dm_tree_node_num_children(child, 0))
+ if (!dm_tree_suspend_children(child, uuid_prefix, uuid_prefix_len))
+ return_0;
+ }
+
+ return r;
+}
+
+int dm_tree_activate_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ int r = 1;
+ void *handle = NULL;
+ struct dm_tree_node *child = dnode;
+ struct dm_info newinfo;
+ const char *name;
+ const char *uuid;
+ int priority;
+
+ /* Activate children first */
+ while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ if (!(uuid = dm_tree_node_get_uuid(child))) {
+ stack;
+ continue;
+ }
+
+ if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ if (dm_tree_node_num_children(child, 0))
+ if (!dm_tree_activate_children(child, uuid_prefix, uuid_prefix_len))
+ return_0;
+ }
+
+ handle = NULL;
+
+ for (priority = 0; priority < 3; priority++) {
+ while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ if (!(uuid = dm_tree_node_get_uuid(child))) {
+ stack;
+ continue;
+ }
+
+ if (!_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ if (priority != child->activation_priority)
+ continue;
+
+ if (!(name = dm_tree_node_get_name(child))) {
+ stack;
+ continue;
+ }
+
+ /* Rename? */
+ if (child->props.new_name) {
+ if (!_rename_node(name, child->props.new_name, child->info.major,
+ child->info.minor, &child->dtree->cookie,
+ child->udev_flags)) {
+ log_error("Failed to rename %s (%" PRIu32
+ ":%" PRIu32 ") to %s", name, child->info.major,
+ child->info.minor, child->props.new_name);
+ return 0;
+ }
+ child->name = child->props.new_name;
+ child->props.new_name = NULL;
+ }
+
+ if (!child->info.inactive_table && !child->info.suspended)
+ continue;
+
+ if (!_resume_node(child->name, child->info.major, child->info.minor,
+ child->props.read_ahead, child->props.read_ahead_flags,
+ &newinfo, &child->dtree->cookie, child->udev_flags)) {
+ log_error("Unable to resume %s (%" PRIu32
+ ":%" PRIu32 ")", child->name, child->info.major,
+ child->info.minor);
+ r = 0;
+ continue;
+ }
+
+ /* Update cached info */
+ child->info = newinfo;
+ }
+ }
+
+ handle = NULL;
+
+ return r;
+}
+
+static int _create_node(struct dm_tree_node *dnode)
+{
+ int r = 0;
+ struct dm_task *dmt;
+
+ log_verbose("Creating %s", dnode->name);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_CREATE))) {
+ log_error("Create dm_task creation failed for %s", dnode->name);
+ return 0;
+ }
+
+ if (!dm_task_set_name(dmt, dnode->name)) {
+ log_error("Failed to set device name for %s", dnode->name);
+ goto out;
+ }
+
+ if (!dm_task_set_uuid(dmt, dnode->uuid)) {
+ log_error("Failed to set uuid for %s", dnode->name);
+ goto out;
+ }
+
+ if (dnode->props.major &&
+ (!dm_task_set_major(dmt, dnode->props.major) ||
+ !dm_task_set_minor(dmt, dnode->props.minor))) {
+ log_error("Failed to set device number for %s creation.", dnode->name);
+ goto out;
+ }
+
+ if (dnode->props.read_only && !dm_task_set_ro(dmt)) {
+ log_error("Failed to set read only flag for %s", dnode->name);
+ goto out;
+ }
+
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ if ((r = dm_task_run(dmt)))
+ r = dm_task_get_info(dmt, &dnode->info);
+
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+
+static int _build_dev_string(char *devbuf, size_t bufsize, struct dm_tree_node *node)
+{
+ if (!dm_format_dev(devbuf, bufsize, node->info.major, node->info.minor)) {
+ log_error("Failed to format %s device number for %s as dm "
+ "target (%u,%u)",
+ node->name, node->uuid, node->info.major, node->info.minor);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* simplify string emiting code */
+#define EMIT_PARAMS(p, str...)\
+do {\
+ int w;\
+ if ((w = dm_snprintf(params + p, paramsize - (size_t) p, str)) < 0) {\
+ stack; /* Out of space */\
+ return -1;\
+ }\
+ p += w;\
+} while (0)
+
+/*
+ * _emit_areas_line
+ *
+ * Returns: 1 on success, 0 on failure
+ */
+static int _emit_areas_line(struct dm_task *dmt __attribute__((unused)),
+ struct load_segment *seg, char *params,
+ size_t paramsize, int *pos)
+{
+ struct seg_area *area;
+ char devbuf[DM_FORMAT_DEV_BUFSIZE];
+ unsigned first_time = 1;
+ const char *logtype, *synctype;
+ unsigned log_parm_count;
+
+ dm_list_iterate_items(area, &seg->areas) {
+ if (!_build_dev_string(devbuf, sizeof(devbuf), area->dev_node))
+ return_0;
+
+ switch (seg->type) {
+ case SEG_REPLICATOR_DEV:
+ EMIT_PARAMS(*pos, " %d 1 %s", area->rsite_index, devbuf);
+ if (first_time)
+ EMIT_PARAMS(*pos, " nolog 0");
+ else {
+ /* Remote devices */
+ log_parm_count = (area->flags &
+ (DM_NOSYNC | DM_FORCESYNC)) ? 2 : 1;
+
+ if (!area->slog) {
+ devbuf[0] = 0; /* Only core log parameters */
+ logtype = "core";
+ } else {
+ devbuf[0] = ' '; /* Extra space before device name */
+ if (!_build_dev_string(devbuf + 1,
+ sizeof(devbuf) - 1,
+ area->slog))
+ return_0;
+ logtype = "disk";
+ log_parm_count++; /* Extra sync log device name parameter */
+ }
+
+ EMIT_PARAMS(*pos, " %s %u%s %" PRIu64, logtype,
+ log_parm_count, devbuf, area->region_size);
+
+ synctype = (area->flags & DM_NOSYNC) ?
+ " nosync" : (area->flags & DM_FORCESYNC) ?
+ " sync" : NULL;
+
+ if (synctype)
+ EMIT_PARAMS(*pos, "%s", synctype);
+ }
+ break;
+ default:
+ EMIT_PARAMS(*pos, "%s%s %" PRIu64, first_time ? "" : " ",
+ devbuf, area->offset);
+ }
+
+ first_time = 0;
+ }
+
+ return 1;
+}
+
+static int _replicator_emit_segment_line(const struct load_segment *seg, char *params,
+ size_t paramsize, int *pos)
+{
+ const struct load_segment *rlog_seg;
+ struct replicator_site *rsite;
+ char rlogbuf[DM_FORMAT_DEV_BUFSIZE];
+ unsigned parm_count;
+
+ if (!seg->log || !_build_dev_string(rlogbuf, sizeof(rlogbuf), seg->log))
+ return_0;
+
+ rlog_seg = dm_list_item(dm_list_last(&seg->log->props.segs),
+ struct load_segment);
+
+ EMIT_PARAMS(*pos, "%s 4 %s 0 auto %" PRIu64,
+ seg->rlog_type, rlogbuf, rlog_seg->size);
+
+ dm_list_iterate_items(rsite, &seg->rsites) {
+ parm_count = (rsite->fall_behind_data
+ || rsite->fall_behind_ios
+ || rsite->async_timeout) ? 4 : 2;
+
+ EMIT_PARAMS(*pos, " blockdev %u %u %s", parm_count, rsite->rsite_index,
+ (rsite->mode == DM_REPLICATOR_SYNC) ? "synchronous" : "asynchronous");
+
+ if (rsite->fall_behind_data)
+ EMIT_PARAMS(*pos, " data %" PRIu64, rsite->fall_behind_data);
+ else if (rsite->fall_behind_ios)
+ EMIT_PARAMS(*pos, " ios %" PRIu32, rsite->fall_behind_ios);
+ else if (rsite->async_timeout)
+ EMIT_PARAMS(*pos, " timeout %" PRIu32, rsite->async_timeout);
+ }
+
+ return 1;
+}
+
+/*
+ * Returns: 1 on success, 0 on failure
+ */
+static int _mirror_emit_segment_line(struct dm_task *dmt, uint32_t major,
+ uint32_t minor, struct load_segment *seg,
+ uint64_t *seg_start, char *params,
+ size_t paramsize)
+{
+ int block_on_error = 0;
+ int handle_errors = 0;
+ int dm_log_userspace = 0;
+ struct utsname uts;
+ unsigned log_parm_count;
+ int pos = 0;
+ char logbuf[DM_FORMAT_DEV_BUFSIZE];
+ const char *logtype;
+ unsigned kmaj, kmin, krel;
+
+ if (uname(&uts) == -1 || sscanf(uts.release, "%u.%u.%u", &kmaj, &kmin, &krel) != 3) {
+ log_error("Cannot read kernel release version");
+ return 0;
+ }
+
+ if ((seg->flags & DM_BLOCK_ON_ERROR)) {
+ /*
+ * Originally, block_on_error was an argument to the log
+ * portion of the mirror CTR table. It was renamed to
+ * "handle_errors" and now resides in the 'features'
+ * section of the mirror CTR table (i.e. at the end).
+ *
+ * We can identify whether to use "block_on_error" or
+ * "handle_errors" by the dm-mirror module's version
+ * number (>= 1.12) or by the kernel version (>= 2.6.22).
+ */
+ if (KERNEL_VERSION(kmaj, kmin, krel) >= KERNEL_VERSION(2, 6, 22))
+ handle_errors = 1;
+ else
+ block_on_error = 1;
+ }
+
+ if (seg->clustered) {
+ /* Cluster mirrors require a UUID */
+ if (!seg->uuid)
+ return_0;
+
+ /*
+ * Cluster mirrors used to have their own log
+ * types. Now they are accessed through the
+ * userspace log type.
+ *
+ * The dm-log-userspace module was added to the
+ * 2.6.31 kernel.
+ */
+ if (KERNEL_VERSION(kmaj, kmin, krel) >= KERNEL_VERSION(2, 6, 31))
+ dm_log_userspace = 1;
+ }
+
+ /* Region size */
+ log_parm_count = 1;
+
+ /* [no]sync, block_on_error etc. */
+ log_parm_count += hweight32(seg->flags);
+
+ /* "handle_errors" is a feature arg now */
+ if (handle_errors)
+ log_parm_count--;
+
+ /* DM_CORELOG does not count in the param list */
+ if (seg->flags & DM_CORELOG)
+ log_parm_count--;
+
+ if (seg->clustered) {
+ log_parm_count++; /* For UUID */
+
+ if (!dm_log_userspace)
+ EMIT_PARAMS(pos, "clustered-");
+ else
+ /* For clustered-* type field inserted later */
+ log_parm_count++;
+ }
+
+ if (!seg->log)
+ logtype = "core";
+ else {
+ logtype = "disk";
+ log_parm_count++;
+ if (!_build_dev_string(logbuf, sizeof(logbuf), seg->log))
+ return_0;
+ }
+
+ if (dm_log_userspace)
+ EMIT_PARAMS(pos, "userspace %u %s clustered-%s",
+ log_parm_count, seg->uuid, logtype);
+ else
+ EMIT_PARAMS(pos, "%s %u", logtype, log_parm_count);
+
+ if (seg->log)
+ EMIT_PARAMS(pos, " %s", logbuf);
+
+ EMIT_PARAMS(pos, " %u", seg->region_size);
+
+ if (seg->clustered && !dm_log_userspace)
+ EMIT_PARAMS(pos, " %s", seg->uuid);
+
+ if ((seg->flags & DM_NOSYNC))
+ EMIT_PARAMS(pos, " nosync");
+ else if ((seg->flags & DM_FORCESYNC))
+ EMIT_PARAMS(pos, " sync");
+
+ if (block_on_error)
+ EMIT_PARAMS(pos, " block_on_error");
+
+ EMIT_PARAMS(pos, " %u ", seg->mirror_area_count);
+
+ if (_emit_areas_line(dmt, seg, params, paramsize, &pos) <= 0)
+ return_0;
+
+ if (handle_errors)
+ EMIT_PARAMS(pos, " 1 handle_errors");
+
+ return 1;
+}
+
+static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
+ uint32_t minor, struct load_segment *seg,
+ uint64_t *seg_start, char *params,
+ size_t paramsize)
+{
+ int pos = 0;
+ int r;
+ char originbuf[DM_FORMAT_DEV_BUFSIZE], cowbuf[DM_FORMAT_DEV_BUFSIZE];
+
+ switch(seg->type) {
+ case SEG_ERROR:
+ case SEG_ZERO:
+ case SEG_LINEAR:
+ break;
+ case SEG_MIRRORED:
+ /* Mirrors are pretty complicated - now in separate function */
+ r = _mirror_emit_segment_line(dmt, major, minor, seg, seg_start,
+ params, paramsize);
+ if (!r)
+ return_0;
+ break;
+ case SEG_REPLICATOR:
+ if ((r = _replicator_emit_segment_line(seg, params, paramsize,
+ &pos)) <= 0) {
+ stack;
+ return r;
+ }
+ break;
+ case SEG_REPLICATOR_DEV:
+ if (!seg->replicator || !_build_dev_string(originbuf,
+ sizeof(originbuf),
+ seg->replicator))
+ return_0;
+
+ EMIT_PARAMS(pos, "%s %" PRIu64, originbuf, seg->rdevice_index);
+ break;
+ case SEG_SNAPSHOT:
+ case SEG_SNAPSHOT_MERGE:
+ if (!_build_dev_string(originbuf, sizeof(originbuf), seg->origin))
+ return_0;
+ if (!_build_dev_string(cowbuf, sizeof(cowbuf), seg->cow))
+ return_0;
+ EMIT_PARAMS(pos, "%s %s %c %d", originbuf, cowbuf,
+ seg->persistent ? 'P' : 'N', seg->chunk_size);
+ break;
+ case SEG_SNAPSHOT_ORIGIN:
+ if (!_build_dev_string(originbuf, sizeof(originbuf), seg->origin))
+ return_0;
+ EMIT_PARAMS(pos, "%s", originbuf);
+ break;
+ case SEG_STRIPED:
+ EMIT_PARAMS(pos, "%u %u ", seg->area_count, seg->stripe_size);
+ break;
+ case SEG_CRYPT:
+ EMIT_PARAMS(pos, "%s%s%s%s%s %s %" PRIu64 " ", seg->cipher,
+ seg->chainmode ? "-" : "", seg->chainmode ?: "",
+ seg->iv ? "-" : "", seg->iv ?: "", seg->key,
+ seg->iv_offset != DM_CRYPT_IV_DEFAULT ?
+ seg->iv_offset : *seg_start);
+ break;
+ }
+
+ switch(seg->type) {
+ case SEG_ERROR:
+ case SEG_REPLICATOR:
+ case SEG_SNAPSHOT:
+ case SEG_SNAPSHOT_ORIGIN:
+ case SEG_SNAPSHOT_MERGE:
+ case SEG_ZERO:
+ break;
+ case SEG_CRYPT:
+ case SEG_LINEAR:
+ case SEG_REPLICATOR_DEV:
+ case SEG_STRIPED:
+ if ((r = _emit_areas_line(dmt, seg, params, paramsize, &pos)) <= 0) {
+ stack;
+ return r;
+ }
+ break;
+ }
+
+ log_debug("Adding target to (%" PRIu32 ":%" PRIu32 "): %" PRIu64
+ " %" PRIu64 " %s %s", major, minor,
+ *seg_start, seg->size, dm_segtypes[seg->type].target, params);
+
+ if (!dm_task_add_target(dmt, *seg_start, seg->size, dm_segtypes[seg->type].target, params))
+ return_0;
+
+ *seg_start += seg->size;
+
+ return 1;
+}
+
+#undef EMIT_PARAMS
+
+static int _emit_segment(struct dm_task *dmt, uint32_t major, uint32_t minor,
+ struct load_segment *seg, uint64_t *seg_start)
+{
+ char *params;
+ size_t paramsize = 4096;
+ int ret;
+
+ do {
+ if (!(params = dm_malloc(paramsize))) {
+ log_error("Insufficient space for target parameters.");
+ return 0;
+ }
+
+ params[0] = '\0';
+ ret = _emit_segment_line(dmt, major, minor, seg, seg_start,
+ params, paramsize);
+ dm_free(params);
+
+ if (!ret)
+ stack;
+
+ if (ret >= 0)
+ return ret;
+
+ log_debug("Insufficient space in params[%" PRIsize_t
+ "] for target parameters.", paramsize);
+
+ paramsize *= 2;
+ } while (paramsize < MAX_TARGET_PARAMSIZE);
+
+ log_error("Target parameter size too big. Aborting.");
+ return 0;
+}
+
+static int _load_node(struct dm_tree_node *dnode)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ struct load_segment *seg;
+ uint64_t seg_start = 0;
+
+ log_verbose("Loading %s table (%" PRIu32 ":%" PRIu32 ")", dnode->name,
+ dnode->info.major, dnode->info.minor);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_RELOAD))) {
+ log_error("Reload dm_task creation failed for %s", dnode->name);
+ return 0;
+ }
+
+ if (!dm_task_set_major(dmt, dnode->info.major) ||
+ !dm_task_set_minor(dmt, dnode->info.minor)) {
+ log_error("Failed to set device number for %s reload.", dnode->name);
+ goto out;
+ }
+
+ if (dnode->props.read_only && !dm_task_set_ro(dmt)) {
+ log_error("Failed to set read only flag for %s", dnode->name);
+ goto out;
+ }
+
+ if (!dm_task_no_open_count(dmt))
+ log_error("Failed to disable open_count");
+
+ dm_list_iterate_items(seg, &dnode->props.segs)
+ if (!_emit_segment(dmt, dnode->info.major, dnode->info.minor,
+ seg, &seg_start))
+ goto_out;
+
+ if (!dm_task_suppress_identical_reload(dmt))
+ log_error("Failed to suppress reload of identical tables.");
+
+ if ((r = dm_task_run(dmt))) {
+ r = dm_task_get_info(dmt, &dnode->info);
+ if (r && !dnode->info.inactive_table)
+ log_verbose("Suppressed %s identical table reload.",
+ dnode->name);
+
+ if ((dnode->props.size_changed =
+ (dm_task_get_existing_table_size(dmt) == seg_start) ? 0 : 1))
+ log_debug("Table size changed from %" PRIu64 " to %"
+ PRIu64 " for %s",
+ dm_task_get_existing_table_size(dmt),
+ seg_start, dnode->name);
+ }
+
+ dnode->props.segment_count = 0;
+
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+int dm_tree_preload_children(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ int r = 1;
+ void *handle = NULL;
+ struct dm_tree_node *child;
+ struct dm_info newinfo;
+ int update_devs_flag = 0;
+
+ /* Preload children first */
+ while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ /* Skip existing non-device-mapper devices */
+ if (!child->info.exists && child->info.major)
+ continue;
+
+ /* Ignore if it doesn't belong to this VG */
+ if (child->info.exists &&
+ !_uuid_prefix_matches(child->uuid, uuid_prefix, uuid_prefix_len))
+ continue;
+
+ if (dm_tree_node_num_children(child, 0))
+ if (!dm_tree_preload_children(child, uuid_prefix, uuid_prefix_len))
+ return_0;
+
+ /* FIXME Cope if name exists with no uuid? */
+ if (!child->info.exists) {
+ if (!_create_node(child)) {
+ stack;
+ return 0;
+ }
+ }
+
+ if (!child->info.inactive_table && child->props.segment_count) {
+ if (!_load_node(child)) {
+ stack;
+ return 0;
+ }
+ }
+
+ /* Propagate device size change change */
+ if (child->props.size_changed)
+ dnode->props.size_changed = 1;
+
+ /* Resume device immediately if it has parents and its size changed */
+ if (!dm_tree_node_num_children(child, 1) || !child->props.size_changed)
+ continue;
+
+ if (!child->info.inactive_table && !child->info.suspended)
+ continue;
+
+ if (!_resume_node(child->name, child->info.major, child->info.minor,
+ child->props.read_ahead, child->props.read_ahead_flags,
+ &newinfo, &child->dtree->cookie, child->udev_flags)) {
+ log_error("Unable to resume %s (%" PRIu32
+ ":%" PRIu32 ")", child->name, child->info.major,
+ child->info.minor);
+ r = 0;
+ continue;
+ }
+
+ /* Update cached info */
+ child->info = newinfo;
+
+ /*
+ * Prepare for immediate synchronization with udev and flush all stacked
+ * dev node operations if requested by immediate_dev_node property. But
+ * finish processing current level in the tree first.
+ */
+ if (child->props.immediate_dev_node)
+ update_devs_flag = 1;
+
+ }
+
+ handle = NULL;
+
+ if (update_devs_flag) {
+ if (!dm_udev_wait(dm_tree_get_cookie(dnode)))
+ stack;
+ dm_tree_set_cookie(dnode, 0);
+ dm_task_update_nodes();
+ }
+
+ return r;
+}
+
+/*
+ * Returns 1 if unsure.
+ */
+int dm_tree_children_use_uuid(struct dm_tree_node *dnode,
+ const char *uuid_prefix,
+ size_t uuid_prefix_len)
+{
+ void *handle = NULL;
+ struct dm_tree_node *child = dnode;
+ const char *uuid;
+
+ while ((child = dm_tree_next_child(&handle, dnode, 0))) {
+ if (!(uuid = dm_tree_node_get_uuid(child))) {
+ log_error("Failed to get uuid for dtree node.");
+ return 1;
+ }
+
+ if (_uuid_prefix_matches(uuid, uuid_prefix, uuid_prefix_len))
+ return 1;
+
+ if (dm_tree_node_num_children(child, 0))
+ dm_tree_children_use_uuid(child, uuid_prefix, uuid_prefix_len);
+ }
+
+ return 0;
+}
+
+/*
+ * Target functions
+ */
+static struct load_segment *_add_segment(struct dm_tree_node *dnode, unsigned type, uint64_t size)
+{
+ struct load_segment *seg;
+
+ if (!(seg = dm_pool_zalloc(dnode->dtree->mem, sizeof(*seg)))) {
+ log_error("dtree node segment allocation failed");
+ return NULL;
+ }
+
+ seg->type = type;
+ seg->size = size;
+ seg->area_count = 0;
+ dm_list_init(&seg->areas);
+ seg->stripe_size = 0;
+ seg->persistent = 0;
+ seg->chunk_size = 0;
+ seg->cow = NULL;
+ seg->origin = NULL;
+ seg->merge = NULL;
+
+ dm_list_add(&dnode->props.segs, &seg->list);
+ dnode->props.segment_count++;
+
+ return seg;
+}
+
+int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode,
+ uint64_t size,
+ const char *origin_uuid)
+{
+ struct load_segment *seg;
+ struct dm_tree_node *origin_node;
+
+ if (!(seg = _add_segment(dnode, SEG_SNAPSHOT_ORIGIN, size)))
+ return_0;
+
+ if (!(origin_node = dm_tree_find_node_by_uuid(dnode->dtree, origin_uuid))) {
+ log_error("Couldn't find snapshot origin uuid %s.", origin_uuid);
+ return 0;
+ }
+
+ seg->origin = origin_node;
+ if (!_link_tree_nodes(dnode, origin_node))
+ return_0;
+
+ /* Resume snapshot origins after new snapshots */
+ dnode->activation_priority = 1;
+
+ return 1;
+}
+
+static int _add_snapshot_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *origin_uuid,
+ const char *cow_uuid,
+ const char *merge_uuid,
+ int persistent,
+ uint32_t chunk_size)
+{
+ struct load_segment *seg;
+ struct dm_tree_node *origin_node, *cow_node, *merge_node;
+ unsigned seg_type;
+
+ seg_type = !merge_uuid ? SEG_SNAPSHOT : SEG_SNAPSHOT_MERGE;
+
+ if (!(seg = _add_segment(node, seg_type, size)))
+ return_0;
+
+ if (!(origin_node = dm_tree_find_node_by_uuid(node->dtree, origin_uuid))) {
+ log_error("Couldn't find snapshot origin uuid %s.", origin_uuid);
+ return 0;
+ }
+
+ seg->origin = origin_node;
+ if (!_link_tree_nodes(node, origin_node))
+ return_0;
+
+ if (!(cow_node = dm_tree_find_node_by_uuid(node->dtree, cow_uuid))) {
+ log_error("Couldn't find snapshot COW device uuid %s.", cow_uuid);
+ return 0;
+ }
+
+ seg->cow = cow_node;
+ if (!_link_tree_nodes(node, cow_node))
+ return_0;
+
+ seg->persistent = persistent ? 1 : 0;
+ seg->chunk_size = chunk_size;
+
+ if (merge_uuid) {
+ if (!(merge_node = dm_tree_find_node_by_uuid(node->dtree, merge_uuid))) {
+ /* not a pure error, merging snapshot may have been deactivated */
+ log_verbose("Couldn't find merging snapshot uuid %s.", merge_uuid);
+ } else {
+ seg->merge = merge_node;
+ /* must not link merging snapshot, would undermine activation_priority below */
+ }
+
+ /* Resume snapshot-merge (acting origin) after other snapshots */
+ node->activation_priority = 1;
+ if (seg->merge) {
+ /* Resume merging snapshot after snapshot-merge */
+ seg->merge->activation_priority = 2;
+ }
+ }
+
+ return 1;
+}
+
+
+int dm_tree_node_add_snapshot_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *origin_uuid,
+ const char *cow_uuid,
+ int persistent,
+ uint32_t chunk_size)
+{
+ return _add_snapshot_target(node, size, origin_uuid, cow_uuid,
+ NULL, persistent, chunk_size);
+}
+
+int dm_tree_node_add_snapshot_merge_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *origin_uuid,
+ const char *cow_uuid,
+ const char *merge_uuid,
+ uint32_t chunk_size)
+{
+ return _add_snapshot_target(node, size, origin_uuid, cow_uuid,
+ merge_uuid, 1, chunk_size);
+}
+
+int dm_tree_node_add_error_target(struct dm_tree_node *node,
+ uint64_t size)
+{
+ if (!_add_segment(node, SEG_ERROR, size))
+ return_0;
+
+ return 1;
+}
+
+int dm_tree_node_add_zero_target(struct dm_tree_node *node,
+ uint64_t size)
+{
+ if (!_add_segment(node, SEG_ZERO, size))
+ return_0;
+
+ return 1;
+}
+
+int dm_tree_node_add_linear_target(struct dm_tree_node *node,
+ uint64_t size)
+{
+ if (!_add_segment(node, SEG_LINEAR, size))
+ return_0;
+
+ return 1;
+}
+
+int dm_tree_node_add_striped_target(struct dm_tree_node *node,
+ uint64_t size,
+ uint32_t stripe_size)
+{
+ struct load_segment *seg;
+
+ if (!(seg = _add_segment(node, SEG_STRIPED, size)))
+ return_0;
+
+ seg->stripe_size = stripe_size;
+
+ return 1;
+}
+
+int dm_tree_node_add_crypt_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *cipher,
+ const char *chainmode,
+ const char *iv,
+ uint64_t iv_offset,
+ const char *key)
+{
+ struct load_segment *seg;
+
+ if (!(seg = _add_segment(node, SEG_CRYPT, size)))
+ return_0;
+
+ seg->cipher = cipher;
+ seg->chainmode = chainmode;
+ seg->iv = iv;
+ seg->iv_offset = iv_offset;
+ seg->key = key;
+
+ return 1;
+}
+
+int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node,
+ uint32_t region_size,
+ unsigned clustered,
+ const char *log_uuid,
+ unsigned area_count,
+ uint32_t flags)
+{
+ struct dm_tree_node *log_node = NULL;
+ struct load_segment *seg;
+
+ if (!node->props.segment_count) {
+ log_error(INTERNAL_ERROR "Attempt to add target area to missing segment.");
+ return 0;
+ }
+
+ seg = dm_list_item(dm_list_last(&node->props.segs), struct load_segment);
+
+ if (log_uuid) {
+ if (!(seg->uuid = dm_pool_strdup(node->dtree->mem, log_uuid))) {
+ log_error("log uuid pool_strdup failed");
+ return 0;
+ }
+ if (!(flags & DM_CORELOG)) {
+ if (!(log_node = dm_tree_find_node_by_uuid(node->dtree, log_uuid))) {
+ log_error("Couldn't find mirror log uuid %s.", log_uuid);
+ return 0;
+ }
+
+ if (clustered)
+ log_node->props.immediate_dev_node = 1;
+
+ if (!_link_tree_nodes(node, log_node))
+ return_0;
+ }
+ }
+
+ seg->log = log_node;
+ seg->region_size = region_size;
+ seg->clustered = clustered;
+ seg->mirror_area_count = area_count;
+ seg->flags = flags;
+
+ return 1;
+}
+
+int dm_tree_node_add_mirror_target(struct dm_tree_node *node,
+ uint64_t size)
+{
+ if (!_add_segment(node, SEG_MIRRORED, size))
+ return_0;
+
+ return 1;
+}
+
+int dm_tree_node_add_replicator_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *rlog_uuid,
+ const char *rlog_type,
+ unsigned rsite_index,
+ dm_replicator_mode_t mode,
+ uint32_t async_timeout,
+ uint64_t fall_behind_data,
+ uint32_t fall_behind_ios)
+{
+ struct load_segment *rseg;
+ struct replicator_site *rsite;
+
+ /* Local site0 - adds replicator segment and links rlog device */
+ if (rsite_index == REPLICATOR_LOCAL_SITE) {
+ if (node->props.segment_count) {
+ log_error(INTERNAL_ERROR "Attempt to add replicator segment to already used node.");
+ return 0;
+ }
+
+ if (!(rseg = _add_segment(node, SEG_REPLICATOR, size)))
+ return_0;
+
+ if (!(rseg->log = dm_tree_find_node_by_uuid(node->dtree, rlog_uuid))) {
+ log_error("Missing replicator log uuid %s.", rlog_uuid);
+ return 0;
+ }
+
+ if (!_link_tree_nodes(node, rseg->log))
+ return_0;
+
+ if (strcmp(rlog_type, "ringbuffer") != 0) {
+ log_error("Unsupported replicator log type %s.", rlog_type);
+ return 0;
+ }
+
+ if (!(rseg->rlog_type = dm_pool_strdup(node->dtree->mem, rlog_type)))
+ return_0;
+
+ dm_list_init(&rseg->rsites);
+ rseg->rdevice_count = 0;
+ node->activation_priority = 1;
+ }
+
+ /* Add site to segment */
+ if (mode == DM_REPLICATOR_SYNC
+ && (async_timeout || fall_behind_ios || fall_behind_data)) {
+ log_error("Async parameters passed for synchronnous replicator.");
+ return 0;
+ }
+
+ if (node->props.segment_count != 1) {
+ log_error(INTERNAL_ERROR "Attempt to add remote site area before setting replicator log.");
+ return 0;
+ }
+
+ rseg = dm_list_item(dm_list_last(&node->props.segs), struct load_segment);
+ if (rseg->type != SEG_REPLICATOR) {
+ log_error(INTERNAL_ERROR "Attempt to use non replicator segment %s.",
+ dm_segtypes[rseg->type].target);
+ return 0;
+ }
+
+ if (!(rsite = dm_pool_zalloc(node->dtree->mem, sizeof(*rsite)))) {
+ log_error("Failed to allocate remote site segment.");
+ return 0;
+ }
+
+ dm_list_add(&rseg->rsites, &rsite->list);
+ rseg->rsite_count++;
+
+ rsite->mode = mode;
+ rsite->async_timeout = async_timeout;
+ rsite->fall_behind_data = fall_behind_data;
+ rsite->fall_behind_ios = fall_behind_ios;
+ rsite->rsite_index = rsite_index;
+
+ return 1;
+}
+
+/* Appends device node to Replicator */
+int dm_tree_node_add_replicator_dev_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *replicator_uuid,
+ uint64_t rdevice_index,
+ const char *rdev_uuid,
+ unsigned rsite_index,
+ const char *slog_uuid,
+ uint32_t slog_flags,
+ uint32_t slog_region_size)
+{
+ struct seg_area *area;
+ struct load_segment *rseg;
+ struct load_segment *rep_seg;
+
+ if (rsite_index == REPLICATOR_LOCAL_SITE) {
+ /* Site index for local target */
+ if (!(rseg = _add_segment(node, SEG_REPLICATOR_DEV, size)))
+ return_0;
+
+ if (!(rseg->replicator = dm_tree_find_node_by_uuid(node->dtree, replicator_uuid))) {
+ log_error("Missing replicator uuid %s.", replicator_uuid);
+ return 0;
+ }
+
+ /* Local slink0 for replicator must be always initialized first */
+ if (rseg->replicator->props.segment_count != 1) {
+ log_error(INTERNAL_ERROR "Attempt to use non replicator segment.");
+ return 0;
+ }
+
+ rep_seg = dm_list_item(dm_list_last(&rseg->replicator->props.segs), struct load_segment);
+ if (rep_seg->type != SEG_REPLICATOR) {
+ log_error(INTERNAL_ERROR "Attempt to use non replicator segment %s.",
+ dm_segtypes[rep_seg->type].target);
+ return 0;
+ }
+ rep_seg->rdevice_count++;
+
+ if (!_link_tree_nodes(node, rseg->replicator))
+ return_0;
+
+ rseg->rdevice_index = rdevice_index;
+ } else {
+ /* Local slink0 for replicator must be always initialized first */
+ if (node->props.segment_count != 1) {
+ log_error(INTERNAL_ERROR "Attempt to use non replicator-dev segment.");
+ return 0;
+ }
+
+ rseg = dm_list_item(dm_list_last(&node->props.segs), struct load_segment);
+ if (rseg->type != SEG_REPLICATOR_DEV) {
+ log_error(INTERNAL_ERROR "Attempt to use non replicator-dev segment %s.",
+ dm_segtypes[rseg->type].target);
+ return 0;
+ }
+ }
+
+ if (!(slog_flags & DM_CORELOG) && !slog_uuid) {
+ log_error("Unspecified sync log uuid.");
+ return 0;
+ }
+
+ if (!dm_tree_node_add_target_area(node, NULL, rdev_uuid, 0))
+ return_0;
+
+ area = dm_list_item(dm_list_last(&rseg->areas), struct seg_area);
+
+ if (!(slog_flags & DM_CORELOG)) {
+ if (!(area->slog = dm_tree_find_node_by_uuid(node->dtree, slog_uuid))) {
+ log_error("Couldn't find sync log uuid %s.", slog_uuid);
+ return 0;
+ }
+
+ if (!_link_tree_nodes(node, area->slog))
+ return_0;
+ }
+
+ area->flags = slog_flags;
+ area->region_size = slog_region_size;
+ area->rsite_index = rsite_index;
+
+ return 1;
+}
+
+static int _add_area(struct dm_tree_node *node, struct load_segment *seg, struct dm_tree_node *dev_node, uint64_t offset)
+{
+ struct seg_area *area;
+
+ if (!(area = dm_pool_zalloc(node->dtree->mem, sizeof (*area)))) {
+ log_error("Failed to allocate target segment area.");
+ return 0;
+ }
+
+ area->dev_node = dev_node;
+ area->offset = offset;
+
+ dm_list_add(&seg->areas, &area->list);
+ seg->area_count++;
+
+ return 1;
+}
+
+int dm_tree_node_add_target_area(struct dm_tree_node *node,
+ const char *dev_name,
+ const char *uuid,
+ uint64_t offset)
+{
+ struct load_segment *seg;
+ struct stat info;
+ struct dm_tree_node *dev_node;
+
+ if ((!dev_name || !*dev_name) && (!uuid || !*uuid)) {
+ log_error("dm_tree_node_add_target_area called without device");
+ return 0;
+ }
+
+ if (uuid) {
+ if (!(dev_node = dm_tree_find_node_by_uuid(node->dtree, uuid))) {
+ log_error("Couldn't find area uuid %s.", uuid);
+ return 0;
+ }
+ if (!_link_tree_nodes(node, dev_node))
+ return_0;
+ } else {
+ if (stat(dev_name, &info) < 0) {
+ log_error("Device %s not found.", dev_name);
+ return 0;
+ }
+
+ if (!S_ISBLK(info.st_mode)) {
+ log_error("Device %s is not a block device.", dev_name);
+ return 0;
+ }
+
+ /* FIXME Check correct macro use */
+ if (!(dev_node = _add_dev(node->dtree, node, MAJOR(info.st_rdev),
+ MINOR(info.st_rdev), 0)))
+ return_0;
+ }
+
+ if (!node->props.segment_count) {
+ log_error(INTERNAL_ERROR "Attempt to add target area to missing segment.");
+ return 0;
+ }
+
+ seg = dm_list_item(dm_list_last(&node->props.segs), struct load_segment);
+
+ if (!_add_area(node, seg, dev_node, offset))
+ return_0;
+
+ return 1;
+}
+
+void dm_tree_set_cookie(struct dm_tree_node *node, uint32_t cookie)
+{
+ node->dtree->cookie = cookie;
+}
+
+uint32_t dm_tree_get_cookie(struct dm_tree_node *node)
+{
+ return node->dtree->cookie;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+
+#include <sys/file.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+static int _create_dir_recursive(const char *dir)
+{
+ char *orig, *s;
+ int rc, r = 0;
+
+ log_verbose("Creating directory \"%s\"", dir);
+ /* Create parent directories */
+ orig = s = dm_strdup(dir);
+ while ((s = strchr(s, '/')) != NULL) {
+ *s = '\0';
+ if (*orig) {
+ rc = mkdir(orig, 0777);
+ if (rc < 0 && errno != EEXIST) {
+ if (errno != EROFS)
+ log_sys_error("mkdir", orig);
+ goto out;
+ }
+ }
+ *s++ = '/';
+ }
+
+ /* Create final directory */
+ rc = mkdir(dir, 0777);
+ if (rc < 0 && errno != EEXIST) {
+ if (errno != EROFS)
+ log_sys_error("mkdir", orig);
+ goto out;
+ }
+
+ r = 1;
+out:
+ dm_free(orig);
+ return r;
+}
+
+int dm_create_dir(const char *dir)
+{
+ struct stat info;
+
+ if (!*dir)
+ return 1;
+
+ if (stat(dir, &info) < 0)
+ return _create_dir_recursive(dir);
+
+ if (S_ISDIR(info.st_mode))
+ return 1;
+
+ log_error("Directory \"%s\" not found", dir);
+ return 0;
+}
+
+int dm_fclose(FILE *stream)
+{
+ int prev_fail = ferror(stream);
+ int fclose_fail = fclose(stream);
+
+ /* If there was a previous failure, but fclose succeeded,
+ clear errno, since ferror does not set it, and its value
+ may be unrelated to the ferror-reported failure. */
+ if (prev_fail && !fclose_fail)
+ errno = 0;
+
+ return prev_fail || fclose_fail ? EOF : 0;
+}
+
+int dm_create_lockfile(const char *lockfile)
+{
+ int fd, value;
+ size_t bufferlen;
+ ssize_t write_out;
+ struct flock lock;
+ char buffer[50];
+ int retries = 0;
+
+ if((fd = open(lockfile, O_CREAT | O_WRONLY,
+ (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) < 0) {
+ log_error("Cannot open lockfile [%s], error was [%s]",
+ lockfile, strerror(errno));
+ return 0;
+ }
+
+ lock.l_type = F_WRLCK;
+ lock.l_start = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = 0;
+retry_fcntl:
+ if (fcntl(fd, F_SETLK, &lock) < 0) {
+ switch (errno) {
+ case EINTR:
+ goto retry_fcntl;
+ break;
+ case EACCES:
+ case EAGAIN:
+ if (retries == 20) {
+ log_error("Cannot lock lockfile [%s], error was [%s]",
+ lockfile, strerror(errno));
+ break;
+ } else {
+ ++ retries;
+ usleep(1000);
+ goto retry_fcntl;
+ }
+ default:
+ log_error("process is already running");
+ }
+
+ goto fail_close;
+ }
+
+ if (ftruncate(fd, 0) < 0) {
+ log_error("Cannot truncate pidfile [%s], error was [%s]",
+ lockfile, strerror(errno));
+
+ goto fail_close_unlink;
+ }
+
+ memset(buffer, 0, sizeof(buffer));
+ snprintf(buffer, sizeof(buffer)-1, "%u\n", getpid());
+
+ bufferlen = strlen(buffer);
+ write_out = write(fd, buffer, bufferlen);
+
+ if ((write_out < 0) || (write_out == 0 && errno)) {
+ log_error("Cannot write pid to pidfile [%s], error was [%s]",
+ lockfile, strerror(errno));
+
+ goto fail_close_unlink;
+ }
+
+ if ((write_out == 0) || (write_out < bufferlen)) {
+ log_error("Cannot write pid to pidfile [%s], shortwrite of"
+ "[%" PRIsize_t "] bytes, expected [%" PRIsize_t "]\n",
+ lockfile, write_out, bufferlen);
+
+ goto fail_close_unlink;
+ }
+
+ if ((value = fcntl(fd, F_GETFD, 0)) < 0) {
+ log_error("Cannot get close-on-exec flag from pidfile [%s], "
+ "error was [%s]", lockfile, strerror(errno));
+
+ goto fail_close_unlink;
+ }
+ value |= FD_CLOEXEC;
+ if (fcntl(fd, F_SETFD, value) < 0) {
+ log_error("Cannot set close-on-exec flag from pidfile [%s], "
+ "error was [%s]", lockfile, strerror(errno));
+
+ goto fail_close_unlink;
+ }
+
+ return 1;
+
+fail_close_unlink:
+ if (unlink(lockfile))
+ stack;
+fail_close:
+ if (close(fd))
+ stack;
+
+ return 0;
+}
+
+int dm_daemon_is_running(const char* lockfile)
+{
+ int fd;
+ struct flock lock;
+
+ if((fd = open(lockfile, O_RDONLY)) < 0)
+ return 0;
+
+ lock.l_type = F_WRLCK;
+ lock.l_start = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = 0;
+ if (fcntl(fd, F_GETLK, &lock) < 0) {
+ log_error("Cannot check lock status of lockfile [%s], error was [%s]",
+ lockfile, strerror(errno));
+ if (close(fd))
+ stack;
+ return 0;
+ }
+
+ if (close(fd))
+ stack;
+
+ return (lock.l_type == F_UNLCK) ? 0 : 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+
+#include <ctype.h>
+
+/*
+ * Internal flags
+ */
+#define RH_SORT_REQUIRED 0x00000100
+#define RH_HEADINGS_PRINTED 0x00000200
+
+struct dm_report {
+ struct dm_pool *mem;
+
+ /* To report all available types */
+#define REPORT_TYPES_ALL UINT32_MAX
+ uint32_t report_types;
+ const char *output_field_name_prefix;
+ const char *field_prefix;
+ uint32_t flags;
+ const char *separator;
+
+ uint32_t keys_count;
+
+ /* Ordered list of fields needed for this report */
+ struct dm_list field_props;
+
+ /* Rows of report data */
+ struct dm_list rows;
+
+ /* Array of field definitions */
+ const struct dm_report_field_type *fields;
+ const struct dm_report_object_type *types;
+
+ /* To store caller private data */
+ void *private;
+};
+
+/*
+ * Internal per-field flags
+ */
+#define FLD_HIDDEN 0x00000100
+#define FLD_SORT_KEY 0x00000200
+#define FLD_ASCENDING 0x00000400
+#define FLD_DESCENDING 0x00000800
+
+struct field_properties {
+ struct dm_list list;
+ uint32_t field_num;
+ uint32_t sort_posn;
+ int32_t width;
+ const struct dm_report_object_type *type;
+ uint32_t flags;
+};
+
+/*
+ * Report data field
+ */
+struct dm_report_field {
+ struct dm_list list;
+ struct field_properties *props;
+
+ const char *report_string; /* Formatted ready for display */
+ const void *sort_value; /* Raw value for sorting */
+};
+
+struct row {
+ struct dm_list list;
+ struct dm_report *rh;
+ struct dm_list fields; /* Fields in display order */
+ struct dm_report_field *(*sort_fields)[]; /* Fields in sort order */
+};
+
+static const struct dm_report_object_type *_find_type(struct dm_report *rh,
+ uint32_t report_type)
+{
+ const struct dm_report_object_type *t;
+
+ for (t = rh->types; t->data_fn; t++)
+ if (t->id == report_type)
+ return t;
+
+ return NULL;
+}
+
+/*
+ * Data-munging functions to prepare each data type for display and sorting
+ */
+
+int dm_report_field_string(struct dm_report *rh,
+ struct dm_report_field *field, const char **data)
+{
+ char *repstr;
+
+ if (!(repstr = dm_pool_strdup(rh->mem, *data))) {
+ log_error("dm_report_field_string: dm_pool_strdup failed");
+ return 0;
+ }
+
+ field->report_string = repstr;
+ field->sort_value = (const void *) field->report_string;
+
+ return 1;
+}
+
+int dm_report_field_int(struct dm_report *rh,
+ struct dm_report_field *field, const int *data)
+{
+ const int value = *data;
+ uint64_t *sortval;
+ char *repstr;
+
+ if (!(repstr = dm_pool_zalloc(rh->mem, 13))) {
+ log_error("dm_report_field_int: dm_pool_alloc failed");
+ return 0;
+ }
+
+ if (!(sortval = dm_pool_alloc(rh->mem, sizeof(int64_t)))) {
+ log_error("dm_report_field_int: dm_pool_alloc failed");
+ return 0;
+ }
+
+ if (dm_snprintf(repstr, 12, "%d", value) < 0) {
+ log_error("dm_report_field_int: int too big: %d", value);
+ return 0;
+ }
+
+ *sortval = (const uint64_t) value;
+ field->sort_value = sortval;
+ field->report_string = repstr;
+
+ return 1;
+}
+
+int dm_report_field_uint32(struct dm_report *rh,
+ struct dm_report_field *field, const uint32_t *data)
+{
+ const uint32_t value = *data;
+ uint64_t *sortval;
+ char *repstr;
+
+ if (!(repstr = dm_pool_zalloc(rh->mem, 12))) {
+ log_error("dm_report_field_uint32: dm_pool_alloc failed");
+ return 0;
+ }
+
+ if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
+ log_error("dm_report_field_uint32: dm_pool_alloc failed");
+ return 0;
+ }
+
+ if (dm_snprintf(repstr, 11, "%u", value) < 0) {
+ log_error("dm_report_field_uint32: uint32 too big: %u", value);
+ return 0;
+ }
+
+ *sortval = (const uint64_t) value;
+ field->sort_value = sortval;
+ field->report_string = repstr;
+
+ return 1;
+}
+
+int dm_report_field_int32(struct dm_report *rh,
+ struct dm_report_field *field, const int32_t *data)
+{
+ const int32_t value = *data;
+ uint64_t *sortval;
+ char *repstr;
+
+ if (!(repstr = dm_pool_zalloc(rh->mem, 13))) {
+ log_error("dm_report_field_int32: dm_pool_alloc failed");
+ return 0;
+ }
+
+ if (!(sortval = dm_pool_alloc(rh->mem, sizeof(int64_t)))) {
+ log_error("dm_report_field_int32: dm_pool_alloc failed");
+ return 0;
+ }
+
+ if (dm_snprintf(repstr, 12, "%d", value) < 0) {
+ log_error("dm_report_field_int32: int32 too big: %d", value);
+ return 0;
+ }
+
+ *sortval = (const uint64_t) value;
+ field->sort_value = sortval;
+ field->report_string = repstr;
+
+ return 1;
+}
+
+int dm_report_field_uint64(struct dm_report *rh,
+ struct dm_report_field *field, const uint64_t *data)
+{
+ const uint64_t value = *data;
+ uint64_t *sortval;
+ char *repstr;
+
+ if (!(repstr = dm_pool_zalloc(rh->mem, 22))) {
+ log_error("dm_report_field_uint64: dm_pool_alloc failed");
+ return 0;
+ }
+
+ if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
+ log_error("dm_report_field_uint64: dm_pool_alloc failed");
+ return 0;
+ }
+
+ if (dm_snprintf(repstr, 21, "%" PRIu64 , value) < 0) {
+ log_error("dm_report_field_uint64: uint64 too big: %" PRIu64, value);
+ return 0;
+ }
+
+ *sortval = value;
+ field->sort_value = sortval;
+ field->report_string = repstr;
+
+ return 1;
+}
+
+/*
+ * Helper functions for custom report functions
+ */
+void dm_report_field_set_value(struct dm_report_field *field, const void *value, const void *sortvalue)
+{
+ field->report_string = (const char *) value;
+ field->sort_value = sortvalue ? : value;
+}
+
+/*
+ * show help message
+ */
+static void _display_fields(struct dm_report *rh)
+{
+ uint32_t f;
+ const struct dm_report_object_type *type;
+ const char *desc, *last_desc = "";
+ size_t id_len = 0;
+
+ for (f = 0; rh->fields[f].report_fn; f++)
+ if (strlen(rh->fields[f].id) > id_len)
+ id_len = strlen(rh->fields[f].id);
+
+
+ for (type = rh->types; type->data_fn; type++)
+ if (strlen(type->prefix) + 3 > id_len)
+ id_len = strlen(type->prefix) + 3;
+
+ for (f = 0; rh->fields[f].report_fn; f++) {
+ if ((type = _find_type(rh, rh->fields[f].type)) && type->desc)
+ desc = type->desc;
+ else
+ desc = " ";
+ if (desc != last_desc) {
+ if (*last_desc)
+ log_warn(" ");
+ log_warn("%s Fields", desc);
+ log_warn("%*.*s", (int) strlen(desc) + 7,
+ (int) strlen(desc) + 7,
+ "-------------------------------------------------------------------------------");
+ log_warn(" %sall%-*s - %s", type->prefix,
+ (int) (id_len - 3 - strlen(type->prefix)), "",
+ "All fields in this section.");
+ }
+
+ /* FIXME Add line-wrapping at terminal width (or 80 cols) */
+ log_warn(" %-*s - %s", (int) id_len, rh->fields[f].id, rh->fields[f].desc);
+ last_desc = desc;
+ }
+}
+
+/*
+ * Initialise report handle
+ */
+static int _copy_field(struct dm_report *rh, struct field_properties *dest,
+ uint32_t field_num)
+{
+ dest->field_num = field_num;
+ dest->width = rh->fields[field_num].width;
+ dest->flags = rh->fields[field_num].flags & DM_REPORT_FIELD_MASK;
+
+ /* set object type method */
+ dest->type = _find_type(rh, rh->fields[field_num].type);
+ if (!dest->type) {
+ log_error("dm_report: field not match: %s",
+ rh->fields[field_num].id);
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct field_properties * _add_field(struct dm_report *rh,
+ uint32_t field_num, uint32_t flags)
+{
+ struct field_properties *fp;
+
+ if (!(fp = dm_pool_zalloc(rh->mem, sizeof(struct field_properties)))) {
+ log_error("dm_report: struct field_properties allocation "
+ "failed");
+ return NULL;
+ }
+
+ if (!_copy_field(rh, fp, field_num)) {
+ stack;
+ dm_pool_free(rh->mem, fp);
+ return NULL;
+ }
+
+ fp->flags |= flags;
+
+ /*
+ * Place hidden fields at the front so dm_list_end() will
+ * tell us when we've reached the last visible field.
+ */
+ if (fp->flags & FLD_HIDDEN)
+ dm_list_add_h(&rh->field_props, &fp->list);
+ else
+ dm_list_add(&rh->field_props, &fp->list);
+
+ return fp;
+}
+
+/*
+ * Compare name1 against name2 or prefix plus name2
+ * name2 is not necessarily null-terminated.
+ * len2 is the length of name2.
+ */
+static int _is_same_field(const char *name1, const char *name2,
+ size_t len2, const char *prefix)
+{
+ size_t prefix_len;
+
+ /* Exact match? */
+ if (!strncasecmp(name1, name2, len2) && strlen(name1) == len2)
+ return 1;
+
+ /* Match including prefix? */
+ prefix_len = strlen(prefix);
+ if (!strncasecmp(prefix, name1, prefix_len) &&
+ !strncasecmp(name1 + prefix_len, name2, len2) &&
+ strlen(name1) == prefix_len + len2)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Check for a report type prefix + "all" match.
+ */
+static uint32_t _all_match(struct dm_report *rh, const char *field, size_t flen)
+{
+ size_t prefix_len;
+ const struct dm_report_object_type *t;
+ char prefixed_all[32];
+
+ if (!strncasecmp(field, "all", 3) && flen == 3) {
+ if (strlen(rh->field_prefix)) {
+ strcpy(prefixed_all, rh->field_prefix);
+ strcat(prefixed_all, "all");
+ /*
+ * Add also prefix to receive all attributes
+ * (e.g.LABEL/PVS use the same prefix)
+ */
+ return rh->report_types |
+ _all_match(rh, prefixed_all,
+ strlen(prefixed_all));
+ } else
+ return (rh->report_types)
+ ? rh->report_types : REPORT_TYPES_ALL;
+ }
+
+ for (t = rh->types; t->data_fn; t++) {
+ prefix_len = strlen(t->prefix);
+ if (!strncasecmp(t->prefix, field, prefix_len) &&
+ !strncasecmp(field + prefix_len, "all", 3) &&
+ flen == prefix_len + 3)
+ return t->id;
+ }
+
+ return 0;
+}
+
+/*
+ * Add all fields with a matching type.
+ */
+static int _add_all_fields(struct dm_report *rh, uint32_t type)
+{
+ uint32_t f;
+
+ for (f = 0; rh->fields[f].report_fn; f++)
+ if ((rh->fields[f].type & type) && !_add_field(rh, f, 0))
+ return 0;
+
+ return 1;
+}
+
+static int _field_match(struct dm_report *rh, const char *field, size_t flen,
+ unsigned report_type_only)
+{
+ uint32_t f, type;
+
+ if (!flen)
+ return 0;
+
+ for (f = 0; rh->fields[f].report_fn; f++)
+ if (_is_same_field(rh->fields[f].id, field, flen,
+ rh->field_prefix)) {
+ if (report_type_only) {
+ rh->report_types |= rh->fields[f].type;
+ return 1;
+ } else
+ return _add_field(rh, f, 0) ? 1 : 0;
+ }
+
+ if ((type = _all_match(rh, field, flen))) {
+ if (report_type_only) {
+ rh->report_types |= type;
+ return 1;
+ } else
+ return _add_all_fields(rh, type);
+ }
+
+ return 0;
+}
+
+static int _add_sort_key(struct dm_report *rh, uint32_t field_num,
+ uint32_t flags, unsigned report_type_only)
+{
+ struct field_properties *fp, *found = NULL;
+
+ dm_list_iterate_items(fp, &rh->field_props) {
+ if (fp->field_num == field_num) {
+ found = fp;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (report_type_only)
+ rh->report_types |= rh->fields[field_num].type;
+ else if (!(found = _add_field(rh, field_num, FLD_HIDDEN)))
+ return_0;
+ }
+
+ if (report_type_only)
+ return 1;
+
+ if (found->flags & FLD_SORT_KEY) {
+ log_error("dm_report: Ignoring duplicate sort field: %s",
+ rh->fields[field_num].id);
+ return 1;
+ }
+
+ found->flags |= FLD_SORT_KEY;
+ found->sort_posn = rh->keys_count++;
+ found->flags |= flags;
+
+ return 1;
+}
+
+static int _key_match(struct dm_report *rh, const char *key, size_t len,
+ unsigned report_type_only)
+{
+ uint32_t f;
+ uint32_t flags;
+
+ if (!len)
+ return 0;
+
+ if (*key == '+') {
+ key++;
+ len--;
+ flags = FLD_ASCENDING;
+ } else if (*key == '-') {
+ key++;
+ len--;
+ flags = FLD_DESCENDING;
+ } else
+ flags = FLD_ASCENDING;
+
+ if (!len) {
+ log_error("dm_report: Missing sort field name");
+ return 0;
+ }
+
+ for (f = 0; rh->fields[f].report_fn; f++)
+ if (_is_same_field(rh->fields[f].id, key, len,
+ rh->field_prefix))
+ return _add_sort_key(rh, f, flags, report_type_only);
+
+ return 0;
+}
+
+static int _parse_fields(struct dm_report *rh, const char *format,
+ unsigned report_type_only)
+{
+ const char *ws; /* Word start */
+ const char *we = format; /* Word end */
+
+ while (*we) {
+ /* Allow consecutive commas */
+ while (*we && *we == ',')
+ we++;
+
+ /* start of the field name */
+ ws = we;
+ while (*we && *we != ',')
+ we++;
+
+ if (!_field_match(rh, ws, (size_t) (we - ws), report_type_only)) {
+ _display_fields(rh);
+ log_warn(" ");
+ if (strcasecmp(ws, "help") && strcmp(ws, "?"))
+ log_error("Unrecognised field: %.*s",
+ (int) (we - ws), ws);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int _parse_keys(struct dm_report *rh, const char *keys,
+ unsigned report_type_only)
+{
+ const char *ws; /* Word start */
+ const char *we = keys; /* Word end */
+
+ while (*we) {
+ /* Allow consecutive commas */
+ while (*we && *we == ',')
+ we++;
+ ws = we;
+ while (*we && *we != ',')
+ we++;
+ if (!_key_match(rh, ws, (size_t) (we - ws), report_type_only)) {
+ log_error("dm_report: Unrecognised field: %.*s",
+ (int) (we - ws), ws);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+struct dm_report *dm_report_init(uint32_t *report_types,
+ const struct dm_report_object_type *types,
+ const struct dm_report_field_type *fields,
+ const char *output_fields,
+ const char *output_separator,
+ uint32_t output_flags,
+ const char *sort_keys,
+ void *private_data)
+{
+ struct dm_report *rh;
+ const struct dm_report_object_type *type;
+
+ if (!(rh = dm_zalloc(sizeof(*rh)))) {
+ log_error("dm_report_init: dm_malloc failed");
+ return 0;
+ }
+
+ /*
+ * rh->report_types is updated in _parse_fields() and _parse_keys()
+ * to contain all types corresponding to the fields specified by
+ * fields or keys.
+ */
+ if (report_types)
+ rh->report_types = *report_types;
+
+ rh->separator = output_separator;
+ rh->fields = fields;
+ rh->types = types;
+ rh->private = private_data;
+
+ rh->flags |= output_flags & DM_REPORT_OUTPUT_MASK;
+
+ /* With columns_as_rows we must buffer and not align. */
+ if (output_flags & DM_REPORT_OUTPUT_COLUMNS_AS_ROWS) {
+ if (!(output_flags & DM_REPORT_OUTPUT_BUFFERED))
+ rh->flags |= DM_REPORT_OUTPUT_BUFFERED;
+ if (output_flags & DM_REPORT_OUTPUT_ALIGNED)
+ rh->flags &= ~DM_REPORT_OUTPUT_ALIGNED;
+ }
+
+ if (output_flags & DM_REPORT_OUTPUT_BUFFERED)
+ rh->flags |= RH_SORT_REQUIRED;
+
+ dm_list_init(&rh->field_props);
+ dm_list_init(&rh->rows);
+
+ if ((type = _find_type(rh, rh->report_types)) && type->prefix)
+ rh->field_prefix = type->prefix;
+ else
+ rh->field_prefix = "";
+
+ if (!(rh->mem = dm_pool_create("report", 10 * 1024))) {
+ log_error("dm_report_init: allocation of memory pool failed");
+ dm_free(rh);
+ return NULL;
+ }
+
+ /*
+ * To keep the code needed to add the "all" field to a minimum, we parse
+ * the field lists twice. The first time we only update the report type.
+ * FIXME Use one pass instead and expand the "all" field afterwards.
+ */
+ if (!_parse_fields(rh, output_fields, 1) ||
+ !_parse_keys(rh, sort_keys, 1)) {
+ dm_report_free(rh);
+ return NULL;
+ }
+
+ /* Generate list of fields for output based on format string & flags */
+ if (!_parse_fields(rh, output_fields, 0) ||
+ !_parse_keys(rh, sort_keys, 0)) {
+ dm_report_free(rh);
+ return NULL;
+ }
+
+ /* Return updated types value for further compatility check by caller */
+ if (report_types)
+ *report_types = rh->report_types;
+
+ return rh;
+}
+
+void dm_report_free(struct dm_report *rh)
+{
+ dm_pool_destroy(rh->mem);
+ dm_free(rh);
+}
+
+static char *_toupperstr(char *str)
+{
+ char *u = str;
+
+ do
+ *u = toupper(*u);
+ while (*u++);
+
+ return str;
+}
+
+int dm_report_set_output_field_name_prefix(struct dm_report *rh, const char *output_field_name_prefix)
+{
+ char *prefix;
+
+ if (!(prefix = dm_pool_strdup(rh->mem, output_field_name_prefix))) {
+ log_error("dm_report_set_output_field_name_prefix: dm_pool_strdup failed");
+ return 0;
+ }
+
+ rh->output_field_name_prefix = _toupperstr(prefix);
+
+ return 1;
+}
+
+/*
+ * Create a row of data for an object
+ */
+static void * _report_get_field_data(struct dm_report *rh,
+ struct field_properties *fp, void *object)
+{
+ void *ret = fp->type->data_fn(object);
+
+ if (!ret)
+ return NULL;
+
+ return ret + rh->fields[fp->field_num].offset;
+}
+
+int dm_report_object(struct dm_report *rh, void *object)
+{
+ struct field_properties *fp;
+ struct row *row;
+ struct dm_report_field *field;
+ void *data = NULL;
+
+ if (!(row = dm_pool_zalloc(rh->mem, sizeof(*row)))) {
+ log_error("dm_report_object: struct row allocation failed");
+ return 0;
+ }
+
+ row->rh = rh;
+
+ if ((rh->flags & RH_SORT_REQUIRED) &&
+ !(row->sort_fields =
+ dm_pool_zalloc(rh->mem, sizeof(struct dm_report_field *) *
+ rh->keys_count))) {
+ log_error("dm_report_object: "
+ "row sort value structure allocation failed");
+ return 0;
+ }
+
+ dm_list_init(&row->fields);
+ dm_list_add(&rh->rows, &row->list);
+
+ /* For each field to be displayed, call its report_fn */
+ dm_list_iterate_items(fp, &rh->field_props) {
+ if (!(field = dm_pool_zalloc(rh->mem, sizeof(*field)))) {
+ log_error("dm_report_object: "
+ "struct dm_report_field allocation failed");
+ return 0;
+ }
+ field->props = fp;
+
+ data = _report_get_field_data(rh, fp, object);
+ if (!data)
+ return 0;
+
+ if (!rh->fields[fp->field_num].report_fn(rh, rh->mem,
+ field, data,
+ rh->private)) {
+ log_error("dm_report_object: "
+ "report function failed for field %s",
+ rh->fields[fp->field_num].id);
+ return 0;
+ }
+
+ if ((strlen(field->report_string) > field->props->width))
+ field->props->width = strlen(field->report_string);
+
+ if ((rh->flags & RH_SORT_REQUIRED) &&
+ (field->props->flags & FLD_SORT_KEY)) {
+ (*row->sort_fields)[field->props->sort_posn] = field;
+ }
+ dm_list_add(&row->fields, &field->list);
+ }
+
+ if (!(rh->flags & DM_REPORT_OUTPUT_BUFFERED))
+ return dm_report_output(rh);
+
+ return 1;
+}
+
+/*
+ * Print row of headings
+ */
+static int _report_headings(struct dm_report *rh)
+{
+ struct field_properties *fp;
+ const char *heading;
+ char *buf = NULL;
+ size_t buf_size = 0;
+
+ if (rh->flags & RH_HEADINGS_PRINTED)
+ return 1;
+
+ rh->flags |= RH_HEADINGS_PRINTED;
+
+ if (!(rh->flags & DM_REPORT_OUTPUT_HEADINGS))
+ return 1;
+
+ if (!dm_pool_begin_object(rh->mem, 128)) {
+ log_error("dm_report: "
+ "dm_pool_begin_object failed for headings");
+ return 0;
+ }
+
+ dm_list_iterate_items(fp, &rh->field_props) {
+ if (buf_size < fp->width)
+ buf_size = fp->width;
+ }
+ /* Including trailing '\0'! */
+ buf_size++;
+
+ if (!(buf = dm_malloc(buf_size))) {
+ log_error("dm_report: Could not allocate memory for heading buffer.");
+ goto bad;
+ }
+
+ /* First heading line */
+ dm_list_iterate_items(fp, &rh->field_props) {
+ if (fp->flags & FLD_HIDDEN)
+ continue;
+
+ heading = rh->fields[fp->field_num].heading;
+ if (rh->flags & DM_REPORT_OUTPUT_ALIGNED) {
+ if (dm_snprintf(buf, buf_size, "%-*.*s",
+ fp->width, fp->width, heading) < 0) {
+ log_error("dm_report: snprintf heading failed");
+ goto bad;
+ }
+ if (!dm_pool_grow_object(rh->mem, buf, fp->width)) {
+ log_error("dm_report: Failed to generate report headings for printing");
+ goto bad;
+ }
+ } else if (!dm_pool_grow_object(rh->mem, heading, 0)) {
+ log_error("dm_report: Failed to generate report headings for printing");
+ goto bad;
+ }
+
+ if (!dm_list_end(&rh->field_props, &fp->list))
+ if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
+ log_error("dm_report: Failed to generate report headings for printing");
+ goto bad;
+ }
+ }
+ if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
+ log_error("dm_report: Failed to generate report headings for printing");
+ goto bad;
+ }
+ log_print("%s", (char *) dm_pool_end_object(rh->mem));
+
+ dm_free(buf);
+
+ return 1;
+
+ bad:
+ dm_free(buf);
+ dm_pool_abandon_object(rh->mem);
+ return 0;
+}
+
+/*
+ * Sort rows of data
+ */
+static int _row_compare(const void *a, const void *b)
+{
+ const struct row *rowa = *(const struct row * const *) a;
+ const struct row *rowb = *(const struct row * const *) b;
+ const struct dm_report_field *sfa, *sfb;
+ uint32_t cnt;
+
+ for (cnt = 0; cnt < rowa->rh->keys_count; cnt++) {
+ sfa = (*rowa->sort_fields)[cnt];
+ sfb = (*rowb->sort_fields)[cnt];
+ if (sfa->props->flags & DM_REPORT_FIELD_TYPE_NUMBER) {
+ const uint64_t numa =
+ *(const uint64_t *) sfa->sort_value;
+ const uint64_t numb =
+ *(const uint64_t *) sfb->sort_value;
+
+ if (numa == numb)
+ continue;
+
+ if (sfa->props->flags & FLD_ASCENDING) {
+ return (numa > numb) ? 1 : -1;
+ } else { /* FLD_DESCENDING */
+ return (numa < numb) ? 1 : -1;
+ }
+ } else { /* DM_REPORT_FIELD_TYPE_STRING */
+ const char *stra = (const char *) sfa->sort_value;
+ const char *strb = (const char *) sfb->sort_value;
+ int cmp = strcmp(stra, strb);
+
+ if (!cmp)
+ continue;
+
+ if (sfa->props->flags & FLD_ASCENDING) {
+ return (cmp > 0) ? 1 : -1;
+ } else { /* FLD_DESCENDING */
+ return (cmp < 0) ? 1 : -1;
+ }
+ }
+ }
+
+ return 0; /* Identical */
+}
+
+static int _sort_rows(struct dm_report *rh)
+{
+ struct row *(*rows)[];
+ uint32_t count = 0;
+ struct row *row;
+
+ if (!(rows = dm_pool_alloc(rh->mem, sizeof(**rows) *
+ dm_list_size(&rh->rows)))) {
+ log_error("dm_report: sort array allocation failed");
+ return 0;
+ }
+
+ dm_list_iterate_items(row, &rh->rows)
+ (*rows)[count++] = row;
+
+ qsort(rows, count, sizeof(**rows), _row_compare);
+
+ dm_list_init(&rh->rows);
+ while (count--)
+ dm_list_add_h(&rh->rows, &(*rows)[count]->list);
+
+ return 1;
+}
+
+/*
+ * Produce report output
+ */
+static int _output_field(struct dm_report *rh, struct dm_report_field *field)
+{
+ char *field_id;
+ int32_t width;
+ uint32_t align;
+ const char *repstr;
+ char *buf = NULL;
+ size_t buf_size = 0;
+
+ if (rh->flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX) {
+ if (!(field_id = dm_strdup(rh->fields[field->props->field_num].id))) {
+ log_error("dm_report: Failed to copy field name");
+ return 0;
+ }
+
+ if (!dm_pool_grow_object(rh->mem, rh->output_field_name_prefix, 0)) {
+ log_error("dm_report: Unable to extend output line");
+ dm_free(field_id);
+ return 0;
+ }
+
+ if (!dm_pool_grow_object(rh->mem, _toupperstr(field_id), 0)) {
+ log_error("dm_report: Unable to extend output line");
+ dm_free(field_id);
+ return 0;
+ }
+
+ dm_free(field_id);
+
+ if (!dm_pool_grow_object(rh->mem, "=", 1)) {
+ log_error("dm_report: Unable to extend output line");
+ return 0;
+ }
+
+ if (!(rh->flags & DM_REPORT_OUTPUT_FIELD_UNQUOTED) &&
+ !dm_pool_grow_object(rh->mem, "\'", 1)) {
+ log_error("dm_report: Unable to extend output line");
+ return 0;
+ }
+ }
+
+ repstr = field->report_string;
+ width = field->props->width;
+ if (!(rh->flags & DM_REPORT_OUTPUT_ALIGNED)) {
+ if (!dm_pool_grow_object(rh->mem, repstr, 0)) {
+ log_error("dm_report: Unable to extend output line");
+ return 0;
+ }
+ } else {
+ if (!(align = field->props->flags & DM_REPORT_FIELD_ALIGN_MASK))
+ align = (field->props->flags & DM_REPORT_FIELD_TYPE_NUMBER) ?
+ DM_REPORT_FIELD_ALIGN_RIGHT : DM_REPORT_FIELD_ALIGN_LEFT;
+
+ /* Including trailing '\0'! */
+ buf_size = width + 1;
+ if (!(buf = dm_malloc(buf_size))) {
+ log_error("dm_report: Could not allocate memory for output line buffer.");
+ return 0;
+ }
+
+ if (align & DM_REPORT_FIELD_ALIGN_LEFT) {
+ if (dm_snprintf(buf, buf_size, "%-*.*s",
+ width, width, repstr) < 0) {
+ log_error("dm_report: left-aligned snprintf() failed");
+ goto bad;
+ }
+ if (!dm_pool_grow_object(rh->mem, buf, width)) {
+ log_error("dm_report: Unable to extend output line");
+ goto bad;
+ }
+ } else if (align & DM_REPORT_FIELD_ALIGN_RIGHT) {
+ if (dm_snprintf(buf, buf_size, "%*.*s",
+ width, width, repstr) < 0) {
+ log_error("dm_report: right-aligned snprintf() failed");
+ goto bad;
+ }
+ if (!dm_pool_grow_object(rh->mem, buf, width)) {
+ log_error("dm_report: Unable to extend output line");
+ goto bad;
+ }
+ }
+ }
+
+ if ((rh->flags & DM_REPORT_OUTPUT_FIELD_NAME_PREFIX) &&
+ !(rh->flags & DM_REPORT_OUTPUT_FIELD_UNQUOTED))
+ if (!dm_pool_grow_object(rh->mem, "\'", 1)) {
+ log_error("dm_report: Unable to extend output line");
+ goto bad;
+ }
+
+ dm_free(buf);
+ return 1;
+
+bad:
+ dm_free(buf);
+ return 0;
+}
+
+static int _output_as_rows(struct dm_report *rh)
+{
+ struct field_properties *fp;
+ struct dm_report_field *field;
+ struct row *row;
+
+ if (!dm_pool_begin_object(rh->mem, 512)) {
+ log_error("dm_report: Unable to allocate output line");
+ return 0;
+ }
+
+ dm_list_iterate_items(fp, &rh->field_props) {
+ if (fp->flags & FLD_HIDDEN) {
+ dm_list_iterate_items(row, &rh->rows) {
+ field = dm_list_item(dm_list_first(&row->fields), struct dm_report_field);
+ dm_list_del(&field->list);
+ }
+ continue;
+ }
+
+ if ((rh->flags & DM_REPORT_OUTPUT_HEADINGS)) {
+ if (!dm_pool_grow_object(rh->mem, rh->fields[fp->field_num].heading, 0)) {
+ log_error("dm_report: Failed to extend row for field name");
+ goto bad;
+ }
+ if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
+ log_error("dm_report: Failed to extend row with separator");
+ goto bad;
+ }
+ }
+
+ dm_list_iterate_items(row, &rh->rows) {
+ if ((field = dm_list_item(dm_list_first(&row->fields), struct dm_report_field))) {
+ if (!_output_field(rh, field))
+ goto bad;
+ dm_list_del(&field->list);
+ }
+
+ if (!dm_list_end(&rh->rows, &row->list))
+ if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
+ log_error("dm_report: Unable to extend output line");
+ goto bad;
+ }
+ }
+
+ if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
+ log_error("dm_report: Failed to terminate row");
+ goto bad;
+ }
+ log_print("%s", (char *) dm_pool_end_object(rh->mem));
+ }
+
+ return 1;
+
+ bad:
+ dm_pool_abandon_object(rh->mem);
+ return 0;
+}
+
+static int _output_as_columns(struct dm_report *rh)
+{
+ struct dm_list *fh, *rowh, *ftmp, *rtmp;
+ struct row *row = NULL;
+ struct dm_report_field *field;
+
+ /* If headings not printed yet, calculate field widths and print them */
+ if (!(rh->flags & RH_HEADINGS_PRINTED))
+ _report_headings(rh);
+
+ /* Print and clear buffer */
+ dm_list_iterate_safe(rowh, rtmp, &rh->rows) {
+ if (!dm_pool_begin_object(rh->mem, 512)) {
+ log_error("dm_report: Unable to allocate output line");
+ return 0;
+ }
+ row = dm_list_item(rowh, struct row);
+ dm_list_iterate_safe(fh, ftmp, &row->fields) {
+ field = dm_list_item(fh, struct dm_report_field);
+ if (field->props->flags & FLD_HIDDEN)
+ continue;
+
+ if (!_output_field(rh, field))
+ goto bad;
+
+ if (!dm_list_end(&row->fields, fh))
+ if (!dm_pool_grow_object(rh->mem, rh->separator, 0)) {
+ log_error("dm_report: Unable to extend output line");
+ goto bad;
+ }
+
+ dm_list_del(&field->list);
+ }
+ if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
+ log_error("dm_report: Unable to terminate output line");
+ goto bad;
+ }
+ log_print("%s", (char *) dm_pool_end_object(rh->mem));
+ dm_list_del(&row->list);
+ }
+
+ if (row)
+ dm_pool_free(rh->mem, row);
+
+ return 1;
+
+ bad:
+ dm_pool_abandon_object(rh->mem);
+ return 0;
+}
+
+int dm_report_output(struct dm_report *rh)
+{
+ if (dm_list_empty(&rh->rows))
+ return 1;
+
+ if ((rh->flags & RH_SORT_REQUIRED))
+ _sort_rows(rh);
+
+ if ((rh->flags & DM_REPORT_OUTPUT_COLUMNS_AS_ROWS))
+ return _output_as_rows(rh);
+ else
+ return _output_as_columns(rh);
+}
--- /dev/null
+/*
+ * Copyright (C) 2006-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+#include "libdevmapper.h"
+
+#include <ctype.h>
+
+/*
+ * consume characters while they match the predicate function.
+ */
+static char *_consume(char *buffer, int (*fn) (int))
+{
+ while (*buffer && fn(*buffer))
+ buffer++;
+
+ return buffer;
+}
+
+static int _isword(int c)
+{
+ return !isspace(c);
+}
+
+/*
+ * Split buffer into NULL-separated words in argv.
+ * Returns number of words.
+ */
+int dm_split_words(char *buffer, unsigned max,
+ unsigned ignore_comments __attribute__((unused)),
+ char **argv)
+{
+ unsigned arg;
+
+ for (arg = 0; arg < max; arg++) {
+ buffer = _consume(buffer, isspace);
+ if (!*buffer)
+ break;
+
+ argv[arg] = buffer;
+ buffer = _consume(buffer, _isword);
+
+ if (*buffer) {
+ *buffer = '\0';
+ buffer++;
+ }
+ }
+
+ return arg;
+}
+
+/*
+ * Remove hyphen quoting from a component of a name.
+ * NULL-terminates the component and returns start of next component.
+ */
+static char *_unquote(char *component)
+{
+ char *c = component;
+ char *o = c;
+ char *r;
+
+ while (*c) {
+ if (*(c + 1)) {
+ if (*c == '-') {
+ if (*(c + 1) == '-')
+ c++;
+ else
+ break;
+ }
+ }
+ *o = *c;
+ o++;
+ c++;
+ }
+
+ r = (*c) ? c + 1 : c;
+ *o = '\0';
+
+ return r;
+}
+
+int dm_split_lvm_name(struct dm_pool *mem, const char *dmname,
+ char **vgname, char **lvname, char **layer)
+{
+ if (mem && !(*vgname = dm_pool_strdup(mem, dmname)))
+ return 0;
+
+ _unquote(*layer = _unquote(*lvname = _unquote(*vgname)));
+
+ return 1;
+}
+
+/*
+ * On error, up to glibc 2.0.6, snprintf returned -1 if buffer was too small;
+ * From glibc 2.1 it returns number of chars (excl. trailing null) that would
+ * have been written had there been room.
+ *
+ * dm_snprintf reverts to the old behaviour.
+ */
+int dm_snprintf(char *buf, size_t bufsize, const char *format, ...)
+{
+ int n;
+ va_list ap;
+
+ va_start(ap, format);
+ n = vsnprintf(buf, bufsize, format, ap);
+ va_end(ap);
+
+ if (n < 0 || ((unsigned) n + 1 > bufsize))
+ return -1;
+
+ return n;
+}
+
+const char *dm_basename(const char *path)
+{
+ const char *p = strrchr(path, '/');
+
+ return p ? p + 1 : path;
+}
+
+int dm_asprintf(char **result, const char *format, ...)
+{
+ int n, ok = 0, size = 32;
+ va_list ap;
+ char *buf = dm_malloc(size);
+
+ *result = 0;
+
+ if (!buf)
+ return -1;
+
+ while (!ok) {
+ va_start(ap, format);
+ n = vsnprintf(buf, size, format, ap);
+ va_end(ap);
+
+ if (0 <= n && n < size)
+ ok = 1;
+ else {
+ dm_free(buf);
+ size *= 2;
+ buf = dm_malloc(size);
+ if (!buf)
+ return -1;
+ }
+ }
+
+ *result = dm_strdup(buf);
+ dm_free(buf);
+ return n + 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001 - 2003 Sistina Software (UK) Limited.
+ * Copyright (C) 2004 - 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef _LINUX_DM_IOCTL_V4_H
+#define _LINUX_DM_IOCTL_V4_H
+
+#ifdef linux
+# include <linux/types.h>
+#endif
+
+#define DM_DIR "mapper" /* Slashes not supported */
+#define DM_CONTROL_NODE "control"
+#define DM_MAX_TYPE_NAME 16
+#define DM_NAME_LEN 128
+#define DM_UUID_LEN 129
+
+/*
+ * A traditional ioctl interface for the device mapper.
+ *
+ * Each device can have two tables associated with it, an
+ * 'active' table which is the one currently used by io passing
+ * through the device, and an 'inactive' one which is a table
+ * that is being prepared as a replacement for the 'active' one.
+ *
+ * DM_VERSION:
+ * Just get the version information for the ioctl interface.
+ *
+ * DM_REMOVE_ALL:
+ * Remove all dm devices, destroy all tables. Only really used
+ * for debug.
+ *
+ * DM_LIST_DEVICES:
+ * Get a list of all the dm device names.
+ *
+ * DM_DEV_CREATE:
+ * Create a new device, neither the 'active' or 'inactive' table
+ * slots will be filled. The device will be in suspended state
+ * after creation, however any io to the device will get errored
+ * since it will be out-of-bounds.
+ *
+ * DM_DEV_REMOVE:
+ * Remove a device, destroy any tables.
+ *
+ * DM_DEV_RENAME:
+ * Rename a device or set its uuid if none was previously supplied.
+ *
+ * DM_SUSPEND:
+ * This performs both suspend and resume, depending which flag is
+ * passed in.
+ * Suspend: This command will not return until all pending io to
+ * the device has completed. Further io will be deferred until
+ * the device is resumed.
+ * Resume: It is no longer an error to issue this command on an
+ * unsuspended device. If a table is present in the 'inactive'
+ * slot, it will be moved to the active slot, then the old table
+ * from the active slot will be _destroyed_. Finally the device
+ * is resumed.
+ *
+ * DM_DEV_STATUS:
+ * Retrieves the status for the table in the 'active' slot.
+ *
+ * DM_DEV_WAIT:
+ * Wait for a significant event to occur to the device. This
+ * could either be caused by an event triggered by one of the
+ * targets of the table in the 'active' slot, or a table change.
+ *
+ * DM_TABLE_LOAD:
+ * Load a table into the 'inactive' slot for the device. The
+ * device does _not_ need to be suspended prior to this command.
+ *
+ * DM_TABLE_CLEAR:
+ * Destroy any table in the 'inactive' slot (ie. abort).
+ *
+ * DM_TABLE_DEPS:
+ * Return a set of device dependencies for the 'active' table.
+ *
+ * DM_TABLE_STATUS:
+ * Return the targets status for the 'active' table.
+ *
+ * DM_TARGET_MSG:
+ * Pass a message string to the target at a specific offset of a device.
+ *
+ * DM_DEV_SET_GEOMETRY:
+ * Set the geometry of a device by passing in a string in this format:
+ *
+ * "cylinders heads sectors_per_track start_sector"
+ *
+ * Beware that CHS geometry is nearly obsolete and only provided
+ * for compatibility with dm devices that can be booted by a PC
+ * BIOS. See struct hd_geometry for range limits. Also note that
+ * the geometry is erased if the device size changes.
+ */
+
+/*
+ * All ioctl arguments consist of a single chunk of memory, with
+ * this structure at the start. If a uuid is specified any
+ * lookup (eg. for a DM_INFO) will be done on that, *not* the
+ * name.
+ */
+struct dm_ioctl {
+ /*
+ * The version number is made up of three parts:
+ * major - no backward or forward compatibility,
+ * minor - only backwards compatible,
+ * patch - both backwards and forwards compatible.
+ *
+ * All clients of the ioctl interface should fill in the
+ * version number of the interface that they were
+ * compiled with.
+ *
+ * All recognised ioctl commands (ie. those that don't
+ * return -ENOTTY) fill out this field, even if the
+ * command failed.
+ */
+ uint32_t version[3]; /* in/out */
+ uint32_t data_size; /* total size of data passed in
+ * including this struct */
+
+ uint32_t data_start; /* offset to start of data
+ * relative to start of this struct */
+
+ uint32_t target_count; /* in/out */
+ int32_t open_count; /* out */
+ uint32_t flags; /* in/out */
+
+ /*
+ * event_nr holds either the event number (input and output) or the
+ * udev cookie value (input only).
+ * The DM_DEV_WAIT ioctl takes an event number as input.
+ * The DM_SUSPEND, DM_DEV_REMOVE and DM_DEV_RENAME ioctls
+ * use the field as a cookie to return in the DM_COOKIE
+ * variable with the uevents they issue.
+ * For output, the ioctls return the event number, not the cookie.
+ */
+ uint32_t event_nr; /* in/out */
+ uint32_t padding;
+
+ uint64_t dev; /* in/out */
+
+ char name[DM_NAME_LEN]; /* device name */
+ char uuid[DM_UUID_LEN]; /* unique identifier for
+ * the block device */
+ char data[7]; /* padding or data */
+};
+
+/*
+ * Used to specify tables. These structures appear after the
+ * dm_ioctl.
+ */
+struct dm_target_spec {
+ uint64_t sector_start;
+ uint64_t length;
+ int32_t status; /* used when reading from kernel only */
+
+ /*
+ * Location of the next dm_target_spec.
+ * - When specifying targets on a DM_TABLE_LOAD command, this value is
+ * the number of bytes from the start of the "current" dm_target_spec
+ * to the start of the "next" dm_target_spec.
+ * - When retrieving targets on a DM_TABLE_STATUS command, this value
+ * is the number of bytes from the start of the first dm_target_spec
+ * (that follows the dm_ioctl struct) to the start of the "next"
+ * dm_target_spec.
+ */
+ uint32_t next;
+
+ char target_type[DM_MAX_TYPE_NAME];
+
+ /*
+ * Parameter string starts immediately after this object.
+ * Be careful to add padding after string to ensure correct
+ * alignment of subsequent dm_target_spec.
+ */
+};
+
+/*
+ * Used to retrieve the target dependencies.
+ */
+struct dm_target_deps {
+ uint32_t count; /* Array size */
+ uint32_t padding; /* unused */
+ uint64_t dev[0]; /* out */
+};
+
+/*
+ * Used to get a list of all dm devices.
+ */
+struct dm_name_list {
+ uint64_t dev;
+ uint32_t next; /* offset to the next record from
+ the _start_ of this */
+ char name[0];
+};
+
+/*
+ * Used to retrieve the target versions
+ */
+struct dm_target_versions {
+ uint32_t next;
+ uint32_t version[3];
+
+ char name[0];
+};
+
+/*
+ * Used to pass message to a target
+ */
+struct dm_target_msg {
+ uint64_t sector; /* Device sector */
+
+ char message[0];
+};
+
+/*
+ * If you change this make sure you make the corresponding change
+ * to dm-ioctl.c:lookup_ioctl()
+ */
+enum {
+ /* Top level cmds */
+ DM_VERSION_CMD = 0,
+ DM_REMOVE_ALL_CMD,
+ DM_LIST_DEVICES_CMD,
+
+ /* device level cmds */
+ DM_DEV_CREATE_CMD,
+ DM_DEV_REMOVE_CMD,
+ DM_DEV_RENAME_CMD,
+ DM_DEV_SUSPEND_CMD,
+ DM_DEV_STATUS_CMD,
+ DM_DEV_WAIT_CMD,
+
+ /* Table level cmds */
+ DM_TABLE_LOAD_CMD,
+ DM_TABLE_CLEAR_CMD,
+ DM_TABLE_DEPS_CMD,
+ DM_TABLE_STATUS_CMD,
+
+ /* Added later */
+ DM_LIST_VERSIONS_CMD,
+ DM_TARGET_MSG_CMD,
+ DM_DEV_SET_GEOMETRY_CMD
+};
+
+#define DM_IOCTL 0xfd
+
+#define DM_VERSION _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl)
+#define DM_REMOVE_ALL _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl)
+#define DM_LIST_DEVICES _IOWR(DM_IOCTL, DM_LIST_DEVICES_CMD, struct dm_ioctl)
+
+#define DM_DEV_CREATE _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl)
+#define DM_DEV_REMOVE _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl)
+#define DM_DEV_RENAME _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl)
+#define DM_DEV_SUSPEND _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl)
+#define DM_DEV_STATUS _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl)
+#define DM_DEV_WAIT _IOWR(DM_IOCTL, DM_DEV_WAIT_CMD, struct dm_ioctl)
+
+#define DM_TABLE_LOAD _IOWR(DM_IOCTL, DM_TABLE_LOAD_CMD, struct dm_ioctl)
+#define DM_TABLE_CLEAR _IOWR(DM_IOCTL, DM_TABLE_CLEAR_CMD, struct dm_ioctl)
+#define DM_TABLE_DEPS _IOWR(DM_IOCTL, DM_TABLE_DEPS_CMD, struct dm_ioctl)
+#define DM_TABLE_STATUS _IOWR(DM_IOCTL, DM_TABLE_STATUS_CMD, struct dm_ioctl)
+
+#define DM_LIST_VERSIONS _IOWR(DM_IOCTL, DM_LIST_VERSIONS_CMD, struct dm_ioctl)
+
+#define DM_TARGET_MSG _IOWR(DM_IOCTL, DM_TARGET_MSG_CMD, struct dm_ioctl)
+#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
+
+#define DM_VERSION_MAJOR 4
+#define DM_VERSION_MINOR 19
+#define DM_VERSION_PATCHLEVEL 0
+#define DM_VERSION_EXTRA "-ioctl (2010-10-14)"
+
+/* Status bits */
+#define DM_READONLY_FLAG (1 << 0) /* In/Out */
+#define DM_SUSPEND_FLAG (1 << 1) /* In/Out */
+#define DM_PERSISTENT_DEV_FLAG (1 << 3) /* In */
+
+/*
+ * Flag passed into ioctl STATUS command to get table information
+ * rather than current status.
+ */
+#define DM_STATUS_TABLE_FLAG (1 << 4) /* In */
+
+/*
+ * Flags that indicate whether a table is present in either of
+ * the two table slots that a device has.
+ */
+#define DM_ACTIVE_PRESENT_FLAG (1 << 5) /* Out */
+#define DM_INACTIVE_PRESENT_FLAG (1 << 6) /* Out */
+
+/*
+ * Indicates that the buffer passed in wasn't big enough for the
+ * results.
+ */
+#define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */
+
+/*
+ * This flag is now ignored.
+ */
+#define DM_SKIP_BDGET_FLAG (1 << 9) /* In */
+
+/*
+ * Set this to avoid attempting to freeze any filesystem when suspending.
+ */
+#define DM_SKIP_LOCKFS_FLAG (1 << 10) /* In */
+
+/*
+ * Set this to suspend without flushing queued ios.
+ */
+#define DM_NOFLUSH_FLAG (1 << 11) /* In */
+
+/*
+ * If set, any table information returned will relate to the inactive
+ * table instead of the live one. Always check DM_INACTIVE_PRESENT_FLAG
+ * is set before using the data returned.
+ */
+#define DM_QUERY_INACTIVE_TABLE_FLAG (1 << 12) /* In */
+
+/*
+ * If set, a uevent was generated for which the caller may need to wait.
+ */
+#define DM_UEVENT_GENERATED_FLAG (1 << 13) /* Out */
+
+/*
+ * If set, rename changes the uuid not the name. Only permitted
+ * if no uuid was previously supplied: an existing uuid cannot be changed.
+ */
+#define DM_UUID_FLAG (1 << 14) /* In */
+
+#endif /* _LINUX_DM_IOCTL_H */
--- /dev/null
+/*
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef __DM_LOG_USERSPACE_H__
+#define __DM_LOG_USERSPACE_H__
+
+#include "dm-ioctl.h" /* For DM_UUID_LEN */
+
+/*
+ * The device-mapper userspace log module consists of a kernel component and
+ * a user-space component. The kernel component implements the API defined
+ * in dm-dirty-log.h. Its purpose is simply to pass the parameters and
+ * return values of those API functions between kernel and user-space.
+ *
+ * Below are defined the 'request_types' - DM_ULOG_CTR, DM_ULOG_DTR, etc.
+ * These request types represent the different functions in the device-mapper
+ * dirty log API. Each of these is described in more detail below.
+ *
+ * The user-space program must listen for requests from the kernel (representing
+ * the various API functions) and process them.
+ *
+ * User-space begins by setting up the communication link (error checking
+ * removed for clarity):
+ * fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
+ * addr.nl_family = AF_NETLINK;
+ * addr.nl_groups = CN_IDX_DM;
+ * addr.nl_pid = 0;
+ * r = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
+ * opt = addr.nl_groups;
+ * setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &opt, sizeof(opt));
+ *
+ * User-space will then wait to receive requests form the kernel, which it
+ * will process as described below. The requests are received in the form,
+ * ((struct dm_ulog_request) + (additional data)). Depending on the request
+ * type, there may or may not be 'additional data'. In the descriptions below,
+ * you will see 'Payload-to-userspace' and 'Payload-to-kernel'. The
+ * 'Payload-to-userspace' is what the kernel sends in 'additional data' as
+ * necessary parameters to complete the request. The 'Payload-to-kernel' is
+ * the 'additional data' returned to the kernel that contains the necessary
+ * results of the request. The 'data_size' field in the dm_ulog_request
+ * structure denotes the availability and amount of payload data.
+ */
+
+/*
+ * DM_ULOG_CTR corresponds to (found in dm-dirty-log.h):
+ * int (*ctr)(struct dm_dirty_log *log, struct dm_target *ti,
+ * unsigned argc, char **argv);
+ *
+ * Payload-to-userspace:
+ * A single string containing all the argv arguments separated by ' 's
+ * Payload-to-kernel:
+ * None. ('data_size' in the dm_ulog_request struct should be 0.)
+ *
+ * The UUID contained in the dm_ulog_request structure is the reference that
+ * will be used by all request types to a specific log. The constructor must
+ * record this assotiation with instance created.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_CTR 1
+
+/*
+ * DM_ULOG_DTR corresponds to (found in dm-dirty-log.h):
+ * void (*dtr)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ * A single string containing all the argv arguments separated by ' 's
+ * Payload-to-kernel:
+ * None. ('data_size' in the dm_ulog_request struct should be 0.)
+ *
+ * The UUID contained in the dm_ulog_request structure is all that is
+ * necessary to identify the log instance being destroyed. There is no
+ * payload data.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and clearing
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_DTR 2
+
+/*
+ * DM_ULOG_PRESUSPEND corresponds to (found in dm-dirty-log.h):
+ * int (*presuspend)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ * None.
+ * Payload-to-kernel:
+ * None.
+ *
+ * The UUID contained in the dm_ulog_request structure is all that is
+ * necessary to identify the log instance being presuspended. There is no
+ * payload data.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_PRESUSPEND 3
+
+/*
+ * DM_ULOG_POSTSUSPEND corresponds to (found in dm-dirty-log.h):
+ * int (*postsuspend)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ * None.
+ * Payload-to-kernel:
+ * None.
+ *
+ * The UUID contained in the dm_ulog_request structure is all that is
+ * necessary to identify the log instance being postsuspended. There is no
+ * payload data.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_POSTSUSPEND 4
+
+/*
+ * DM_ULOG_RESUME corresponds to (found in dm-dirty-log.h):
+ * int (*resume)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ * None.
+ * Payload-to-kernel:
+ * None.
+ *
+ * The UUID contained in the dm_ulog_request structure is all that is
+ * necessary to identify the log instance being resumed. There is no
+ * payload data.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_RESUME 5
+
+/*
+ * DM_ULOG_GET_REGION_SIZE corresponds to (found in dm-dirty-log.h):
+ * uint32_t (*get_region_size)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ * None.
+ * Payload-to-kernel:
+ * uint64_t - contains the region size
+ *
+ * The region size is something that was determined at constructor time.
+ * It is returned in the payload area and 'data_size' is set to
+ * reflect this.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field appropriately.
+ */
+#define DM_ULOG_GET_REGION_SIZE 6
+
+/*
+ * DM_ULOG_IS_CLEAN corresponds to (found in dm-dirty-log.h):
+ * int (*is_clean)(struct dm_dirty_log *log, region_t region);
+ *
+ * Payload-to-userspace:
+ * uint64_t - the region to get clean status on
+ * Payload-to-kernel:
+ * int64_t - 1 if clean, 0 otherwise
+ *
+ * Payload is sizeof(uint64_t) and contains the region for which the clean
+ * status is being made.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - filling the payload with 0 (not clean) or
+ * 1 (clean), setting 'data_size' and 'error' appropriately.
+ */
+#define DM_ULOG_IS_CLEAN 7
+
+/*
+ * DM_ULOG_IN_SYNC corresponds to (found in dm-dirty-log.h):
+ * int (*in_sync)(struct dm_dirty_log *log, region_t region,
+ * int can_block);
+ *
+ * Payload-to-userspace:
+ * uint64_t - the region to get sync status on
+ * Payload-to-kernel:
+ * int64_t - 1 if in-sync, 0 otherwise
+ *
+ * Exactly the same as 'is_clean' above, except this time asking "has the
+ * region been recovered?" vs. "is the region not being modified?"
+ */
+#define DM_ULOG_IN_SYNC 8
+
+/*
+ * DM_ULOG_FLUSH corresponds to (found in dm-dirty-log.h):
+ * int (*flush)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ * None.
+ * Payload-to-kernel:
+ * None.
+ *
+ * No incoming or outgoing payload. Simply flush log state to disk.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and clearing
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_FLUSH 9
+
+/*
+ * DM_ULOG_MARK_REGION corresponds to (found in dm-dirty-log.h):
+ * void (*mark_region)(struct dm_dirty_log *log, region_t region);
+ *
+ * Payload-to-userspace:
+ * uint64_t [] - region(s) to mark
+ * Payload-to-kernel:
+ * None.
+ *
+ * Incoming payload contains the one or more regions to mark dirty.
+ * The number of regions contained in the payload can be determined from
+ * 'data_size/sizeof(uint64_t)'.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and clearing
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_MARK_REGION 10
+
+/*
+ * DM_ULOG_CLEAR_REGION corresponds to (found in dm-dirty-log.h):
+ * void (*clear_region)(struct dm_dirty_log *log, region_t region);
+ *
+ * Payload-to-userspace:
+ * uint64_t [] - region(s) to clear
+ * Payload-to-kernel:
+ * None.
+ *
+ * Incoming payload contains the one or more regions to mark clean.
+ * The number of regions contained in the payload can be determined from
+ * 'data_size/sizeof(uint64_t)'.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and clearing
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_CLEAR_REGION 11
+
+/*
+ * DM_ULOG_GET_RESYNC_WORK corresponds to (found in dm-dirty-log.h):
+ * int (*get_resync_work)(struct dm_dirty_log *log, region_t *region);
+ *
+ * Payload-to-userspace:
+ * None.
+ * Payload-to-kernel:
+ * {
+ * int64_t i; -- 1 if recovery necessary, 0 otherwise
+ * uint64_t r; -- The region to recover if i=1
+ * }
+ * 'data_size' should be set appropriately.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field appropriately.
+ */
+#define DM_ULOG_GET_RESYNC_WORK 12
+
+/*
+ * DM_ULOG_SET_REGION_SYNC corresponds to (found in dm-dirty-log.h):
+ * void (*set_region_sync)(struct dm_dirty_log *log,
+ * region_t region, int in_sync);
+ *
+ * Payload-to-userspace:
+ * {
+ * uint64_t - region to set sync state on
+ * int64_t - 0 if not-in-sync, 1 if in-sync
+ * }
+ * Payload-to-kernel:
+ * None.
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and clearing
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_SET_REGION_SYNC 13
+
+/*
+ * DM_ULOG_GET_SYNC_COUNT corresponds to (found in dm-dirty-log.h):
+ * region_t (*get_sync_count)(struct dm_dirty_log *log);
+ *
+ * Payload-to-userspace:
+ * None.
+ * Payload-to-kernel:
+ * uint64_t - the number of in-sync regions
+ *
+ * No incoming payload. Kernel-bound payload contains the number of
+ * regions that are in-sync (in a size_t).
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_GET_SYNC_COUNT 14
+
+/*
+ * DM_ULOG_STATUS_INFO corresponds to (found in dm-dirty-log.h):
+ * int (*status)(struct dm_dirty_log *log, STATUSTYPE_INFO,
+ * char *result, unsigned maxlen);
+ *
+ * Payload-to-userspace:
+ * None.
+ * Payload-to-kernel:
+ * Character string containing STATUSTYPE_INFO
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_STATUS_INFO 15
+
+/*
+ * DM_ULOG_STATUS_TABLE corresponds to (found in dm-dirty-log.h):
+ * int (*status)(struct dm_dirty_log *log, STATUSTYPE_TABLE,
+ * char *result, unsigned maxlen);
+ *
+ * Payload-to-userspace:
+ * None.
+ * Payload-to-kernel:
+ * Character string containing STATUSTYPE_TABLE
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_STATUS_TABLE 16
+
+/*
+ * DM_ULOG_IS_REMOTE_RECOVERING corresponds to (found in dm-dirty-log.h):
+ * int (*is_remote_recovering)(struct dm_dirty_log *log, region_t region);
+ *
+ * Payload-to-userspace:
+ * uint64_t - region to determine recovery status on
+ * Payload-to-kernel:
+ * {
+ * int64_t is_recovering; -- 0 if no, 1 if yes
+ * uint64_t in_sync_hint; -- lowest region still needing resync
+ * }
+ *
+ * When the request has been processed, user-space must return the
+ * dm_ulog_request to the kernel - setting the 'error' field and
+ * 'data_size' appropriately.
+ */
+#define DM_ULOG_IS_REMOTE_RECOVERING 17
+
+/*
+ * (DM_ULOG_REQUEST_MASK & request_type) to get the request type
+ *
+ * Payload-to-userspace:
+ * A single string containing all the argv arguments separated by ' 's
+ * Payload-to-kernel:
+ * None. ('data_size' in the dm_ulog_request struct should be 0.)
+ *
+ * We are reserving 8 bits of the 32-bit 'request_type' field for the
+ * various request types above. The remaining 24-bits are currently
+ * set to zero and are reserved for future use and compatibility concerns.
+ *
+ * User-space should always use DM_ULOG_REQUEST_TYPE to aquire the
+ * request type from the 'request_type' field to maintain forward compatibility.
+ */
+#define DM_ULOG_REQUEST_MASK 0xFF
+#define DM_ULOG_REQUEST_TYPE(request_type) \
+ (DM_ULOG_REQUEST_MASK & (request_type))
+
+struct dm_ulog_request {
+ /*
+ * The local unique identifier (luid) and the universally unique
+ * identifier (uuid) are used to tie a request to a specific
+ * mirror log. A single machine log could probably make due with
+ * just the 'luid', but a cluster-aware log must use the 'uuid' and
+ * the 'luid'. The uuid is what is required for node to node
+ * communication concerning a particular log, but the 'luid' helps
+ * differentiate between logs that are being swapped and have the
+ * same 'uuid'. (Think "live" and "inactive" device-mapper tables.)
+ */
+ uint64_t luid;
+ char uuid[DM_UUID_LEN];
+ char padding[7]; /* Padding because DM_UUID_LEN = 129 */
+
+ int32_t error; /* Used to report back processing errors */
+
+ uint32_t seq; /* Sequence number for request */
+ uint32_t request_type; /* DM_ULOG_* defined above */
+ uint32_t data_size; /* How much data (not including this struct) */
+
+ char data[];
+};
+
+#endif /* __DM_LOG_USERSPACE_H__ */
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DM_LOGGING_H
+#define _DM_LOGGING_H
+
+#include "libdevmapper.h"
+
+extern dm_log_fn dm_log;
+extern dm_log_with_errno_fn dm_log_with_errno;
+
+#define LOG_MESG(l, f, ln, e, x...) \
+ do { \
+ if (dm_log_is_non_default()) \
+ dm_log(l, f, ln, ## x); \
+ else \
+ dm_log_with_errno(l, f, ln, e, ## x); \
+ } while (0)
+
+#define LOG_LINE(l, x...) LOG_MESG(l, __FILE__, __LINE__, 0, ## x)
+#define LOG_LINE_WITH_ERRNO(l, e, x...) LOG_MESG(l, __FILE__, __LINE__, e, ## x)
+
+#include "log.h"
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This file must be included first by every device-mapper library source file.
+ */
+#ifndef _DM_LIB_H
+#define _DM_LIB_H
+
+#define DM
+
+#include "lib.h"
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2004-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LIBDM_KDEV_H
+#define _LIBDM_KDEV_H
+
+#define MAJOR(dev) ((dev & 0xfff00) >> 8)
+#define MINOR(dev) ((dev & 0xff) | ((dev >> 12) & 0xfff00))
+#define MKDEV(ma,mi) ((mi & 0xff) | (ma << 8) | ((mi & ~0xff) << 12))
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+
+#include <assert.h>
+#include <stdarg.h>
+
+char *dm_strdup_aux(const char *str, const char *file, int line)
+{
+ char *ret;
+
+ if (!str) {
+ log_error(INTERNAL_ERROR "dm_strdup called with NULL pointer");
+ return NULL;
+ }
+
+ if ((ret = dm_malloc_aux_debug(strlen(str) + 1, file, line)))
+ strcpy(ret, str);
+
+ return ret;
+}
+
+struct memblock {
+ struct memblock *prev, *next; /* All allocated blocks are linked */
+ size_t length; /* Size of the requested block */
+ int id; /* Index of the block */
+ const char *file; /* File that allocated */
+ int line; /* Line that allocated */
+ void *magic; /* Address of this block */
+} __attribute__((aligned(8)));
+
+static struct {
+ unsigned block_serialno;/* Non-decreasing serialno of block */
+ unsigned blocks_allocated; /* Current number of blocks allocated */
+ unsigned blocks_max; /* Max no of concurrently-allocated blocks */
+ unsigned int bytes, mbytes;
+
+} _mem_stats = {
+0, 0, 0, 0, 0};
+
+static struct memblock *_head = 0;
+static struct memblock *_tail = 0;
+
+void *dm_malloc_aux_debug(size_t s, const char *file, int line)
+{
+ struct memblock *nb;
+ size_t tsize = s + sizeof(*nb) + sizeof(unsigned long);
+
+ if (s > 50000000) {
+ log_error("Huge memory allocation (size %" PRIsize_t
+ ") rejected - metadata corruption?", s);
+ return 0;
+ }
+
+ if (!(nb = malloc(tsize))) {
+ log_error("couldn't allocate any memory, size = %" PRIsize_t,
+ s);
+ return 0;
+ }
+
+ /* set up the file and line info */
+ nb->file = file;
+ nb->line = line;
+
+ dm_bounds_check();
+
+ /* setup fields */
+ nb->magic = nb + 1;
+ nb->length = s;
+ nb->id = ++_mem_stats.block_serialno;
+ nb->next = 0;
+
+ /* stomp a pretty pattern across the new memory
+ and fill in the boundary bytes */
+ {
+ char *ptr = (char *) (nb + 1);
+ size_t i;
+ for (i = 0; i < s; i++)
+ *ptr++ = i & 0x1 ? (char) 0xba : (char) 0xbe;
+
+ for (i = 0; i < sizeof(unsigned long); i++)
+ *ptr++ = (char) nb->id;
+ }
+
+ nb->prev = _tail;
+
+ /* link to tail of the list */
+ if (!_head)
+ _head = _tail = nb;
+ else {
+ _tail->next = nb;
+ _tail = nb;
+ }
+
+ _mem_stats.blocks_allocated++;
+ if (_mem_stats.blocks_allocated > _mem_stats.blocks_max)
+ _mem_stats.blocks_max = _mem_stats.blocks_allocated;
+
+ _mem_stats.bytes += s;
+ if (_mem_stats.bytes > _mem_stats.mbytes)
+ _mem_stats.mbytes = _mem_stats.bytes;
+
+ /* log_debug("Allocated: %u %u %u", nb->id, _mem_stats.blocks_allocated,
+ _mem_stats.bytes); */
+
+ return nb + 1;
+}
+
+void *dm_zalloc_aux_debug(size_t s, const char *file, int line)
+{
+ void *ptr = dm_malloc_aux_debug(s, file, line);
+
+ if (ptr)
+ memset(ptr, 0, s);
+
+ return ptr;
+}
+
+void dm_free_aux(void *p)
+{
+ char *ptr;
+ size_t i;
+ struct memblock *mb = ((struct memblock *) p) - 1;
+ if (!p)
+ return;
+
+ dm_bounds_check();
+
+ /* sanity check */
+ assert(mb->magic == p);
+
+ /* check data at the far boundary */
+ ptr = ((char *) mb) + sizeof(struct memblock) + mb->length;
+ for (i = 0; i < sizeof(unsigned long); i++)
+ if (*ptr++ != (char) mb->id)
+ assert(!"Damage at far end of block");
+
+ /* have we freed this before ? */
+ assert(mb->id != 0);
+
+ /* unlink */
+ if (mb->prev)
+ mb->prev->next = mb->next;
+ else
+ _head = mb->next;
+
+ if (mb->next)
+ mb->next->prev = mb->prev;
+ else
+ _tail = mb->prev;
+
+ mb->id = 0;
+
+ /* stomp a different pattern across the memory */
+ ptr = ((char *) mb) + sizeof(struct memblock);
+ for (i = 0; i < mb->length; i++)
+ *ptr++ = i & 1 ? (char) 0xde : (char) 0xad;
+
+ assert(_mem_stats.blocks_allocated);
+ _mem_stats.blocks_allocated--;
+ _mem_stats.bytes -= mb->length;
+
+ /* free the memory */
+ free(mb);
+}
+
+void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line)
+{
+ void *r;
+ struct memblock *mb = ((struct memblock *) p) - 1;
+
+ r = dm_malloc_aux_debug(s, file, line);
+
+ if (p) {
+ memcpy(r, p, mb->length);
+ dm_free_aux(p);
+ }
+
+ return r;
+}
+
+int dm_dump_memory_debug(void)
+{
+ unsigned long tot = 0;
+ struct memblock *mb;
+ char str[32];
+ size_t c;
+
+ if (_head)
+ log_very_verbose("You have a memory leak:");
+
+ for (mb = _head; mb; mb = mb->next) {
+#ifdef VALGRIND_POOL
+ /*
+ * We can't look at the memory in case it has had
+ * VALGRIND_MAKE_MEM_NOACCESS called on it.
+ */
+ str[0] = '\0';
+#else
+ for (c = 0; c < sizeof(str) - 1; c++) {
+ if (c >= mb->length)
+ str[c] = ' ';
+ else if (((char *)mb->magic)[c] == '\0')
+ str[c] = '\0';
+ else if (((char *)mb->magic)[c] < ' ')
+ str[c] = '?';
+ else
+ str[c] = ((char *)mb->magic)[c];
+ }
+ str[sizeof(str) - 1] = '\0';
+#endif
+
+ LOG_MESG(_LOG_INFO, mb->file, mb->line, 0,
+ "block %d at %p, size %" PRIsize_t "\t [%s]",
+ mb->id, mb->magic, mb->length, str);
+ tot += mb->length;
+ }
+
+ if (_head)
+ log_very_verbose("%ld bytes leaked in total", tot);
+
+ return 1;
+}
+
+void dm_bounds_check_debug(void)
+{
+ struct memblock *mb = _head;
+ while (mb) {
+ size_t i;
+ char *ptr = ((char *) (mb + 1)) + mb->length;
+ for (i = 0; i < sizeof(unsigned long); i++)
+ if (*ptr++ != (char) mb->id)
+ assert(!"Memory smash");
+
+ mb = mb->next;
+ }
+}
+
+void *dm_malloc_aux(size_t s, const char *file __attribute__((unused)),
+ int line __attribute__((unused)))
+{
+ if (s > 50000000) {
+ log_error("Huge memory allocation (size %" PRIsize_t
+ ") rejected - metadata corruption?", s);
+ return 0;
+ }
+
+ return malloc(s);
+}
+
+void *dm_zalloc_aux(size_t s, const char *file, int line)
+{
+ void *ptr = dm_malloc_aux(s, file, line);
+
+ if (ptr)
+ memset(ptr, 0, s);
+
+ return ptr;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DM_DBG_MALLOC_H
+#define _DM_DBG_MALLOC_H
+
+#include <stdlib.h>
+#include <string.h>
+
+void *malloc_aux(size_t s, const char *file, int line);
+#define dm_malloc(s) malloc_aux((s), __FILE__, __LINE__)
+
+char *dbg_strdup(const char *str);
+
+#ifdef DEBUG_MEM
+
+void free_aux(void *p);
+void *realloc_aux(void *p, unsigned int s, const char *file, int line);
+int dump_memory(void);
+void bounds_check(void);
+
+# define dm_free(p) free_aux(p)
+# define dbg_realloc(p, s) realloc_aux(p, s, __FILE__, __LINE__)
+
+#else
+
+# define dm_free(p) do {if (p) free(p); } while (0)
+# define dbg_realloc(p, s) realloc(p, s)
+# define dump_memory()
+# define bounds_check()
+
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+#include <assert.h>
+
+struct block {
+ struct block *next;
+ size_t size;
+ void *data;
+};
+
+typedef struct {
+ unsigned block_serialno; /* Non-decreasing serialno of block */
+ unsigned blocks_allocated; /* Current number of blocks allocated */
+ unsigned blocks_max; /* Max no of concurrently-allocated blocks */
+ unsigned int bytes, maxbytes;
+} pool_stats;
+
+struct dm_pool {
+ struct dm_list list;
+ const char *name;
+ void *orig_pool; /* to pair it with first allocation call */
+
+ int begun;
+ struct block *object;
+
+ struct block *blocks;
+ struct block *tail;
+
+ pool_stats stats;
+};
+
+/* by default things come out aligned for doubles */
+#define DEFAULT_ALIGNMENT __alignof__ (double)
+
+struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
+{
+ struct dm_pool *mem = dm_malloc(sizeof(*mem));
+
+ if (!mem) {
+ log_error("Couldn't create memory pool %s (size %"
+ PRIsize_t ")", name, sizeof(*mem));
+ return NULL;
+ }
+
+ mem->name = name;
+ mem->begun = 0;
+ mem->object = 0;
+ mem->blocks = mem->tail = NULL;
+
+ mem->stats.block_serialno = 0;
+ mem->stats.blocks_allocated = 0;
+ mem->stats.blocks_max = 0;
+ mem->stats.bytes = 0;
+ mem->stats.maxbytes = 0;
+
+ mem->orig_pool = mem;
+
+#ifdef DEBUG_POOL
+ log_debug("Created mempool %s", name);
+#endif
+
+ dm_list_add(&_dm_pools, &mem->list);
+ return mem;
+}
+
+static void _free_blocks(struct dm_pool *p, struct block *b)
+{
+ struct block *n;
+
+ while (b) {
+ p->stats.bytes -= b->size;
+ p->stats.blocks_allocated--;
+
+ n = b->next;
+ dm_free(b->data);
+ dm_free(b);
+ b = n;
+ }
+}
+
+static void _pool_stats(struct dm_pool *p, const char *action)
+{
+#ifdef DEBUG_POOL
+ log_debug("%s mempool %s: %u/%u bytes, %u/%u blocks, "
+ "%u allocations)", action, p->name, p->stats.bytes,
+ p->stats.maxbytes, p->stats.blocks_allocated,
+ p->stats.blocks_max, p->stats.block_serialno);
+#else
+ ;
+#endif
+}
+
+void dm_pool_destroy(struct dm_pool *p)
+{
+ _pool_stats(p, "Destroying");
+ _free_blocks(p, p->blocks);
+ dm_list_del(&p->list);
+ dm_free(p);
+}
+
+void *dm_pool_alloc(struct dm_pool *p, size_t s)
+{
+ return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
+}
+
+static void _append_block(struct dm_pool *p, struct block *b)
+{
+ if (p->tail) {
+ p->tail->next = b;
+ p->tail = b;
+ } else
+ p->blocks = p->tail = b;
+
+ p->stats.block_serialno++;
+ p->stats.blocks_allocated++;
+ if (p->stats.blocks_allocated > p->stats.blocks_max)
+ p->stats.blocks_max = p->stats.blocks_allocated;
+
+ p->stats.bytes += b->size;
+ if (p->stats.bytes > p->stats.maxbytes)
+ p->stats.maxbytes = p->stats.bytes;
+}
+
+static struct block *_new_block(size_t s, unsigned alignment)
+{
+ /* FIXME: I'm currently ignoring the alignment arg. */
+ size_t len = sizeof(struct block) + s;
+ struct block *b = dm_malloc(len);
+
+ /*
+ * Too lazy to implement alignment for debug version, and
+ * I don't think LVM will use anything but default
+ * align.
+ */
+ assert(alignment == DEFAULT_ALIGNMENT);
+
+ if (!b) {
+ log_error("Out of memory");
+ return NULL;
+ }
+
+ if (!(b->data = dm_malloc(s))) {
+ log_error("Out of memory");
+ dm_free(b);
+ return NULL;
+ }
+
+ b->next = NULL;
+ b->size = s;
+
+ return b;
+}
+
+void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
+{
+ struct block *b = _new_block(s, alignment);
+
+ if (!b)
+ return NULL;
+
+ _append_block(p, b);
+
+ return b->data;
+}
+
+void dm_pool_empty(struct dm_pool *p)
+{
+ _pool_stats(p, "Emptying");
+ _free_blocks(p, p->blocks);
+ p->blocks = p->tail = NULL;
+}
+
+void dm_pool_free(struct dm_pool *p, void *ptr)
+{
+ struct block *b, *prev = NULL;
+
+ _pool_stats(p, "Freeing (before)");
+
+ for (b = p->blocks; b; b = b->next) {
+ if (b->data == ptr)
+ break;
+ prev = b;
+ }
+
+ /*
+ * If this fires then you tried to free a
+ * pointer that either wasn't from this
+ * pool, or isn't the start of a block.
+ */
+ assert(b);
+
+ _free_blocks(p, b);
+
+ if (prev) {
+ p->tail = prev;
+ prev->next = NULL;
+ } else
+ p->blocks = p->tail = NULL;
+
+ _pool_stats(p, "Freeing (after)");
+}
+
+int dm_pool_begin_object(struct dm_pool *p, size_t init_size)
+{
+ assert(!p->begun);
+ p->begun = 1;
+ return 1;
+}
+
+int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta)
+{
+ struct block *new;
+ size_t new_size;
+
+ if (!delta)
+ delta = strlen(extra);
+
+ assert(p->begun);
+
+ if (p->object)
+ new_size = delta + p->object->size;
+ else
+ new_size = delta;
+
+ if (!(new = _new_block(new_size, DEFAULT_ALIGNMENT))) {
+ log_error("Couldn't extend object.");
+ return 0;
+ }
+
+ if (p->object) {
+ memcpy(new->data, p->object->data, p->object->size);
+ dm_free(p->object->data);
+ dm_free(p->object);
+ }
+ p->object = new;
+
+ memcpy(new->data + new_size - delta, extra, delta);
+
+ return 1;
+}
+
+void *dm_pool_end_object(struct dm_pool *p)
+{
+ assert(p->begun);
+ _append_block(p, p->object);
+
+ p->begun = 0;
+ p->object = NULL;
+ return p->tail->data;
+}
+
+void dm_pool_abandon_object(struct dm_pool *p)
+{
+ assert(p->begun);
+ dm_free(p->object);
+ p->begun = 0;
+ p->object = NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef VALGRIND_POOL
+#include "valgrind/memcheck.h"
+#endif
+
+#include "dmlib.h"
+
+struct chunk {
+ char *begin, *end;
+ struct chunk *prev;
+};
+
+struct dm_pool {
+ struct dm_list list;
+ struct chunk *chunk, *spare_chunk; /* spare_chunk is a one entry free
+ list to stop 'bobbling' */
+ size_t chunk_size;
+ size_t object_len;
+ unsigned object_alignment;
+};
+
+static void _align_chunk(struct chunk *c, unsigned alignment);
+static struct chunk *_new_chunk(struct dm_pool *p, size_t s);
+static void _free_chunk(struct chunk *c);
+
+/* by default things come out aligned for doubles */
+#define DEFAULT_ALIGNMENT __alignof__ (double)
+
+struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint)
+{
+ size_t new_size = 1024;
+ struct dm_pool *p = dm_zalloc(sizeof(*p));
+
+ if (!p) {
+ log_error("Couldn't create memory pool %s (size %"
+ PRIsize_t ")", name, sizeof(*p));
+ return 0;
+ }
+
+ /* round chunk_hint up to the next power of 2 */
+ p->chunk_size = chunk_hint + sizeof(struct chunk);
+ while (new_size < p->chunk_size)
+ new_size <<= 1;
+ p->chunk_size = new_size;
+ dm_list_add(&_dm_pools, &p->list);
+ return p;
+}
+
+void dm_pool_destroy(struct dm_pool *p)
+{
+ struct chunk *c, *pr;
+ _free_chunk(p->spare_chunk);
+ c = p->chunk;
+ while (c) {
+ pr = c->prev;
+ _free_chunk(c);
+ c = pr;
+ }
+
+ dm_list_del(&p->list);
+ dm_free(p);
+}
+
+void *dm_pool_alloc(struct dm_pool *p, size_t s)
+{
+ return dm_pool_alloc_aligned(p, s, DEFAULT_ALIGNMENT);
+}
+
+void *dm_pool_alloc_aligned(struct dm_pool *p, size_t s, unsigned alignment)
+{
+ struct chunk *c = p->chunk;
+ void *r;
+
+ /* realign begin */
+ if (c)
+ _align_chunk(c, alignment);
+
+ /* have we got room ? */
+ if (!c || (c->begin > c->end) || (c->end - c->begin < s)) {
+ /* allocate new chunk */
+ size_t needed = s + alignment + sizeof(struct chunk);
+ c = _new_chunk(p, (needed > p->chunk_size) ?
+ needed : p->chunk_size);
+
+ if (!c)
+ return NULL;
+
+ _align_chunk(c, alignment);
+ }
+
+ r = c->begin;
+ c->begin += s;
+
+#ifdef VALGRIND_POOL
+ VALGRIND_MAKE_MEM_UNDEFINED(r, s);
+#endif
+
+ return r;
+}
+
+void dm_pool_empty(struct dm_pool *p)
+{
+ struct chunk *c;
+
+ for (c = p->chunk; c && c->prev; c = c->prev)
+ ;
+
+ if (c)
+ dm_pool_free(p, (char *) (c + 1));
+}
+
+void dm_pool_free(struct dm_pool *p, void *ptr)
+{
+ struct chunk *c = p->chunk;
+
+ while (c) {
+ if (((char *) c < (char *) ptr) &&
+ ((char *) c->end > (char *) ptr)) {
+ c->begin = ptr;
+#ifdef VALGRIND_POOL
+ VALGRIND_MAKE_MEM_NOACCESS(c->begin, c->end - c->begin);
+#endif
+ break;
+ }
+
+ if (p->spare_chunk)
+ _free_chunk(p->spare_chunk);
+
+ c->begin = (char *) (c + 1);
+#ifdef VALGRIND_POOL
+ VALGRIND_MAKE_MEM_NOACCESS(c->begin, c->end - c->begin);
+#endif
+
+ p->spare_chunk = c;
+ c = c->prev;
+ }
+
+ if (!c)
+ log_error(INTERNAL_ERROR "pool_free asked to free pointer "
+ "not in pool");
+ else
+ p->chunk = c;
+}
+
+int dm_pool_begin_object(struct dm_pool *p, size_t hint)
+{
+ struct chunk *c = p->chunk;
+ const size_t align = DEFAULT_ALIGNMENT;
+
+ p->object_len = 0;
+ p->object_alignment = align;
+
+ if (c)
+ _align_chunk(c, align);
+
+ if (!c || (c->begin > c->end) || (c->end - c->begin < hint)) {
+ /* allocate a new chunk */
+ c = _new_chunk(p,
+ hint > (p->chunk_size - sizeof(struct chunk)) ?
+ hint + sizeof(struct chunk) + align :
+ p->chunk_size);
+
+ if (!c)
+ return 0;
+
+ _align_chunk(c, align);
+ }
+
+ return 1;
+}
+
+int dm_pool_grow_object(struct dm_pool *p, const void *extra, size_t delta)
+{
+ struct chunk *c = p->chunk, *nc;
+
+ if (!delta)
+ delta = strlen(extra);
+
+ if (c->end - (c->begin + p->object_len) < delta) {
+ /* move into a new chunk */
+ if (p->object_len + delta > (p->chunk_size / 2))
+ nc = _new_chunk(p, (p->object_len + delta) * 2);
+ else
+ nc = _new_chunk(p, p->chunk_size);
+
+ if (!nc)
+ return 0;
+
+ _align_chunk(p->chunk, p->object_alignment);
+
+#ifdef VALGRIND_POOL
+ VALGRIND_MAKE_MEM_UNDEFINED(p->chunk->begin, p->object_len);
+#endif
+
+ memcpy(p->chunk->begin, c->begin, p->object_len);
+
+#ifdef VALGRIND_POOL
+ VALGRIND_MAKE_MEM_NOACCESS(c->begin, p->object_len);
+#endif
+
+ c = p->chunk;
+ }
+
+#ifdef VALGRIND_POOL
+ VALGRIND_MAKE_MEM_UNDEFINED(p->chunk->begin + p->object_len, delta);
+#endif
+
+ memcpy(c->begin + p->object_len, extra, delta);
+ p->object_len += delta;
+ return 1;
+}
+
+void *dm_pool_end_object(struct dm_pool *p)
+{
+ struct chunk *c = p->chunk;
+ void *r = c->begin;
+ c->begin += p->object_len;
+ p->object_len = 0u;
+ p->object_alignment = DEFAULT_ALIGNMENT;
+ return r;
+}
+
+void dm_pool_abandon_object(struct dm_pool *p)
+{
+ p->object_len = 0;
+ p->object_alignment = DEFAULT_ALIGNMENT;
+}
+
+static void _align_chunk(struct chunk *c, unsigned alignment)
+{
+ c->begin += alignment - ((unsigned long) c->begin & (alignment - 1));
+}
+
+static struct chunk *_new_chunk(struct dm_pool *p, size_t s)
+{
+ struct chunk *c;
+
+ if (p->spare_chunk &&
+ ((p->spare_chunk->end - p->spare_chunk->begin) >= s)) {
+ /* reuse old chunk */
+ c = p->spare_chunk;
+ p->spare_chunk = 0;
+ } else {
+ if (!(c = dm_malloc(s))) {
+ log_error("Out of memory. Requested %" PRIsize_t
+ " bytes.", s);
+ return NULL;
+ }
+
+ c->begin = (char *) (c + 1);
+ c->end = (char *) c + s;
+
+#ifdef VALGRIND_POOL
+ VALGRIND_MAKE_MEM_NOACCESS(c->begin, c->end - c->begin);
+#endif
+ }
+
+ c->prev = p->chunk;
+ p->chunk = c;
+ return c;
+}
+
+static void _free_chunk(struct chunk *c)
+{
+ if (c) {
+#ifdef VALGRIND_POOL
+ VALGRIND_MAKE_MEM_UNDEFINED(c, c->end - (char *) c);
+#endif
+
+ dm_free(c);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+
+/* FIXME: thread unsafe */
+static DM_LIST_INIT(_dm_pools);
+void dm_pools_check_leaks(void);
+
+#ifdef DEBUG_POOL
+#include "pool-debug.c"
+#else
+#include "pool-fast.c"
+#endif
+
+char *dm_pool_strdup(struct dm_pool *p, const char *str)
+{
+ char *ret = dm_pool_alloc(p, strlen(str) + 1);
+
+ if (ret)
+ strcpy(ret, str);
+
+ return ret;
+}
+
+char *dm_pool_strndup(struct dm_pool *p, const char *str, size_t n)
+{
+ char *ret = dm_pool_alloc(p, n + 1);
+
+ if (ret) {
+ strncpy(ret, str, n);
+ ret[n] = '\0';
+ }
+
+ return ret;
+}
+
+void *dm_pool_zalloc(struct dm_pool *p, size_t s)
+{
+ void *ptr = dm_pool_alloc(p, s);
+
+ if (ptr)
+ memset(ptr, 0, s);
+
+ return ptr;
+}
+
+void dm_pools_check_leaks(void)
+{
+ struct dm_pool *p;
+
+ if (dm_list_empty(&_dm_pools))
+ return;
+
+ log_error("You have a memory leak (not released memory pool):");
+ dm_list_iterate_items(p, &_dm_pools) {
+#ifdef DEBUG_POOL
+ log_error(" [%p] %s (%u bytes)",
+ p->orig_pool,
+ p->name, p->stats.bytes);
+#else
+ log_error(" [%p]", p);
+#endif
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+#include "parse_rx.h"
+#include "ttree.h"
+#include "assert.h"
+
+struct dfa_state {
+ struct dfa_state *next;
+ int final;
+ dm_bitset_t bits;
+ struct dfa_state *lookup[256];
+};
+
+struct dm_regex { /* Instance variables for the lexer */
+ struct dfa_state *start;
+ unsigned num_nodes;
+ unsigned num_charsets;
+ int nodes_entered;
+ struct rx_node **nodes;
+ int charsets_entered;
+ struct rx_node **charsets;
+ struct dm_pool *scratch, *mem;
+
+ /* stuff for on the fly dfa calculation */
+ dm_bitset_t charmap[256];
+ dm_bitset_t dfa_copy;
+ struct ttree *tt;
+ dm_bitset_t bs;
+ struct dfa_state *h, *t;
+};
+
+static int _count_nodes(struct rx_node *rx)
+{
+ int r = 1;
+
+ if (rx->left)
+ r += _count_nodes(rx->left);
+
+ if (rx->right)
+ r += _count_nodes(rx->right);
+
+ return r;
+}
+
+static unsigned _count_charsets(struct rx_node *rx)
+{
+ if (rx->type == CHARSET)
+ return 1;
+
+ return (rx->left ? _count_charsets(rx->left) : 0) +
+ (rx->right ? _count_charsets(rx->right) : 0);
+}
+
+static void _enumerate_charsets_internal(struct rx_node *rx, unsigned *i)
+{
+ if (rx->type == CHARSET)
+ rx->charset_index = (*i)++;
+ else {
+ if (rx->left)
+ _enumerate_charsets_internal(rx->left, i);
+ if (rx->right)
+ _enumerate_charsets_internal(rx->right, i);
+ }
+}
+
+static void _enumerate_charsets(struct rx_node *rx)
+{
+ unsigned i = 0;
+ _enumerate_charsets_internal(rx, &i);
+}
+
+static void _fill_table(struct dm_regex *m, struct rx_node *rx)
+{
+ assert((rx->type != OR) || (rx->left && rx->right));
+
+ if (rx->left)
+ _fill_table(m, rx->left);
+
+ if (rx->right)
+ _fill_table(m, rx->right);
+
+ m->nodes[m->nodes_entered++] = rx;
+ if (rx->type == CHARSET)
+ m->charsets[m->charsets_entered++] = rx;
+}
+
+static void _create_bitsets(struct dm_regex *m)
+{
+ int i;
+
+ for (i = 0; i < m->num_nodes; i++) {
+ struct rx_node *n = m->nodes[i];
+ n->firstpos = dm_bitset_create(m->scratch, m->num_charsets);
+ n->lastpos = dm_bitset_create(m->scratch, m->num_charsets);
+ n->followpos = dm_bitset_create(m->scratch, m->num_charsets);
+ }
+}
+
+static void _calc_functions(struct dm_regex *m)
+{
+ int i, j, final = 1;
+ struct rx_node *rx, *c1, *c2;
+
+ for (i = 0; i < m->num_nodes; i++) {
+ rx = m->nodes[i];
+ c1 = rx->left;
+ c2 = rx->right;
+
+ if (rx->type == CHARSET && dm_bit(rx->charset, TARGET_TRANS))
+ rx->final = final++;
+
+ switch (rx->type) {
+ case CAT:
+ if (c1->nullable)
+ dm_bit_union(rx->firstpos,
+ c1->firstpos, c2->firstpos);
+ else
+ dm_bit_copy(rx->firstpos, c1->firstpos);
+
+ if (c2->nullable)
+ dm_bit_union(rx->lastpos,
+ c1->lastpos, c2->lastpos);
+ else
+ dm_bit_copy(rx->lastpos, c2->lastpos);
+
+ rx->nullable = c1->nullable && c2->nullable;
+ break;
+
+ case PLUS:
+ dm_bit_copy(rx->firstpos, c1->firstpos);
+ dm_bit_copy(rx->lastpos, c1->lastpos);
+ rx->nullable = c1->nullable;
+ break;
+
+ case OR:
+ dm_bit_union(rx->firstpos, c1->firstpos, c2->firstpos);
+ dm_bit_union(rx->lastpos, c1->lastpos, c2->lastpos);
+ rx->nullable = c1->nullable || c2->nullable;
+ break;
+
+ case QUEST:
+ case STAR:
+ dm_bit_copy(rx->firstpos, c1->firstpos);
+ dm_bit_copy(rx->lastpos, c1->lastpos);
+ rx->nullable = 1;
+ break;
+
+ case CHARSET:
+ dm_bit_set(rx->firstpos, rx->charset_index);
+ dm_bit_set(rx->lastpos, rx->charset_index);
+ rx->nullable = 0;
+ break;
+
+ default:
+ log_error(INTERNAL_ERROR "Unknown calc node type");
+ }
+
+ /*
+ * followpos has it's own switch
+ * because PLUS and STAR do the
+ * same thing.
+ */
+ switch (rx->type) {
+ case CAT:
+ for (j = 0; j < m->num_charsets; j++) {
+ struct rx_node *n = m->charsets[j];
+ if (dm_bit(c1->lastpos, j))
+ dm_bit_union(n->followpos,
+ n->followpos, c2->firstpos);
+ }
+ break;
+
+ case PLUS:
+ case STAR:
+ for (j = 0; j < m->num_charsets; j++) {
+ struct rx_node *n = m->charsets[j];
+ if (dm_bit(rx->lastpos, j))
+ dm_bit_union(n->followpos,
+ n->followpos, rx->firstpos);
+ }
+ break;
+ }
+ }
+}
+
+static struct dfa_state *_create_dfa_state(struct dm_pool *mem)
+{
+ return dm_pool_zalloc(mem, sizeof(struct dfa_state));
+}
+
+static struct dfa_state *_create_state_queue(struct dm_pool *mem,
+ struct dfa_state *dfa,
+ dm_bitset_t bits)
+{
+ dfa->bits = dm_bitset_create(mem, bits[0]); /* first element is the size */
+ dm_bit_copy(dfa->bits, bits);
+ dfa->next = 0;
+ dfa->final = -1;
+ return dfa;
+}
+
+static void _calc_state(struct dm_regex *m, struct dfa_state *dfa, int a)
+{
+ int set_bits = 0, i;
+ dm_bitset_t dfa_bits = dfa->bits;
+ dm_bit_and(m->dfa_copy, m->charmap[a], dfa_bits);
+
+ /* iterate through all the states in firstpos */
+ for (i = dm_bit_get_first(m->dfa_copy); i >= 0; i = dm_bit_get_next(m->dfa_copy, i)) {
+ if (a == TARGET_TRANS)
+ dfa->final = m->charsets[i]->final;
+
+ dm_bit_union(m->bs, m->bs, m->charsets[i]->followpos);
+ set_bits = 1;
+ }
+
+ if (set_bits) {
+ struct dfa_state *tmp;
+ struct dfa_state *ldfa = ttree_lookup(m->tt, m->bs + 1);
+ if (!ldfa) {
+ /* push */
+ ldfa = _create_dfa_state(m->mem);
+ ttree_insert(m->tt, m->bs + 1, ldfa);
+ tmp = _create_state_queue(m->scratch, ldfa, m->bs);
+ if (!m->h)
+ m->h = m->t = tmp;
+ else {
+ m->t->next = tmp;
+ m->t = tmp;
+ }
+ }
+
+ dfa->lookup[a] = ldfa;
+ dm_bit_clear_all(m->bs);
+ }
+}
+
+static int _calc_states(struct dm_regex *m, struct rx_node *rx)
+{
+ unsigned iwidth = (m->num_charsets / DM_BITS_PER_INT) + 1;
+ struct dfa_state *dfa;
+ int i, a;
+
+ m->tt = ttree_create(m->scratch, iwidth);
+ if (!m->tt)
+ return_0;
+
+ if (!(m->bs = dm_bitset_create(m->scratch, m->num_charsets)))
+ return_0;
+
+ /* build some char maps */
+ for (a = 0; a < 256; a++) {
+ m->charmap[a] = dm_bitset_create(m->scratch, m->num_charsets);
+ if (!m->charmap[a])
+ return_0;
+ }
+
+ for (i = 0; i < m->num_nodes; i++) {
+ struct rx_node *n = m->nodes[i];
+ if (n->type == CHARSET) {
+ for (a = dm_bit_get_first(n->charset);
+ a >= 0; a = dm_bit_get_next(n->charset, a))
+ dm_bit_set(m->charmap[a], n->charset_index);
+ }
+ }
+
+ /* create first state */
+ dfa = _create_dfa_state(m->mem);
+ m->start = dfa;
+ ttree_insert(m->tt, rx->firstpos + 1, dfa);
+
+ /* prime the queue */
+ m->h = m->t = _create_state_queue(m->scratch, dfa, rx->firstpos);
+ m->dfa_copy = dm_bitset_create(m->scratch, m->num_charsets);
+ return 1;
+}
+
+/*
+ * Forces all the dfa states to be calculated up front, ie. what
+ * _calc_states() used to do before we switched to calculating on demand.
+ */
+static void _force_states(struct dm_regex *m)
+{
+ int a;
+
+ /* keep processing until there's nothing in the queue */
+ struct dfa_state *s;
+ while ((s = m->h)) {
+ /* pop state off front of the queue */
+ m->h = m->h->next;
+
+ /* iterate through all the inputs for this state */
+ dm_bit_clear_all(m->bs);
+ for (a = 0; a < 256; a++)
+ _calc_state(m, s, a);
+ }
+}
+
+struct dm_regex *dm_regex_create(struct dm_pool *mem, const char * const *patterns,
+ unsigned num_patterns)
+{
+ char *all, *ptr;
+ int i;
+ size_t len = 0;
+ struct rx_node *rx;
+ struct dm_regex *m;
+ struct dm_pool *scratch = mem;
+
+ if (!(m = dm_pool_zalloc(mem, sizeof(*m))))
+ return_NULL;
+
+ /* join the regexps together, delimiting with zero */
+ for (i = 0; i < num_patterns; i++)
+ len += strlen(patterns[i]) + 8;
+
+ ptr = all = dm_pool_alloc(scratch, len + 1);
+
+ if (!all)
+ goto_bad;
+
+ for (i = 0; i < num_patterns; i++) {
+ ptr += sprintf(ptr, "(.*(%s)%c)", patterns[i], TARGET_TRANS);
+ if (i < (num_patterns - 1))
+ *ptr++ = '|';
+ }
+
+ /* parse this expression */
+ if (!(rx = rx_parse_tok(scratch, all, ptr))) {
+ log_error("Couldn't parse regex");
+ goto bad;
+ }
+
+ m->mem = mem;
+ m->scratch = scratch;
+ m->num_nodes = _count_nodes(rx);
+ m->num_charsets = _count_charsets(rx);
+ _enumerate_charsets(rx);
+ m->nodes = dm_pool_alloc(scratch, sizeof(*m->nodes) * m->num_nodes);
+ if (!m->nodes)
+ goto_bad;
+
+ m->charsets = dm_pool_alloc(scratch, sizeof(*m->charsets) * m->num_charsets);
+ if (!m->charsets)
+ goto_bad;
+
+ _fill_table(m, rx);
+ _create_bitsets(m);
+ _calc_functions(m);
+ _calc_states(m, rx);
+ return m;
+
+ bad:
+ dm_pool_free(mem, m);
+ return NULL;
+}
+
+static struct dfa_state *_step_matcher(struct dm_regex *m, int c, struct dfa_state *cs, int *r)
+{
+ struct dfa_state *ns;
+
+ if (!(ns = cs->lookup[(unsigned char) c])) {
+ _calc_state(m, cs, (unsigned char) c);
+ if (!(ns = cs->lookup[(unsigned char) c]))
+ return NULL;
+ }
+
+ // yuck, we have to special case the target trans
+ if (ns->final == -1)
+ _calc_state(m, ns, TARGET_TRANS);
+
+ if (ns->final && (ns->final > *r))
+ *r = ns->final;
+
+ return ns;
+}
+
+int dm_regex_match(struct dm_regex *regex, const char *s)
+{
+ struct dfa_state *cs = regex->start;
+ int r = 0;
+
+ dm_bit_clear_all(regex->bs);
+ if (!(cs = _step_matcher(regex, HAT_CHAR, cs, &r)))
+ goto out;
+
+ for (; *s; s++)
+ if (!(cs = _step_matcher(regex, *s, cs, &r)))
+ goto out;
+
+ _step_matcher(regex, DOLLAR_CHAR, cs, &r);
+
+ out:
+ /* subtract 1 to get back to zero index */
+ return r - 1;
+}
+
+/*
+ * The next block of code concerns calculating a fingerprint for the dfa.
+ *
+ * We're not calculating a minimal dfa in _calculate_state (maybe a future
+ * improvement). As such it's possible that two non-isomorphic dfas
+ * recognise the same language. This can only really happen if you start
+ * with equivalent, but different regexes (for example the simplifier in
+ * parse_rx.c may have changed).
+ *
+ * The code is inefficient; repeatedly searching a singly linked list for
+ * previously seen nodes. Not worried since this is test code.
+ */
+struct node_list {
+ unsigned node_id;
+ struct dfa_state *node;
+ struct node_list *next;
+};
+
+struct printer {
+ struct dm_pool *mem;
+ struct node_list *pending;
+ struct node_list *processed;
+ unsigned next_index;
+};
+
+static uint32_t randomise_(uint32_t n)
+{
+ /* 2^32 - 5 */
+ uint32_t const prime = (~0) - 4;
+ return n * prime;
+}
+
+static int seen_(struct node_list *n, struct dfa_state *node, uint32_t *i)
+{
+ while (n) {
+ if (n->node == node) {
+ *i = n->node_id;
+ return 1;
+ }
+ n = n->next;
+ }
+
+ return 0;
+}
+
+/*
+ * Push node if it's not been seen before, returning a unique index.
+ */
+static uint32_t push_node_(struct printer *p, struct dfa_state *node)
+{
+ uint32_t i;
+ if (seen_(p->pending, node, &i) ||
+ seen_(p->processed, node, &i))
+ return i;
+ else {
+ struct node_list *n = dm_pool_alloc(p->mem, sizeof(*n));
+ assert(n);
+ n->node_id = p->next_index++;
+ n->node = node;
+ n->next = p->pending;
+ p->pending = n;
+ return n->node_id;
+ }
+}
+
+/*
+ * Pop the front node, and fill out it's previously assigned index.
+ */
+static struct dfa_state *pop_node_(struct printer *p)
+{
+ struct dfa_state *node = NULL;
+
+ if (p->pending) {
+ struct node_list *n = p->pending;
+ p->pending = n->next;
+ n->next = p->processed;
+ p->processed = n;
+
+ node = n->node;
+ }
+
+ return node;
+}
+
+static uint32_t combine_(uint32_t n1, uint32_t n2)
+{
+ return ((n1 << 8) | (n1 >> 24)) ^ randomise_(n2);
+}
+
+static uint32_t fingerprint_(struct printer *p)
+{
+ int c;
+ uint32_t result = 0;
+ struct dfa_state *node;
+
+ while ((node = pop_node_(p))) {
+ result = combine_(result, node->final < 0 ? 0 : node->final);
+ for (c = 0; c < 256; c++)
+ result = combine_(result,
+ push_node_(p, node->lookup[c]));
+ }
+
+ return result;
+}
+
+uint32_t dm_regex_fingerprint(struct dm_regex *regex)
+{
+ uint32_t result;
+ struct printer p;
+ struct dm_pool *mem = dm_pool_create("regex fingerprint", 1024);
+
+ _force_states(regex);
+
+ assert(mem);
+ p.mem = mem;
+ p.pending = NULL;
+ p.processed = NULL;
+ p.next_index = 0;
+
+ push_node_(&p, regex->start);
+ result = fingerprint_(&p);
+ dm_pool_destroy(mem);
+ return result;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+#include "parse_rx.h"
+
+#ifdef DEBUG
+#include <ctype.h>
+
+static void _regex_print(struct rx_node *rx, int depth, unsigned show_nodes)
+{
+ int i, numchars;
+
+ if (rx->left) {
+ if (rx->left->type != CHARSET && (show_nodes || (!((rx->type == CAT || rx->type == OR) && rx->left->type == CAT))))
+ printf("(");
+
+ _regex_print(rx->left, depth + 1, show_nodes);
+
+ if (rx->left->type != CHARSET && (show_nodes || (!((rx->type == CAT || rx->type == OR) && rx->left->type == CAT))))
+ printf(")");
+ }
+
+ /* display info about the node */
+ switch (rx->type) {
+ case CAT:
+ break;
+
+ case OR:
+ printf("|");
+ break;
+
+ case STAR:
+ printf("*");
+ break;
+
+ case PLUS:
+ printf("+");
+ break;
+
+ case QUEST:
+ printf("?");
+ break;
+
+ case CHARSET:
+ numchars = 0;
+ for (i = 0; i < 256; i++)
+ if (dm_bit(rx->charset, i) && (isprint(i) || i == HAT_CHAR || i == DOLLAR_CHAR))
+ numchars++;
+ if (numchars == 97) {
+ printf(".");
+ break;
+ }
+ if (numchars > 1)
+ printf("[");
+ for (i = 0; i < 256; i++)
+ if (dm_bit(rx->charset, i)) {
+ if isprint(i)
+ printf("%c", (char) i);
+ else if (i == HAT_CHAR)
+ printf("^");
+ else if (i == DOLLAR_CHAR)
+ printf("$");
+ }
+ if (numchars > 1)
+ printf("]");
+ break;
+
+ default:
+ fprintf(stderr, "Unknown type");
+ }
+
+ if (rx->right) {
+ if (rx->right->type != CHARSET && (show_nodes || (!(rx->type == CAT && rx->right->type == CAT) && rx->right->right)))
+ printf("(");
+ _regex_print(rx->right, depth + 1, show_nodes);
+ if (rx->right->type != CHARSET && (show_nodes || (!(rx->type == CAT && rx->right->type == CAT) && rx->right->right)))
+ printf(")");
+ }
+
+ if (!depth)
+ printf("\n");
+}
+#endif /* DEBUG */
+
+struct parse_sp { /* scratch pad for the parsing process */
+ struct dm_pool *mem;
+ int type; /* token type, 0 indicates a charset */
+ dm_bitset_t charset; /* The current charset */
+ const char *cursor; /* where we are in the regex */
+ const char *rx_end; /* 1pte for the expression being parsed */
+};
+
+static struct rx_node *_or_term(struct parse_sp *ps);
+
+static void _single_char(struct parse_sp *ps, unsigned int c, const char *ptr)
+{
+ ps->type = 0;
+ ps->cursor = ptr + 1;
+ dm_bit_clear_all(ps->charset);
+ dm_bit_set(ps->charset, c);
+}
+
+/*
+ * Get the next token from the regular expression.
+ * Returns: 1 success, 0 end of input, -1 error.
+ */
+static int _rx_get_token(struct parse_sp *ps)
+{
+ int neg = 0, range = 0;
+ char c, lc = 0;
+ const char *ptr = ps->cursor;
+ if (ptr == ps->rx_end) { /* end of input ? */
+ ps->type = -1;
+ return 0;
+ }
+
+ switch (*ptr) {
+ /* charsets and ncharsets */
+ case '[':
+ ptr++;
+ if (*ptr == '^') {
+ dm_bit_set_all(ps->charset);
+
+ /* never transition on zero */
+ dm_bit_clear(ps->charset, 0);
+ neg = 1;
+ ptr++;
+
+ } else
+ dm_bit_clear_all(ps->charset);
+
+ while ((ptr < ps->rx_end) && (*ptr != ']')) {
+ if (*ptr == '\\') {
+ /* an escaped character */
+ ptr++;
+ switch (*ptr) {
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ default:
+ c = *ptr;
+ }
+ } else if (*ptr == '-' && lc) {
+ /* we've got a range on our hands */
+ range = 1;
+ ptr++;
+ if (ptr == ps->rx_end) {
+ log_error("Incomplete range"
+ "specification");
+ return -1;
+ }
+ c = *ptr;
+ } else
+ c = *ptr;
+
+ if (range) {
+ /* add lc - c into the bitset */
+ if (lc > c) {
+ char tmp = c;
+ c = lc;
+ lc = tmp;
+ }
+
+ for (; lc <= c; lc++) {
+ if (neg)
+ dm_bit_clear(ps->charset, lc);
+ else
+ dm_bit_set(ps->charset, lc);
+ }
+ range = 0;
+ } else {
+ /* add c into the bitset */
+ if (neg)
+ dm_bit_clear(ps->charset, c);
+ else
+ dm_bit_set(ps->charset, c);
+ }
+ ptr++;
+ lc = c;
+ }
+
+ if (ptr >= ps->rx_end) {
+ ps->type = -1;
+ return -1;
+ }
+
+ ps->type = 0;
+ ps->cursor = ptr + 1;
+ break;
+
+ /* These characters are special, we just return their ASCII
+ codes as the type. Sorted into ascending order to help the
+ compiler */
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case '?':
+ case '|':
+ ps->type = (int) *ptr;
+ ps->cursor = ptr + 1;
+ break;
+
+ case '^':
+ _single_char(ps, HAT_CHAR, ptr);
+ break;
+
+ case '$':
+ _single_char(ps, DOLLAR_CHAR, ptr);
+ break;
+
+ case '.':
+ /* The 'all but newline' character set */
+ ps->type = 0;
+ ps->cursor = ptr + 1;
+ dm_bit_set_all(ps->charset);
+ dm_bit_clear(ps->charset, (int) '\n');
+ dm_bit_clear(ps->charset, (int) '\r');
+ dm_bit_clear(ps->charset, 0);
+ break;
+
+ case '\\':
+ /* escaped character */
+ ptr++;
+ if (ptr >= ps->rx_end) {
+ log_error("Badly quoted character at end "
+ "of expression");
+ ps->type = -1;
+ return -1;
+ }
+
+ ps->type = 0;
+ ps->cursor = ptr + 1;
+ dm_bit_clear_all(ps->charset);
+ switch (*ptr) {
+ case 'n':
+ dm_bit_set(ps->charset, (int) '\n');
+ break;
+ case 'r':
+ dm_bit_set(ps->charset, (int) '\r');
+ break;
+ case 't':
+ dm_bit_set(ps->charset, (int) '\t');
+ break;
+ default:
+ dm_bit_set(ps->charset, (int) *ptr);
+ }
+ break;
+
+ default:
+ /* add a single character to the bitset */
+ ps->type = 0;
+ ps->cursor = ptr + 1;
+ dm_bit_clear_all(ps->charset);
+ dm_bit_set(ps->charset, (int) (unsigned char) *ptr);
+ break;
+ }
+
+ return 1;
+}
+
+static struct rx_node *_node(struct dm_pool *mem, int type,
+ struct rx_node *l, struct rx_node *r)
+{
+ struct rx_node *n = dm_pool_zalloc(mem, sizeof(*n));
+
+ if (n) {
+ if (type == CHARSET && !(n->charset = dm_bitset_create(mem, 256))) {
+ dm_pool_free(mem, n);
+ return NULL;
+ }
+
+ n->type = type;
+ n->left = l;
+ n->right = r;
+ }
+
+ return n;
+}
+
+static struct rx_node *_term(struct parse_sp *ps)
+{
+ struct rx_node *n;
+
+ switch (ps->type) {
+ case 0:
+ if (!(n = _node(ps->mem, CHARSET, NULL, NULL))) {
+ stack;
+ return NULL;
+ }
+
+ dm_bit_copy(n->charset, ps->charset);
+ _rx_get_token(ps); /* match charset */
+ break;
+
+ case '(':
+ _rx_get_token(ps); /* match '(' */
+ n = _or_term(ps);
+ if (ps->type != ')') {
+ log_error("missing ')' in regular expression");
+ return 0;
+ }
+ _rx_get_token(ps); /* match ')' */
+ break;
+
+ default:
+ n = 0;
+ }
+
+ return n;
+}
+
+static struct rx_node *_closure_term(struct parse_sp *ps)
+{
+ struct rx_node *l, *n;
+
+ if (!(l = _term(ps)))
+ return NULL;
+
+ for (;;) {
+ switch (ps->type) {
+ case '*':
+ n = _node(ps->mem, STAR, l, NULL);
+ break;
+
+ case '+':
+ n = _node(ps->mem, PLUS, l, NULL);
+ break;
+
+ case '?':
+ n = _node(ps->mem, QUEST, l, NULL);
+ break;
+
+ default:
+ return l;
+ }
+
+ if (!n) {
+ stack;
+ return NULL;
+ }
+
+ _rx_get_token(ps);
+ l = n;
+ }
+
+ return n;
+}
+
+static struct rx_node *_cat_term(struct parse_sp *ps)
+{
+ struct rx_node *l, *r, *n;
+
+ if (!(l = _closure_term(ps)))
+ return NULL;
+
+ if (ps->type == '|')
+ return l;
+
+ if (!(r = _cat_term(ps)))
+ return l;
+
+ if (!(n = _node(ps->mem, CAT, l, r)))
+ stack;
+
+ return n;
+}
+
+static struct rx_node *_or_term(struct parse_sp *ps)
+{
+ struct rx_node *l, *r, *n;
+
+ if (!(l = _cat_term(ps)))
+ return NULL;
+
+ if (ps->type != '|')
+ return l;
+
+ _rx_get_token(ps); /* match '|' */
+
+ if (!(r = _or_term(ps))) {
+ log_error("Badly formed 'or' expression");
+ return NULL;
+ }
+
+ if (!(n = _node(ps->mem, OR, l, r)))
+ stack;
+
+ return n;
+}
+
+/*----------------------------------------------------------------*/
+
+/* Macros for left and right nodes. Inverted if 'leftmost' is set. */
+#define LEFT(a) (leftmost ? (a)->left : (a)->right)
+#define RIGHT(a) (leftmost ? (a)->right : (a)->left)
+
+/*
+ * The optimiser spots common prefixes on either side of an 'or' node, and
+ * lifts them outside the 'or' with a 'cat'.
+ */
+static unsigned _depth(struct rx_node *r, unsigned leftmost)
+{
+ int count = 1;
+
+ while (r->type != CHARSET && LEFT(r) && (leftmost || r->type != OR)) {
+ count++;
+ r = LEFT(r);
+ }
+
+ return count;
+}
+
+/*
+ * FIXME: a unique key could be built up as part of the parse, to make the
+ * comparison quick. Alternatively we could use cons-hashing, and then
+ * this would simply be a pointer comparison.
+ */
+static int _nodes_equal(struct rx_node *l, struct rx_node *r)
+{
+ if (l->type != r->type)
+ return 0;
+
+ switch (l->type) {
+ case CAT:
+ case OR:
+ return _nodes_equal(l->left, r->left) &&
+ _nodes_equal(l->right, r->right);
+
+ case STAR:
+ case PLUS:
+ case QUEST:
+ return _nodes_equal(l->left, r->left);
+
+ case CHARSET:
+ /*
+ * Never change anything containing TARGET_TRANS
+ * used by matcher as boundary marker between concatenated
+ * expressions.
+ */
+ return (!dm_bit(l->charset, TARGET_TRANS) && dm_bitset_equal(l->charset, r->charset));
+ }
+
+ /* NOTREACHED */
+ return_0;
+}
+
+static int _find_leftmost_common(struct rx_node *or,
+ struct rx_node **l,
+ struct rx_node **r,
+ unsigned leftmost)
+{
+ struct rx_node *left = or->left, *right = or->right;
+ unsigned left_depth = _depth(left, leftmost);
+ unsigned right_depth = _depth(right, leftmost);
+
+ while (left_depth > right_depth && left->type != OR) {
+ left = LEFT(left);
+ left_depth--;
+ }
+
+ while (right_depth > left_depth && right->type != OR) {
+ right = LEFT(right);
+ right_depth--;
+ }
+
+ if (left_depth != right_depth)
+ return 0;
+
+ while (left_depth) {
+ if (left->type == CAT && right->type == CAT) {
+ if (_nodes_equal(LEFT(left), LEFT(right))) {
+ *l = left;
+ *r = right;
+ return 1;
+ }
+ }
+ if (left->type == OR || right->type == OR)
+ break;
+ left = LEFT(left);
+ right = LEFT(right);
+ left_depth--;
+ }
+
+ return 0;
+}
+
+/* If top node is OR, rotate (leftmost example) from ((ab)|((ac)|d)) to (((ab)|(ac))|d) */
+static int _rotate_ors(struct rx_node *r, unsigned leftmost)
+{
+ struct rx_node *old_node;
+
+ if (r->type != OR || RIGHT(r)->type != OR)
+ return 0;
+
+ old_node = RIGHT(r);
+
+ if (leftmost) {
+ r->right = RIGHT(old_node);
+ old_node->right = LEFT(old_node);
+ old_node->left = LEFT(r);
+ r->left = old_node;
+ } else {
+ r->left = RIGHT(old_node);
+ old_node->left = LEFT(old_node);
+ old_node->right = LEFT(r);
+ r->right = old_node;
+ }
+
+ return 1;
+}
+
+static struct rx_node *_exchange_nodes(struct dm_pool *mem, struct rx_node *r,
+ struct rx_node *left_cat, struct rx_node *right_cat,
+ unsigned leftmost)
+{
+ struct rx_node *new_r;
+
+ if (leftmost)
+ new_r = _node(mem, CAT, LEFT(left_cat), r);
+ else
+ new_r = _node(mem, CAT, r, LEFT(right_cat));
+
+ if (!new_r)
+ return_NULL;
+
+ memcpy(left_cat, RIGHT(left_cat), sizeof(*left_cat));
+ memcpy(right_cat, RIGHT(right_cat), sizeof(*right_cat));
+
+ return new_r;
+}
+
+static struct rx_node *_pass(struct dm_pool *mem,
+ struct rx_node *r,
+ int *changed)
+{
+ struct rx_node *left, *right;
+
+ /*
+ * walk the tree, optimising every 'or' node.
+ */
+ switch (r->type) {
+ case CAT:
+ if (!(r->left = _pass(mem, r->left, changed)))
+ return_NULL;
+
+ if (!(r->right = _pass(mem, r->right, changed)))
+ return_NULL;
+
+ break;
+
+ case STAR:
+ case PLUS:
+ case QUEST:
+ if (!(r->left = _pass(mem, r->left, changed)))
+ return_NULL;
+
+ break;
+ case OR:
+ /* It's important we optimise sub nodes first */
+ if (!(r->left = _pass(mem, r->left, changed)))
+ return_NULL;
+
+ if (!(r->right = _pass(mem, r->right, changed)))
+ return_NULL;
+ /*
+ * If rotate_ors changes the tree, left and right are stale,
+ * so just set 'changed' to repeat the search.
+ *
+ * FIXME Check we can't 'bounce' between left and right rotations here.
+ */
+ if (_find_leftmost_common(r, &left, &right, 1)) {
+ if (!_rotate_ors(r, 1))
+ r = _exchange_nodes(mem, r, left, right, 1);
+ *changed = 1;
+ } else if (_find_leftmost_common(r, &left, &right, 0)) {
+ if (!_rotate_ors(r, 0))
+ r = _exchange_nodes(mem, r, left, right, 0);
+ *changed = 1;
+ }
+ break;
+
+ case CHARSET:
+ break;
+ }
+
+ return r;
+}
+
+static struct rx_node *_optimise(struct dm_pool *mem, struct rx_node *r)
+{
+ /*
+ * We're looking for (or (... (cat <foo> a)) (... (cat <foo> b)))
+ * and want to turn it into (cat <foo> (or (... a) (... b)))
+ *
+ * (fa)|(fb) becomes f(a|b)
+ */
+
+ /*
+ * Initially done as an inefficient multipass algorithm.
+ */
+ int changed;
+
+ do {
+ changed = 0;
+ r = _pass(mem, r, &changed);
+ } while (r && changed);
+
+ return r;
+}
+
+/*----------------------------------------------------------------*/
+
+struct rx_node *rx_parse_tok(struct dm_pool *mem,
+ const char *begin, const char *end)
+{
+ struct rx_node *r;
+ struct parse_sp *ps = dm_pool_zalloc(mem, sizeof(*ps));
+
+ if (!ps)
+ return_NULL;
+
+ ps->mem = mem;
+ if (!(ps->charset = dm_bitset_create(mem, 256))) {
+ log_error("Regex charset allocation failed");
+ dm_pool_free(mem, ps);
+ return NULL;
+ }
+ ps->cursor = begin;
+ ps->rx_end = end;
+ _rx_get_token(ps); /* load the first token */
+
+ if (!(r = _or_term(ps))) {
+ log_error("Parse error in regex");
+ dm_pool_free(mem, ps);
+ return NULL;
+ }
+
+ if (!(r = _optimise(mem, r))) {
+ log_error("Regex optimisation error");
+ dm_pool_free(mem, ps);
+ return NULL;
+ }
+
+ return r;
+}
+
+struct rx_node *rx_parse_str(struct dm_pool *mem, const char *str)
+{
+ return rx_parse_tok(mem, str, str + strlen(str));
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DM_PARSE_REGEX_H
+#define _DM_PARSE_REGEX_H
+
+enum {
+ CAT,
+ STAR,
+ PLUS,
+ OR,
+ QUEST,
+ CHARSET
+};
+
+/*
+ * We're never going to be running the regex on non-printable
+ * chars, so we can use a couple of these chars to represent the
+ * start and end of a string.
+ */
+#define HAT_CHAR 0x2
+#define DOLLAR_CHAR 0x3
+
+#define TARGET_TRANS '\0'
+
+struct rx_node {
+ int type;
+ dm_bitset_t charset;
+ struct rx_node *left, *right;
+
+ /* used to build the dfa for the toker */
+ unsigned charset_index;
+ int nullable, final;
+ dm_bitset_t firstpos;
+ dm_bitset_t lastpos;
+ dm_bitset_t followpos;
+};
+
+struct rx_node *rx_parse_str(struct dm_pool *mem, const char *str);
+struct rx_node *rx_parse_tok(struct dm_pool *mem,
+ const char *begin, const char *end);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "dmlib.h"
+#include "ttree.h"
+
+struct node {
+ unsigned k;
+ struct node *l, *m, *r;
+ void *data;
+};
+
+struct ttree {
+ int klen;
+ struct dm_pool *mem;
+ struct node *root;
+};
+
+static struct node **_lookup_single(struct node **c, unsigned int k)
+{
+ while (*c) {
+ if (k < (*c)->k)
+ c = &((*c)->l);
+
+ else if (k > (*c)->k)
+ c = &((*c)->r);
+
+ else {
+ c = &((*c)->m);
+ break;
+ }
+ }
+
+ return c;
+}
+
+void *ttree_lookup(struct ttree *tt, unsigned *key)
+{
+ struct node **c = &tt->root;
+ int count = tt->klen;
+
+ while (*c && count) {
+ c = _lookup_single(c, *key++);
+ count--;
+ }
+
+ return *c ? (*c)->data : NULL;
+}
+
+static struct node *_tree_node(struct dm_pool *mem, unsigned int k)
+{
+ struct node *n = dm_pool_zalloc(mem, sizeof(*n));
+
+ if (n)
+ n->k = k;
+
+ return n;
+}
+
+int ttree_insert(struct ttree *tt, unsigned int *key, void *data)
+{
+ struct node **c = &tt->root;
+ int count = tt->klen;
+ unsigned int k;
+
+ do {
+ k = *key++;
+ c = _lookup_single(c, k);
+ count--;
+
+ } while (*c && count);
+
+ if (!*c) {
+ count++;
+
+ while (count--) {
+ if (!(*c = _tree_node(tt->mem, k))) {
+ stack;
+ return 0;
+ }
+
+ if (count) {
+ k = *key++;
+ c = &((*c)->m);
+ }
+ }
+ }
+ (*c)->data = data;
+
+ return 1;
+}
+
+struct ttree *ttree_create(struct dm_pool *mem, unsigned int klen)
+{
+ struct ttree *tt;
+
+ if (!(tt = dm_pool_zalloc(mem, sizeof(*tt)))) {
+ stack;
+ return NULL;
+ }
+
+ tt->klen = klen;
+ tt->mem = mem;
+ return tt;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DM_TTREE_H
+#define _DM_TTREE_H
+
+struct ttree;
+
+struct ttree *ttree_create(struct dm_pool *mem, unsigned int klen);
+
+void *ttree_lookup(struct ttree *tt, unsigned *key);
+int ttree_insert(struct ttree *tt, unsigned *key, void *data);
+
+#endif
--- /dev/null
+# Doxyfile 1.5.7.1
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+DOXYFILE_ENCODING = UTF-8
+PROJECT_NAME =
+PROJECT_NUMBER =
+OUTPUT_DIRECTORY = doxygen-output
+CREATE_SUBDIRS = NO
+OUTPUT_LANGUAGE = English
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ABBREVIATE_BRIEF =
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH =
+STRIP_FROM_INC_PATH =
+SHORT_NAMES = NO
+JAVADOC_AUTOBRIEF = NO
+QT_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS = YES
+SEPARATE_MEMBER_PAGES = NO
+TAB_SIZE = 8
+ALIASES =
+OPTIMIZE_OUTPUT_FOR_C = NO
+OPTIMIZE_OUTPUT_JAVA = NO
+OPTIMIZE_FOR_FORTRAN = NO
+OPTIMIZE_OUTPUT_VHDL = NO
+BUILTIN_STL_SUPPORT = NO
+CPP_CLI_SUPPORT = NO
+SIP_SUPPORT = NO
+IDL_PROPERTY_SUPPORT = YES
+DISTRIBUTE_GROUP_DOC = NO
+SUBGROUPING = YES
+TYPEDEF_HIDES_STRUCT = NO
+SYMBOL_CACHE_SIZE = 0
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL = YES
+EXTRACT_PRIVATE = YES
+EXTRACT_STATIC = NO
+EXTRACT_LOCAL_CLASSES = YES
+EXTRACT_LOCAL_METHODS = NO
+EXTRACT_ANON_NSPACES = NO
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+HIDE_IN_BODY_DOCS = NO
+INTERNAL_DOCS = NO
+CASE_SENSE_NAMES = YES
+HIDE_SCOPE_NAMES = NO
+SHOW_INCLUDE_FILES = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+SORT_GROUP_NAMES = NO
+SORT_BY_SCOPE_NAME = NO
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+SHOW_USED_FILES = YES
+SHOW_DIRECTORIES = NO
+SHOW_FILES = YES
+SHOW_NAMESPACES = YES
+FILE_VERSION_FILTER =
+LAYOUT_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = ./
+INPUT_ENCODING = UTF-8
+FILE_PATTERNS = *.c \
+ *.h
+RECURSIVE = NO
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS =
+EXCLUDE_SYMBOLS =
+EXAMPLE_PATH = ../test/api
+EXAMPLE_PATTERNS =
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_PATTERNS =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = NO
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION = NO
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS = NO
+VERBATIM_HEADERS = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = NO
+COLS_IN_ALPHA_INDEX = 5
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_ALIGN_MEMBERS = YES
+HTML_DYNAMIC_SECTIONS = NO
+GENERATE_DOCSET = NO
+DOCSET_FEEDNAME = "Doxygen generated docs"
+DOCSET_BUNDLE_ID = org.doxygen.Project
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+CHM_INDEX_ENCODING =
+BINARY_TOC = NO
+TOC_EXPAND = NO
+GENERATE_QHP = NO
+QCH_FILE =
+QHP_NAMESPACE = org.doxygen.Project
+QHP_VIRTUAL_FOLDER = doc
+QHG_LOCATION =
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NONE
+TREEVIEW_WIDTH = 250
+FORMULA_FONTSIZE = 10
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = YES
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = YES
+USE_PDFLATEX = YES
+LATEX_BATCHMODE = NO
+LATEX_HIDE_INDICES = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = YES
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = NO
+XML_OUTPUT = xml
+XML_SCHEMA =
+XML_DTD =
+XML_PROGRAMLISTING = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD = NO
+PERLMOD_LATEX = NO
+PERLMOD_PRETTY = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH = ../libdm
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+MSCGEN_PATH =
+HIDE_UNDOC_RELATIONS = YES
+HAVE_DOT = NO
+DOT_FONTNAME = FreeSans
+DOT_FONTSIZE = 10
+DOT_FONTPATH =
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+GROUP_GRAPHS = YES
+UML_LOOK = NO
+TEMPLATE_RELATIONS = NO
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = YES
+CALLER_GRAPH = NO
+GRAPHICAL_HIERARCHY = YES
+DIRECTORY_GRAPH = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+DOT_GRAPH_MAX_NODES = 50
+MAX_DOT_GRAPH_DEPTH = 0
+DOT_TRANSPARENT = NO
+DOT_MULTI_TARGETS = NO
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
--- /dev/null
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES =\
+ lvm_misc.c \
+ lvm_base.c \
+ lvm_lv.c \
+ lvm_pv.c \
+ lvm_vg.c
+
+LIB_NAME = liblvm2app
+LIB_VERSION = $(LIB_VERSION_APP)
+
+ifeq ("@STATIC_LINK@", "yes")
+ LIB_STATIC = $(LIB_NAME).a
+endif
+
+LIB_SHARED = $(LIB_NAME).$(LIB_SUFFIX)
+
+CLEAN_TARGETS += liblvm.cflow $(LIB_NAME).a
+
+EXPORTED_HEADER = $(srcdir)/lvm2app.h
+EXPORTED_FN_PREFIX = lvm
+
+include $(top_builddir)/make.tmpl
+
+LIBS += $(LVMINTERNAL_LIBS) -ldevmapper
+
+ifeq ("@DMEVENTD@", "yes")
+ LIBS += -ldevmapper-event
+endif
+
+.PHONY: install_dynamic install_static install_include install_pkgconfig
+
+INSTALL_TYPE = install_dynamic
+
+ifeq ("@STATIC_LINK@", "yes")
+ INSTALL_TYPE += install_static
+endif
+
+ifeq ("@PKGCONFIG@", "yes")
+ INSTALL_TYPE += install_pkgconfig
+endif
+
+install: $(INSTALL_TYPE) install_include
+
+install_include: $(srcdir)/lvm2app.h
+ $(INSTALL_DATA) -D $< $(includedir)/$(<F)
+
+install_dynamic: install_lib_shared
+
+install_static: $(LIB_STATIC)
+ $(INSTALL_DATA) -D $< $(usrlibdir)/$(<F)
+
+install_pkgconfig: $(LIB_NAME).pc
+ $(INSTALL_DATA) -D $< $(pkgconfigdir)/lvm2app.pc
+
+liblvm.cflow: $(SOURCES)
+ set -e; (echo -n "SOURCES += "; \
+ echo $(SOURCES) | \
+ sed "s/^/ /;s/ / $(top_srcdir)\/liblvm\//g;s/$$//"; \
+ ) > $@
+
+cflow: liblvm.cflow
+
+DISTCLEAN_TARGETS += $(LIB_NAME).pc .exported_symbols_generated
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: lvm2app
+Description: lvm2 application library
+Version: @LVM_MAJOR@.@LVM_LIBAPI@
+Cflags: -I${includedir}
+Libs: -L${libdir} -llvm2app
+Requires.private: devmapper
--- /dev/null
+/*
+ * Copyright (C) 2008,2009,2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _LIB_LVM2APP_H
+#define _LIB_LVM2APP_H
+
+#include <libdevmapper.h>
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******************************** WARNING ***********************************
+ *
+ * NOTE: This API is under development and subject to change at any time.
+ *
+ * Please send feedback to lvm-devel@redhat.com
+ *
+ *********************************** WARNING ********************************/
+
+/*************************** Design Overview ********************************/
+
+/**
+ * \mainpage LVM library API
+ *
+ * The API is designed around the following basic LVM objects:
+ * 1) Physical Volume (pv_t) 2) Volume Group (vg_t) 3) Logical Volume (lv_t).
+ *
+ * The library provides functions to list the objects in a system,
+ * get and set object properties (such as names, UUIDs, and sizes), as well
+ * as create/remove objects and perform more complex operations and
+ * transformations. Each object instance is represented by a handle, and
+ * handles are passed to and from the functions to perform the operations.
+ *
+ * A central object in the library is the Volume Group, represented by the
+ * VG handle, vg_t. Performing an operation on a PV or LV object first
+ * requires obtaining a VG handle. Once the vg_t has been obtained, it can
+ * be used to enumerate the pv_t and lv_t objects within that vg_t. Attributes
+ * of these objects can then be queried or changed.
+ *
+ * A volume group handle may be obtained with read or write permission.
+ * Any attempt to change a property of a pv_t, vg_t, or lv_t without
+ * obtaining write permission on the vg_t will fail with EPERM.
+ *
+ * An application first opening a VG read-only, then later wanting to change
+ * a property of an object must first close the VG and re-open with write
+ * permission. Currently liblvm provides no mechanism to determine whether
+ * the VG has changed on-disk in between these operations - this is the
+ * application's responsiblity. One way the application can ensure the VG
+ * has not changed is to save the "vg_seqno" field after opening the VG with
+ * READ permission. If the application later needs to modify the VG, it can
+ * close the VG and re-open with WRITE permission. It should then check
+ * whether the original "vg_seqno" obtained with READ permission matches
+ * the new one obtained with WRITE permission.
+ */
+
+/**
+ * Retrieve the library version.
+ *
+ * The library version is the same format as the full LVM version.
+ * The format is as follows:
+ * LVM_MAJOR.LVM_MINOR.LVM_PATCHLEVEL(LVM_LIBAPI)[-LVM_RELEASE]
+ * An application wishing to determine compatibility with a particular version
+ * of the library should check at least the LVM_MAJOR, LVM_MINOR, and
+ * LVM_LIBAPI numbers. For example, assume the full LVM version is
+ * 2.02.50(1)-1. The application should verify the "2.02" and the "(1)".
+ *
+ * \return A string describing the library version.
+ */
+const char *lvm_library_get_version(void);
+
+/******************************** structures ********************************/
+
+/**
+ * Opaque structures - do not use directly. Internal structures may change
+ * without notice between releases, whereas this API will be changed much less
+ * frequently. Backwards compatibility will normally be preserved in future
+ * releases. On any occasion when the developers do decide to break backwards
+ * compatibility in any significant way, the LVM_LIBAPI number (included in
+ * the library's soname) will be incremented.
+ */
+struct lvm;
+struct physical_volume;
+struct volume_group;
+struct logical_volume;
+struct lv_segment;
+struct pv_segment;
+
+/**
+ * \class lvm_t
+ *
+ * This is the base handle that is needed to open and create objects such as
+ * volume groups and logical volumes. In addition, this handle provides a
+ * context for error handling information, saving any error number (see
+ * lvm_errno()) and error message (see lvm_errmsg()) that any function may
+ * generate.
+ */
+typedef struct lvm *lvm_t;
+
+/**
+ * \class vg_t
+ *
+ * The volume group object is a central object in the library, and can be
+ * either a read-only object or a read-write object depending on the function
+ * used to obtain the object handle. For example, lvm_vg_create() always
+ * returns a read/write handle, while lvm_vg_open() has a "mode" argument
+ * to define the read/write mode of the handle.
+ */
+typedef struct volume_group *vg_t;
+
+/**
+ * \class lv_t
+ *
+ * This logical volume object is bound to a vg_t and has the same
+ * read/write mode as the vg_t. Changes will be written to disk
+ * when the vg_t gets committed to disk by calling lvm_vg_write().
+ */
+typedef struct logical_volume *lv_t;
+
+/**
+ * \class pv_t
+ *
+ * This physical volume object is bound to a vg_t and has the same
+ * read/write mode as the vg_t. Changes will be written to disk
+ * when the vg_t gets committed to disk by calling lvm_vg_write().
+ */
+typedef struct physical_volume *pv_t;
+
+/**
+ * \class lvseg_t
+ *
+ * This lv segment object is bound to a lv_t.
+ */
+typedef struct lv_segment *lvseg_t;
+
+/**
+ * \class pvseg_t
+ *
+ * This pv segment object is bound to a pv_t.
+ */
+typedef struct pv_segment *pvseg_t;
+
+/**
+ * Logical Volume object list.
+ *
+ * Lists of these structures are returned by lvm_vg_list_lvs().
+ */
+typedef struct lvm_lv_list {
+ struct dm_list list;
+ lv_t lv;
+} lv_list_t;
+
+/**
+ * Logical Volume Segment object list.
+ *
+ * Lists of these structures are returned by lvm_lv_list_lvsegs().
+ */
+typedef struct lvm_lvseg_list {
+ struct dm_list list;
+ lvseg_t lvseg;
+} lvseg_list_t;
+
+/**
+ * Physical volume object list.
+ *
+ * Lists of these structures are returned by lvm_vg_list_pvs().
+ */
+typedef struct lvm_pv_list {
+ struct dm_list list;
+ pv_t pv;
+} pv_list_t;
+
+/**
+ * Physical Volume Segment object list.
+ *
+ * Lists of these structures are returned by lvm_pv_list_pvsegs().
+ */
+typedef struct lvm_pvseg_list {
+ struct dm_list list;
+ pvseg_t pvseg;
+} pvseg_list_t;
+
+/**
+ * String list.
+ *
+ * This string list contains read-only strings.
+ * Lists of these structures are returned by functions such as
+ * lvm_list_vg_names() and lvm_list_vg_uuids().
+ */
+typedef struct lvm_str_list {
+ struct dm_list list;
+ const char *str;
+} lvm_str_list_t;
+
+/**
+ * Property Value
+ *
+ * This structure defines a single LVM property value for an LVM object.
+ * The structures are returned by functions such as
+ * lvm_vg_get_property().
+ *
+ * is_settable: indicates whether a 'set' function exists for this property
+ * is_string: indicates whether this property is a string (1) or not (0)
+ * is_integer: indicates whether this property is an integer (1) or not (0)
+ * is_valid: indicates whether 'value' is valid (1) or not (0)
+ */
+typedef struct lvm_property_value {
+ uint32_t is_settable:1;
+ uint32_t is_string:1;
+ uint32_t is_integer:1;
+ uint32_t is_valid:1;
+ uint32_t padding:28;
+ union {
+ const char *string;
+ uint64_t integer;
+ } value;
+} lvm_property_value_t;
+
+/*************************** generic lvm handling ***************************/
+/**
+ * Create a LVM handle.
+ *
+ * \memberof lvm_t
+ *
+ * Once all LVM operations have been completed, use lvm_quit() to release
+ * the handle and any associated resources.
+ *
+ * \param system_dir
+ * Set an alternative LVM system directory. Use NULL to use the
+ * default value. If the environment variable LVM_SYSTEM_DIR is set,
+ * it will override any system_dir setting.
+ *
+ * \return
+ * A valid LVM handle is returned or NULL if there has been a
+ * memory allocation problem. You have to check if an error occured
+ * with the lvm_error() function.
+ */
+lvm_t lvm_init(const char *system_dir);
+
+/**
+ * Destroy a LVM handle allocated with lvm_init().
+ *
+ * \memberof lvm_t
+ *
+ * This function should be used after all LVM operations are complete or after
+ * an unrecoverable error. Destroying the LVM handle frees the memory and
+ * other resources associated with the handle. Once destroyed, the handle
+ * cannot be used subsequently.
+ *
+ * \param libh
+ * Handle obtained from lvm_init().
+ */
+void lvm_quit(lvm_t libh);
+
+/**
+ * Reload the original configuration from the system directory.
+ *
+ * \memberof lvm_t
+ *
+ * This function should be used when any LVM configuration changes in the LVM
+ * system_dir or by another lvm_config* function, and the change is needed by
+ * the application.
+ *
+ * \param libh
+ * Handle obtained from lvm_init().
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_config_reload(lvm_t libh);
+
+/**
+ * Override the LVM configuration with a configuration string.
+ *
+ * \memberof lvm_t
+ *
+ * This function is equivalent to the --config option on lvm commands.
+ * Once this API has been used to over-ride the configuration,
+ * use lvm_config_reload() to apply the new settings.
+ *
+ * \param libh
+ * Handle obtained from lvm_init().
+ *
+ * \param config_string
+ * LVM configuration string to apply. See the lvm.conf file man page
+ * for the format of the config string.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_config_override(lvm_t libh, const char *config_string);
+
+/**
+ * Return stored error no describing last LVM API error.
+ *
+ * \memberof lvm_t
+ *
+ * Users of liblvm should use lvm_errno to determine the details of a any
+ * failure of the last call. A basic success or fail is always returned by
+ * every function, either by returning a 0 or -1, or a non-NULL / NULL.
+ * If a function has failed, lvm_errno may be used to get a more specific
+ * error code describing the failure. In this way, lvm_errno may be used
+ * after every function call, even after a 'get' function call that simply
+ * returns a value.
+ *
+ * \param libh
+ * Handle obtained from lvm_init().
+ *
+ * \return
+ * An errno value describing the last LVM error.
+ */
+int lvm_errno(lvm_t libh);
+
+/**
+ * Return stored error message describing last LVM error.
+ *
+ * \memberof lvm_t
+ *
+ * This function may be used in conjunction with lvm_errno() to obtain more
+ * specific error information for a function that is known to have failed.
+ *
+ * \param libh
+ * Handle obtained from lvm_init().
+ *
+ * \return
+ * An error string describing the last LVM error.
+ */
+const char *lvm_errmsg(lvm_t libh);
+
+/**
+ * Scan all devices on the system for VGs and LVM metadata.
+ *
+ * \memberof lvm_t
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_scan(lvm_t libh);
+
+/**
+ * Return the list of volume group names.
+ *
+ * \memberof lvm_t
+ *
+ * The memory allocated for the list is tied to the lvm_t handle and will be
+ * released when lvm_quit() is called.
+ *
+ * NOTE: This function normally does not scan devices in the system for LVM
+ * metadata. To scan the system, use lvm_scan().
+ *
+ * To process the list, use the dm_list iterator functions. For example:
+ * vg_t vg;
+ * struct dm_list *vgnames;
+ * struct lvm_str_list *strl;
+ *
+ * vgnames = lvm_list_vg_names(libh);
+ * dm_list_iterate_items(strl, vgnames) {
+ * vgname = strl->str;
+ * vg = lvm_vg_open(libh, vgname, "r");
+ * // do something with vg
+ * lvm_vg_close(vg);
+ * }
+ *
+ *
+ * \return
+ * A list with entries of type struct lvm_str_list, containing the
+ * VG name strings of the Volume Groups known to the system.
+ * NULL is returned if unable to allocate memory.
+ * An empty list (verify with dm_list_empty) is returned if no VGs
+ * exist on the system.
+ */
+struct dm_list *lvm_list_vg_names(lvm_t libh);
+
+/**
+ * Return the list of volume group uuids.
+ *
+ * \memberof lvm_t
+ *
+ * The memory allocated for the list is tied to the lvm_t handle and will be
+ * released when lvm_quit() is called.
+ *
+ * NOTE: This function normally does not scan devices in the system for LVM
+ * metadata. To scan the system, use lvm_scan().
+ *
+ * \param libh
+ * Handle obtained from lvm_init().
+ *
+ * \return
+ * A list with entries of type struct lvm_str_list, containing the
+ * VG UUID strings of the Volume Groups known to the system.
+ * NULL is returned if unable to allocate memory.
+ * An empty list (verify with dm_list_empty) is returned if no VGs
+ * exist on the system.
+ */
+struct dm_list *lvm_list_vg_uuids(lvm_t libh);
+
+/**
+ * Return the volume group name given a PV UUID
+ *
+ * \memberof lvm_t
+ *
+ * The memory allocated for the name is tied to the lvm_t handle and will be
+ * released when lvm_quit() is called.
+ *
+ * NOTE: This function may scan devices in the system for LVM metadata.
+ *
+ * \param libh
+ * Handle obtained from lvm_init().
+ *
+ * \return
+ * The volume group name for the given PV UUID.
+ * NULL is returned if the PV UUID is not associated with a volume group.
+ */
+const char *lvm_vgname_from_pvid(lvm_t libh, const char *pvid);
+
+/**
+ * Return the volume group name given a device name
+ *
+ * \memberof lvm_t
+ *
+ * The memory allocated for the name is tied to the lvm_t handle and will be
+ * released when lvm_quit() is called.
+ *
+ * NOTE: This function may scan devices in the system for LVM metadata.
+ *
+ * \param libh
+ * Handle obtained from lvm_init().
+ *
+ * \return
+ * The volume group name for the given device name.
+ * NULL is returned if the device is not an LVM device.
+ *
+ */
+const char *lvm_vgname_from_device(lvm_t libh, const char *device);
+
+/**
+ * Open an existing VG.
+ *
+ * Open a VG for reading or writing.
+ *
+ * \memberof lvm_t
+ *
+ * \param libh
+ * Handle obtained from lvm_init().
+ *
+ * \param vgname
+ * Name of the VG to open.
+ *
+ * \param mode
+ * Open mode - either "r" (read) or "w" (read/write).
+ * Any other character results in an error with EINVAL set.
+ *
+ * \param flags
+ * Open flags - currently ignored.
+ *
+ * \return non-NULL VG handle (success) or NULL (failure).
+ */
+vg_t lvm_vg_open(lvm_t libh, const char *vgname, const char *mode,
+ uint32_t flags);
+
+/**
+ * Create a VG with default parameters.
+ *
+ * \memberof lvm_t
+ *
+ * This function creates a Volume Group object in memory.
+ * Upon success, other APIs may be used to set non-default parameters.
+ * For example, to set a non-default extent size, use lvm_vg_set_extent_size().
+ * Next, to add physical storage devices to the volume group, use
+ * lvm_vg_extend() for each device.
+ * Once all parameters are set appropriately and all devices are added to the
+ * VG, use lvm_vg_write() to commit the new VG to disk, and lvm_vg_close() to
+ * release the VG handle.
+ *
+ * \param libh
+ * Handle obtained from lvm_init().
+ *
+ * \param vg_name
+ * Name of the VG to open.
+ *
+ * \return
+ * non-NULL vg handle (success) or NULL (failure)
+ */
+vg_t lvm_vg_create(lvm_t libh, const char *vg_name);
+
+/*************************** volume group handling **************************/
+
+/**
+ * Return a list of LV handles for a given VG handle.
+ *
+ * \memberof vg_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * A list of lvm_lv_list structures containing lv handles for this vg.
+ * If no LVs exist on the given VG, NULL is returned.
+ */
+struct dm_list *lvm_vg_list_lvs(vg_t vg);
+
+/**
+ * Return a list of PV handles for a given VG handle.
+ *
+ * \memberof vg_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * A list of lvm_pv_list structures containing pv handles for this vg.
+ * If no PVs exist on the given VG, NULL is returned.
+ */
+struct dm_list *lvm_vg_list_pvs(vg_t vg);
+
+/**
+ * Write a VG to disk.
+ *
+ * \memberof vg_t
+ *
+ * This function commits the Volume Group object referenced by the VG handle
+ * to disk. Upon failure, retry the operation and/or release the VG handle
+ * with lvm_vg_close().
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_write(vg_t vg);
+
+/**
+ * Remove a VG from the system.
+ *
+ * \memberof vg_t
+ *
+ * This function removes a Volume Group object in memory, and requires
+ * calling lvm_vg_write() to commit the removal to disk.
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_remove(vg_t vg);
+
+/**
+ * Close a VG opened with lvm_vg_create or lvm_vg_open().
+ *
+ * \memberof vg_t
+ *
+ * This function releases a VG handle and any resources associated with the
+ * handle.
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_close(vg_t vg);
+
+/**
+ * Extend a VG by adding a device.
+ *
+ * \memberof vg_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully adding a device, use lvm_vg_write() to commit the new VG
+ * to disk. Upon failure, retry the operation or release the VG handle with
+ * lvm_vg_close().
+ * If the device is not initialized for LVM use, it will be initialized
+ * before adding to the VG. Although some internal checks are done,
+ * the caller should be sure the device is not in use by other subsystems
+ * before calling lvm_vg_extend().
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param device
+ * Absolute pathname of device to add to VG.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_extend(vg_t vg, const char *device);
+
+/**
+ * Reduce a VG by removing an unused device.
+ *
+ * \memberof vg_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully removing a device, use lvm_vg_write() to commit the new VG
+ * to disk. Upon failure, retry the operation or release the VG handle with
+ * lvm_vg_close().
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param device
+ * Name of device to remove from VG.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_reduce(vg_t vg, const char *device);
+
+/**
+ * Add a tag to a VG.
+ *
+ * \memberof vg_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully adding a tag, use lvm_vg_write() to commit the
+ * new VG to disk. Upon failure, retry the operation or release the VG handle
+ * with lvm_vg_close().
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param tag
+ * Tag to add to the VG.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_add_tag(vg_t vg, const char *tag);
+
+/**
+ * Remove a tag from a VG.
+ *
+ * \memberof vg_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully removing a tag, use lvm_vg_write() to commit the
+ * new VG to disk. Upon failure, retry the operation or release the VG handle
+ * with lvm_vg_close().
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param tag
+ * Tag to remove from VG.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_remove_tag(vg_t vg, const char *tag);
+
+/**
+ * Set the extent size of a VG.
+ *
+ * \memberof vg_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully setting a new extent size, use lvm_vg_write() to commit
+ * the new VG to disk. Upon failure, retry the operation or release the VG
+ * handle with lvm_vg_close().
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param new_size
+ * New extent size in bytes.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_set_extent_size(vg_t vg, uint32_t new_size);
+
+/**
+ * Get whether or not a volume group is clustered.
+ *
+ * \memberof vg_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * 1 if the VG is clustered, 0 if not
+ */
+uint64_t lvm_vg_is_clustered(vg_t vg);
+
+/**
+ * Get whether or not a volume group is exported.
+ *
+ * \memberof vg_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * 1 if the VG is exported, 0 if not
+ */
+uint64_t lvm_vg_is_exported(vg_t vg);
+
+/**
+ * Get whether or not a volume group is a partial volume group.
+ *
+ * \memberof vg_t
+ *
+ * When one or more physical volumes belonging to the volume group
+ * are missing from the system the volume group is a partial volume
+ * group.
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * 1 if the VG is PVs, 0 if not
+ */
+uint64_t lvm_vg_is_partial(vg_t vg);
+
+/**
+ * Get the current metadata sequence number of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * The metadata sequence number is incrented for each metadata change.
+ * Applications may use the sequence number to determine if any LVM objects
+ * have changed from a prior query.
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Metadata sequence number.
+ */
+uint64_t lvm_vg_get_seqno(const vg_t vg);
+
+/**
+ * Get the current uuid of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * The memory allocated for the uuid is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Copy of the uuid string.
+ */
+const char *lvm_vg_get_uuid(const vg_t vg);
+
+/**
+ * Get the current name of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * The memory allocated for the name is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Copy of the name.
+ */
+const char *lvm_vg_get_name(const vg_t vg);
+
+/**
+ * Get the current size in bytes of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Size in bytes.
+ */
+uint64_t lvm_vg_get_size(const vg_t vg);
+
+/**
+ * Get the current unallocated space in bytes of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Free size in bytes.
+ */
+uint64_t lvm_vg_get_free_size(const vg_t vg);
+
+/**
+ * Get the current extent size in bytes of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Extent size in bytes.
+ */
+uint64_t lvm_vg_get_extent_size(const vg_t vg);
+
+/**
+ * Get the current number of total extents of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Extent count.
+ */
+uint64_t lvm_vg_get_extent_count(const vg_t vg);
+
+/**
+ * Get the current number of free extents of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Free extent count.
+ */
+uint64_t lvm_vg_get_free_extent_count(const vg_t vg);
+
+/**
+ * Get the current number of physical volumes of a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Physical volume count.
+ */
+uint64_t lvm_vg_get_pv_count(const vg_t vg);
+
+/**
+ * Get the maximum number of physical volumes allowed in a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Maximum number of physical volumes allowed in a volume group.
+ */
+uint64_t lvm_vg_get_max_pv(const vg_t vg);
+
+/**
+ * Get the maximum number of logical volumes allowed in a volume group.
+ *
+ * \memberof vg_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \return
+ * Maximum number of logical volumes allowed in a volume group.
+ */
+uint64_t lvm_vg_get_max_lv(const vg_t vg);
+
+/**
+ * Return the list of volume group tags.
+ *
+ * \memberof vg_t
+ *
+ * The memory allocated for the list is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * To process the list, use the dm_list iterator functions. For example:
+ * vg_t vg;
+ * struct dm_list *tags;
+ * struct lvm_str_list *strl;
+ *
+ * tags = lvm_vg_get_tags(vg);
+ * dm_list_iterate_items(strl, tags) {
+ * tag = strl->str;
+ * // do something with tag
+ * }
+ *
+ *
+ * \return
+ * A list with entries of type struct lvm_str_list, containing the
+ * tag strings attached to volume group.
+ * If no tags are attached to the given VG, an empty list is returned
+ * (check with dm_list_empty()).
+ * If there is a problem obtaining the list of tags, NULL is returned.
+ */
+struct dm_list *lvm_vg_get_tags(const vg_t vg);
+
+/**
+ * Get the value of a VG property
+ *
+ * \memberof vg_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param name
+ * Name of property to query. See vgs man page for full list of properties
+ * that may be queried.
+ *
+ * The memory allocated for a string property value is tied to the vg_t
+ * handle and will be released when lvm_vg_close() is called.
+ *
+ * Example:
+ * lvm_property_value v;
+ * char *prop_name = "vg_mda_count";
+ *
+ * v = lvm_vg_get_property(vg, prop_name);
+ * if (!v.is_valid) {
+ * printf("Invalid property name or unable to query"
+ * "'%s', errno = %d.\n", prop_name, lvm_errno(libh));
+ * return;
+ * }
+ * if (v.is_string)
+ * printf(", value = %s\n", v.value.string);
+ * if (v.is_integer)
+ * printf(", value = %"PRIu64"\n", v.value.integer);
+ *
+ *
+ * \return
+ * lvm_property_value structure that will contain the current
+ * value of the property. Caller should check 'is_valid' flag before using
+ * the value. If 'is_valid' is not set, caller should check lvm_errno()
+ * for specific error.
+ */
+struct lvm_property_value lvm_vg_get_property(const vg_t vg, const char *name);
+
+/**
+ * Set the value of a VG property. Note that the property must be
+ * a 'settable' property, as evidenced by the 'is_settable' flag
+ * when querying the property.
+ *
+ * \memberof vg_t
+ *
+ * The memory allocated for a string property value is tied to the vg_t
+ * handle and will be released when lvm_vg_close() is called.
+ *
+ * Example (integer):
+ * lvm_property_value copies;
+ *
+ * if (lvm_vg_get_property(vg, "vg_mda_copies", &copies) < 0) {
+ * // Error - unable to query property
+ * }
+ * if (!copies.is_settable) {
+ * // Error - property not settable
+ * }
+ * copies.value.integer = 2;
+ * if (lvm_vg_set_property(vg, "vg_mda_copies", &copies) < 0) {
+ * // handle error
+ * }
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_set_property(const vg_t vg, const char *name,
+ struct lvm_property_value *value);
+
+/************************** logical volume handling *************************/
+
+/**
+ * Create a linear logical volume.
+ * This function commits the change to disk and does _not_ require calling
+ * lvm_vg_write().
+ * NOTE: The commit behavior of this function is subject to change
+ * as the API is developed.
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param name
+ * Name of logical volume to create.
+ *
+ * \param size
+ * Size of logical volume in extents.
+ *
+ * \return
+ * non-NULL handle to an LV object created, or NULL if creation fails.
+ *
+ */
+lv_t lvm_vg_create_lv_linear(vg_t vg, const char *name, uint64_t size);
+
+/**
+ * Return a list of lvseg handles for a given LV handle.
+ *
+ * \memberof lv_t
+ *
+ * \param lv
+ * Logical volume handle.
+ *
+ * \return
+ * A list of lvm_lvseg_list structures containing lvseg handles for this lv.
+ */
+struct dm_list *lvm_lv_list_lvsegs(lv_t lv);
+
+/**
+ * Lookup an LV handle in a VG by the LV name.
+ *
+ * \memberof lv_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param name
+ * Name of LV to lookup.
+ *
+ * \return
+ * non-NULL handle to the LV 'name' attached to the VG.
+ * NULL is returned if the LV name is not associated with the VG handle.
+ */
+lv_t lvm_lv_from_name(vg_t vg, const char *name);
+
+/**
+ * Lookup an LV handle in a VG by the LV uuid.
+ * The form of the uuid may be either the formatted, human-readable form,
+ * or the non-formatted form.
+ *
+ * \memberof lv_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param uuid
+ * UUID of LV to lookup.
+ *
+ * \return
+ * non-NULL handle to the LV with 'uuid' attached to the VG.
+ * NULL is returned if the LV uuid is not associated with the VG handle.
+ */
+lv_t lvm_lv_from_uuid(vg_t vg, const char *uuid);
+
+/**
+ * Activate a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * This function is the equivalent of the lvm command "lvchange -ay".
+ *
+ * NOTE: This function cannot currently handle LVs with an in-progress pvmove or
+ * lvconvert.
+ *
+ * \param lv
+ * Logical volume handle.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_lv_activate(lv_t lv);
+
+/**
+ * Deactivate a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * This function is the equivalent of the lvm command "lvchange -an".
+ *
+ * \param lv
+ * Logical volume handle.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_lv_deactivate(lv_t lv);
+
+/**
+ * Remove a logical volume from a volume group.
+ *
+ * \memberof lv_t
+ *
+ * This function commits the change to disk and does _not_ require calling
+ * lvm_vg_write().
+ * NOTE: The commit behavior of this function is subject to change
+ * as the API is developed.
+ * Currently only removing linear LVs are possible.
+ *
+ * \param lv
+ * Logical volume handle.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_vg_remove_lv(lv_t lv);
+
+/**
+ * Get the current name of a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * The memory allocated for the uuid is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * \param lv
+ * Logical volume handle.
+ *
+ * \return
+ * Copy of the uuid string.
+ */
+const char *lvm_lv_get_uuid(const lv_t lv);
+
+/**
+ * Get the current uuid of a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * The memory allocated for the name is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * \param lv
+ * Logical volume handle.
+ *
+ * \return
+ * Copy of the name.
+ */
+const char *lvm_lv_get_name(const lv_t lv);
+
+/**
+ * Get the current size in bytes of a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * \param lv
+ * Logical volume handle.
+ *
+ * \return
+ * Size in bytes.
+ */
+uint64_t lvm_lv_get_size(const lv_t lv);
+
+/**
+ * Get the value of a LV property
+ *
+ * \memberof lv_t
+ *
+ * \param lv
+ * Logical volume handle.
+ *
+ * \param name
+ * Name of property to query. See lvs man page for full list of properties
+ * that may be queried.
+ *
+ * The memory allocated for a string property value is tied to the vg_t
+ * handle and will be released when lvm_vg_close() is called.
+ *
+ * Example:
+ * lvm_property_value v;
+ * char *prop_name = "seg_count";
+ *
+ * v = lvm_lv_get_property(lv, prop_name);
+ * if (!v.is_valid) {
+ * printf("Invalid property name or unable to query"
+ * "'%s', errno = %d.\n", prop_name, lvm_errno(libh));
+ * return;
+ * }
+ * if (v.is_string)
+ * printf(", value = %s\n", v.value.string);
+ * if (v.is_integer)
+ * printf(", value = %"PRIu64"\n", v.value.integer);
+ *
+ * \return
+ * lvm_property_value structure that will contain the current
+ * value of the property. Caller should check 'is_valid' flag before using
+ * the value. If 'is_valid' is not set, caller should check lvm_errno()
+ * for specific error.
+ */
+struct lvm_property_value lvm_lv_get_property(const lv_t lv, const char *name);
+
+/**
+ * Get the value of a LV segment property
+ *
+ * \memberof lv_t
+ *
+ * \param lvseg
+ * Logical volume segment handle.
+ *
+ * \param name
+ * Name of property to query. See lvs man page for full list of properties
+ * that may be queried.
+ *
+ * The memory allocated for a string property value is tied to the vg_t
+ * handle and will be released when lvm_vg_close() is called.
+ *
+ * Example:
+ * lvm_property_value v;
+ * char *prop_name = "seg_start_pe";
+ *
+ * v = lvm_lvseg_get_property(lvseg, prop_name);
+ * if (lvm_errno(libh) || !v.is_valid) {
+ * // handle error
+ * printf("Invalid property name or unable to query"
+ * "'%s'.\n", prop_name);
+ * return;
+ * }
+ * if (v.is_string)
+ * printf(", value = %s\n", v.value.string);
+ * else
+ * printf(", value = %"PRIu64"\n", v.value.integer);
+ *
+ * \return
+ * lvm_property_value structure that will contain the current
+ * value of the property. Caller should check lvm_errno() as well
+ * as 'is_valid' flag before using the value.
+ */
+struct lvm_property_value lvm_lvseg_get_property(const lvseg_t lvseg,
+ const char *name);
+
+/**
+ * Get the current activation state of a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * \param lv
+ * Logical volume handle.
+ *
+ * \return
+ * 1 if the LV is active in the kernel, 0 if not
+ */
+uint64_t lvm_lv_is_active(const lv_t lv);
+
+/**
+ * Get the current suspended state of a logical volume.
+ *
+ * \memberof lv_t
+ *
+ * \param lv
+ * Logical volume handle.
+ *
+ * \return
+ * 1 if the LV is suspended in the kernel, 0 if not
+ */
+uint64_t lvm_lv_is_suspended(const lv_t lv);
+
+/**
+ * Add a tag to an LV.
+ *
+ * \memberof lv_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully adding a tag, use lvm_vg_write() to commit the
+ * new VG to disk. Upon failure, retry the operation or release the VG handle
+ * with lvm_vg_close().
+ *
+ * \param lv
+ * Logical volume handle.
+ *
+ * \param tag
+ * Tag to add to an LV.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_lv_add_tag(lv_t lv, const char *tag);
+
+/**
+ * Remove a tag from an LV.
+ *
+ * \memberof lv_t
+ *
+ * This function requires calling lvm_vg_write() to commit the change to disk.
+ * After successfully removing a tag, use lvm_vg_write() to commit the
+ * new VG to disk. Upon failure, retry the operation or release the VG handle
+ * with lvm_vg_close().
+ *
+ * \param lv
+ * Logical volume handle.
+ *
+ * \param tag
+ * Tag to remove from LV.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_lv_remove_tag(lv_t lv, const char *tag);
+
+/**
+ * Return the list of logical volume tags.
+ *
+ * \memberof lv_t
+ *
+ * The memory allocated for the list is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * To process the list, use the dm_list iterator functions. For example:
+ * lv_t lv;
+ * struct dm_list *tags;
+ * struct lvm_str_list *strl;
+ *
+ * tags = lvm_lv_get_tags(lv);
+ * dm_list_iterate_items(strl, tags) {
+ * tag = strl->str;
+ * // do something with tag
+ * }
+ *
+ *
+ * \return
+ * A list with entries of type struct lvm_str_list, containing the
+ * tag strings attached to volume group.
+ * If no tags are attached to the LV, an empty list is returned
+ * (check with dm_list_empty()).
+ * If there is a problem obtaining the list of tags, NULL is returned.
+ */
+struct dm_list *lvm_lv_get_tags(const lv_t lv);
+
+
+/**
+ * Resize logical volume to new_size bytes.
+ *
+ * \memberof lv_t
+ *
+ * NOTE: This function is currently not implemented.
+ *
+ * \param lv
+ * Logical volume handle.
+ *
+ * \param new_size
+ * New size in bytes.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ *
+ */
+int lvm_lv_resize(const lv_t lv, uint64_t new_size);
+
+/************************** physical volume handling ************************/
+
+/**
+ * Physical volume handling should not be needed anymore. Only physical volumes
+ * bound to a vg contain useful information. Therefore the creation,
+ * modification and the removal of orphan physical volumes is not suported.
+ */
+
+/**
+ * Get the current uuid of a physical volume.
+ *
+ * \memberof pv_t
+ *
+ * The memory allocated for the uuid is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * \param pv
+ * Physical volume handle.
+ *
+ * \return
+ * Copy of the uuid string.
+ */
+const char *lvm_pv_get_uuid(const pv_t pv);
+
+/**
+ * Get the current name of a physical volume.
+ *
+ * \memberof pv_t
+ *
+ * The memory allocated for the name is tied to the vg_t handle and will be
+ * released when lvm_vg_close() is called.
+ *
+ * \param pv
+ * Physical volume handle.
+ *
+ * \return
+ * Copy of the name.
+ */
+const char *lvm_pv_get_name(const pv_t pv);
+
+/**
+ * Get the current number of metadata areas in the physical volume.
+ *
+ * \memberof pv_t
+ *
+ * \param pv
+ * Physical volume handle.
+ *
+ * \return
+ * Number of metadata areas in the PV.
+ */
+uint64_t lvm_pv_get_mda_count(const pv_t pv);
+
+/**
+ * Get the current size in bytes of a device underlying a
+ * physical volume.
+ *
+ * \memberof pv_t
+ *
+ * \param pv
+ * Physical volume handle.
+ *
+ * \return
+ * Size in bytes.
+ */
+uint64_t lvm_pv_get_dev_size(const pv_t pv);
+
+/**
+ * Get the current size in bytes of a physical volume.
+ *
+ * \memberof pv_t
+ *
+ * \param pv
+ * Physical volume handle.
+ *
+ * \return
+ * Size in bytes.
+ */
+uint64_t lvm_pv_get_size(const pv_t pv);
+
+/**
+ * Get the current unallocated space in bytes of a physical volume.
+ *
+ * \memberof pv_t
+ *
+ * \param pv
+ * Physical volume handle.
+ *
+ * \return
+ * Free size in bytes.
+ */
+uint64_t lvm_pv_get_free(const pv_t pv);
+
+/**
+ * Get the value of a PV property
+ *
+ * \memberof pv_t
+ *
+ * \param pv
+ * Physical volume handle.
+ *
+ * \param name
+ * Name of property to query. See pvs man page for full list of properties
+ * that may be queried.
+ *
+ * The memory allocated for a string property value is tied to the vg_t handle
+ * and will be released when lvm_vg_close() is called. For "percent" values
+ * (those obtained for copy_percent and snap_percent properties), please see
+ * percent_range_t and lvm_percent_to_float().
+ *
+ * Example:
+ * lvm_property_value value;
+ * char *prop_name = "pv_mda_count";
+ *
+ * v = lvm_pv_get_property(pv, prop_name);
+ * if (!v.is_valid) {
+ * printf("Invalid property name or unable to query"
+ * "'%s', errno = %d.\n", prop_name, lvm_errno(libh));
+ * return;
+ * }
+ * if (v.is_string)
+ * printf(", value = %s\n", v.value.string);
+ * if (v.is_integer)
+ * printf(", value = %"PRIu64"\n", v.value.integer);
+ *
+ * \return
+ * lvm_property_value structure that will contain the current
+ * value of the property. Caller should check 'is_valid' flag before using
+ * the value. If 'is_valid' is not set, caller should check lvm_errno()
+ * for specific error.
+ */
+struct lvm_property_value lvm_pv_get_property(const pv_t pv, const char *name);
+
+/**
+ * Get the value of a PV segment property
+ *
+ * \memberof pv_t
+ *
+ * \param pvseg
+ * Physical volume segment handle.
+ *
+ * \param name
+ * Name of property to query. See pvs man page for full list of properties
+ * that may be queried.
+ *
+ * The memory allocated for a string property value is tied to the vg_t
+ * handle and will be released when lvm_vg_close() is called.
+ *
+ * Example:
+ * lvm_property_value v;
+ * char *prop_name = "pvseg_start";
+ *
+ * v = lvm_pvseg_get_property(pvseg, prop_name);
+ * if (lvm_errno(libh) || !v.is_valid) {
+ * // handle error
+ * printf("Invalid property name or unable to query"
+ * "'%s'.\n", prop_name);
+ * return;
+ * }
+ * if (v.is_string)
+ * printf(", value = %s\n", v.value.string);
+ * else
+ * printf(", value = %"PRIu64"\n", v.value.integer);
+ *
+ * \return
+ * lvm_property_value structure that will contain the current
+ * value of the property. Caller should check lvm_errno() as well
+ * as 'is_valid' flag before using the value.
+ */
+struct lvm_property_value lvm_pvseg_get_property(const pvseg_t pvseg,
+ const char *name);
+
+/**
+ * Return a list of pvseg handles for a given PV handle.
+ *
+ * \memberof pv_t
+ *
+ * \param pv
+ * Physical volume handle.
+ *
+ * \return
+ * A list of lvm_pvseg_list structures containing pvseg handles for this pv.
+ */
+struct dm_list *lvm_pv_list_pvsegs(pv_t pv);
+
+/**
+ * Lookup an PV handle in a VG by the PV name.
+ *
+ * \memberof pv_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param name
+ * Name of PV to lookup.
+ *
+ * \return
+ * non-NULL handle to the PV 'name' attached to the VG.
+ * NULL is returned if the PV name is not associated with the VG handle.
+ */
+pv_t lvm_pv_from_name(vg_t vg, const char *name);
+
+/**
+ * Lookup an PV handle in a VG by the PV uuid.
+ * The form of the uuid may be either the formatted, human-readable form,
+ * or the non-formatted form.
+ *
+ * \memberof pv_t
+ *
+ * \param vg
+ * VG handle obtained from lvm_vg_create() or lvm_vg_open().
+ *
+ * \param uuid
+ * UUID of PV to lookup.
+ *
+ * \return
+ * non-NULL handle to the PV with 'uuid' attached to the VG.
+ * NULL is returned if the PV uuid is not associated with the VG handle.
+ */
+pv_t lvm_pv_from_uuid(vg_t vg, const char *uuid);
+
+/**
+ * Resize physical volume to new_size bytes.
+ *
+ * \memberof pv_t
+ *
+ * NOTE: This function is currently not implemented.
+ *
+ * \param pv
+ * Physical volume handle.
+ *
+ * \param new_size
+ * New size in bytes.
+ *
+ * \return
+ * 0 (success) or -1 (failure).
+ */
+int lvm_pv_resize(const pv_t pv, uint64_t new_size);
+
+#ifndef _LVM_PERCENT_H
+
+/**
+ * This type defines a couple of special percent values. The PERCENT_0 and
+ * PERCENT_100 constants designate *exact* percentages: values are never
+ * rounded to either of these two.
+ */
+typedef enum {
+ PERCENT_0 = 0,
+ PERCENT_1 = 1000000,
+ PERCENT_100 = 100 * PERCENT_1,
+ PERCENT_INVALID = -1
+} percent_range_t;
+
+typedef int32_t percent_t;
+
+#endif
+
+/**
+ * Convert a (fixed-point) value obtained from the percent-denominated
+ * *_get_property functions into a floating-point value.
+ */
+float lvm_percent_to_float(percent_t v);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _LIB_LVM2APP_H */
--- /dev/null
+/*
+ * Copyright (C) 2008,2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "locking.h"
+#include "lvm-version.h"
+#include "metadata-exported.h"
+#include "lvm2app.h"
+
+const char *lvm_library_get_version(void)
+{
+ return LVM_VERSION;
+}
+
+lvm_t lvm_init(const char *system_dir)
+{
+ struct cmd_context *cmd;
+
+ /* FIXME: logging bound to handle
+ */
+
+ /* create context */
+ /* FIXME: split create_toolcontext */
+ /* FIXME: make all globals configurable */
+ cmd = create_toolcontext(0, system_dir);
+ if (!cmd)
+ return NULL;
+
+ if (stored_errno())
+ return (lvm_t) cmd;
+
+ /*
+ * FIXME: if an non memory error occured, return the cmd (maybe some
+ * cleanup needed).
+ */
+
+ /* initialization from lvm_run_command */
+ init_error_message_produced(0);
+
+ /* FIXME: locking_type config option needed? */
+ /* initialize locking */
+ if (!init_locking(-1, cmd, 0)) {
+ /* FIXME: use EAGAIN as error code here */
+ lvm_quit((lvm_t) cmd);
+ return NULL;
+ }
+ /*
+ * FIXME: Use cmd->cmd_line as audit trail for liblvm calls. Used in
+ * archive() call. Possible example:
+ * cmd_line = "lvm_vg_create: vg1\nlvm_vg_extend vg1 /dev/sda1\n"
+ */
+ cmd->cmd_line = "liblvm";
+
+ return (lvm_t) cmd;
+}
+
+void lvm_quit(lvm_t libh)
+{
+ destroy_toolcontext((struct cmd_context *)libh);
+}
+
+int lvm_config_reload(lvm_t libh)
+{
+ /* FIXME: re-init locking needed here? */
+ if (!refresh_toolcontext((struct cmd_context *)libh))
+ return -1;
+ return 0;
+}
+
+/*
+ * FIXME: submit a patch to document the --config option
+ */
+int lvm_config_override(lvm_t libh, const char *config_settings)
+{
+ struct cmd_context *cmd = (struct cmd_context *)libh;
+ if (override_config_tree_from_string(cmd, config_settings))
+ return -1;
+ return 0;
+}
+
+int lvm_errno(lvm_t libh)
+{
+ return stored_errno();
+}
+
+const char *lvm_errmsg(lvm_t libh)
+{
+ return stored_errmsg();
+}
+
+const char *lvm_vgname_from_pvid(lvm_t libh, const char *pvid)
+{
+ struct cmd_context *cmd = (struct cmd_context *)libh;
+ struct id id;
+
+ if (!id_read_format(&id, pvid)) {
+ log_error(INTERNAL_ERROR "Unable to convert uuid");
+ return NULL;
+ }
+ return find_vgname_from_pvid(cmd, (char *)id.uuid);
+}
+
+const char *lvm_vgname_from_device(lvm_t libh, const char *device)
+{
+ struct cmd_context *cmd = (struct cmd_context *)libh;
+ return find_vgname_from_pvname(cmd, device);
+}
--- /dev/null
+/*
+ * Copyright (C) 2008,2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "lvm-string.h"
+#include "defaults.h"
+#include "segtype.h"
+#include "locking.h"
+#include "activate.h"
+#include "lvm_misc.h"
+#include "lvm2app.h"
+
+static int _lv_check_handle(const lv_t lv, const int vg_writeable)
+{
+ if (!lv || !lv->vg || vg_read_error(lv->vg))
+ return -1;
+ if (vg_writeable && !vg_check_write_mode(lv->vg))
+ return -1;
+ return 0;
+}
+
+/* FIXME: have lib/report/report.c _disp function call lv_size()? */
+uint64_t lvm_lv_get_size(const lv_t lv)
+{
+ return SECTOR_SIZE * lv_size(lv);
+}
+
+const char *lvm_lv_get_uuid(const lv_t lv)
+{
+ return lv_uuid_dup(lv);
+}
+
+const char *lvm_lv_get_name(const lv_t lv)
+{
+ return dm_pool_strndup(lv->vg->vgmem, (const char *)lv->name,
+ NAME_LEN+1);
+}
+
+struct lvm_property_value lvm_lv_get_property(const lv_t lv, const char *name)
+{
+ return get_property(NULL, NULL, lv, NULL, NULL, name);
+}
+
+struct lvm_property_value lvm_lvseg_get_property(const lvseg_t lvseg,
+ const char *name)
+{
+ return get_property(NULL, NULL, NULL, lvseg, NULL, name);
+}
+
+uint64_t lvm_lv_is_active(const lv_t lv)
+{
+ struct lvinfo info;
+ if (lv_info(lv->vg->cmd, lv, 0, &info, 1, 0) &&
+ info.exists && info.live_table)
+ return 1;
+ return 0;
+}
+
+uint64_t lvm_lv_is_suspended(const lv_t lv)
+{
+ struct lvinfo info;
+ if (lv_info(lv->vg->cmd, lv, 0, &info, 1, 0) &&
+ info.exists && info.suspended)
+ return 1;
+ return 0;
+}
+
+int lvm_lv_add_tag(lv_t lv, const char *tag)
+{
+ if (_lv_check_handle(lv, 1))
+ return -1;
+ if (!lv_change_tag(lv, tag, 1))
+ return -1;
+ return 0;
+}
+
+
+int lvm_lv_remove_tag(lv_t lv, const char *tag)
+{
+ if (_lv_check_handle(lv, 1))
+ return -1;
+ if (!lv_change_tag(lv, tag, 0))
+ return -1;
+ return 0;
+}
+
+
+struct dm_list *lvm_lv_get_tags(const lv_t lv)
+{
+ return tag_list_copy(lv->vg->vgmem, &lv->tags);
+}
+
+/* Set defaults for non-segment specific LV parameters */
+static void _lv_set_default_params(struct lvcreate_params *lp,
+ vg_t vg, const char *lvname,
+ uint64_t extents)
+{
+ lp->zero = 1;
+ lp->major = -1;
+ lp->minor = -1;
+ lp->activation_monitoring = DEFAULT_DMEVENTD_MONITOR;
+ lp->vg_name = vg->name;
+ lp->lv_name = lvname; /* FIXME: check this for safety */
+ lp->pvh = &vg->pvs;
+
+ lp->extents = extents;
+ lp->permission = LVM_READ | LVM_WRITE;
+ lp->read_ahead = DM_READ_AHEAD_NONE;
+ lp->alloc = ALLOC_INHERIT;
+ dm_list_init(&lp->tags);
+}
+
+/* Set default for linear segment specific LV parameters */
+static void _lv_set_default_linear_params(struct cmd_context *cmd,
+ struct lvcreate_params *lp)
+{
+ lp->segtype = get_segtype_from_string(cmd, "striped");
+ lp->stripes = 1;
+ lp->stripe_size = DEFAULT_STRIPESIZE * 2;
+}
+
+/*
+ * FIXME: This function should probably not commit to disk but require calling
+ * lvm_vg_write. However, this appears to be non-trivial change until
+ * lv_create_single is refactored by segtype.
+ */
+lv_t lvm_vg_create_lv_linear(vg_t vg, const char *name, uint64_t size)
+{
+ struct lvcreate_params lp;
+ uint64_t extents;
+ struct lv_list *lvl;
+
+ if (vg_read_error(vg))
+ return NULL;
+ if (!vg_check_write_mode(vg))
+ return NULL;
+ memset(&lp, 0, sizeof(lp));
+ extents = extents_from_size(vg->cmd, size / SECTOR_SIZE,
+ vg->extent_size);
+ _lv_set_default_params(&lp, vg, name, extents);
+ _lv_set_default_linear_params(vg->cmd, &lp);
+ if (!lv_create_single(vg, &lp))
+ return NULL;
+ lvl = find_lv_in_vg(vg, name);
+ if (!lvl)
+ return NULL;
+ return (lv_t) lvl->lv;
+}
+
+/*
+ * FIXME: This function should probably not commit to disk but require calling
+ * lvm_vg_write.
+ */
+int lvm_vg_remove_lv(lv_t lv)
+{
+ if (!lv || !lv->vg || vg_read_error(lv->vg))
+ return -1;
+ if (!vg_check_write_mode(lv->vg))
+ return -1;
+ if (!lv_remove_single(lv->vg->cmd, lv, DONT_PROMPT))
+ return -1;
+ return 0;
+}
+
+int lvm_lv_activate(lv_t lv)
+{
+ if (!lv || !lv->vg || vg_read_error(lv->vg) || !lv->vg->cmd)
+ return -1;
+
+ /* FIXME: handle pvmove stuff later */
+ if (lv->status & LOCKED) {
+ log_error("Unable to activate locked LV");
+ return -1;
+ }
+
+ /* FIXME: handle lvconvert stuff later */
+ if (lv->status & CONVERTING) {
+ log_error("Unable to activate LV with in-progress lvconvert");
+ return -1;
+ }
+
+ if (lv_is_origin(lv)) {
+ log_verbose("Activating logical volume \"%s\" "
+ "exclusively", lv->name);
+ if (!activate_lv_excl(lv->vg->cmd, lv)) {
+ log_error("Activate exclusive failed.");
+ return -1;
+ }
+ } else {
+ log_verbose("Activating logical volume \"%s\"",
+ lv->name);
+ if (!activate_lv(lv->vg->cmd, lv)) {
+ log_error("Activate failed.");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int lvm_lv_deactivate(lv_t lv)
+{
+ if (!lv || !lv->vg || vg_read_error(lv->vg) || !lv->vg->cmd)
+ return -1;
+
+ log_verbose("Deactivating logical volume \"%s\"", lv->name);
+ if (!deactivate_lv(lv->vg->cmd, lv)) {
+ log_error("Deactivate failed.");
+ return -1;
+ }
+ return 0;
+}
+
+struct dm_list *lvm_lv_list_lvsegs(lv_t lv)
+{
+ struct dm_list *list;
+ lvseg_list_t *lvseg;
+ struct lv_segment *lvl;
+
+ if (dm_list_empty(&lv->segments))
+ return NULL;
+
+ if (!(list = dm_pool_zalloc(lv->vg->vgmem, sizeof(*list)))) {
+ log_errno(ENOMEM, "Memory allocation fail for dm_list.");
+ return NULL;
+ }
+ dm_list_init(list);
+
+ dm_list_iterate_items(lvl, &lv->segments) {
+ if (!(lvseg = dm_pool_zalloc(lv->vg->vgmem, sizeof(*lvseg)))) {
+ log_errno(ENOMEM,
+ "Memory allocation fail for lvm_lvseg_list.");
+ return NULL;
+ }
+ lvseg->lvseg = lvl;
+ dm_list_add(list, &lvseg->list);
+ }
+ return list;
+}
+
+lv_t lvm_lv_from_name(vg_t vg, const char *name)
+{
+ struct lv_list *lvl;
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (!strcmp(name, lvl->lv->name))
+ return lvl->lv;
+ }
+ return NULL;
+}
+
+lv_t lvm_lv_from_uuid(vg_t vg, const char *uuid)
+{
+ struct lv_list *lvl;
+ struct id id;
+
+ if (strlen(uuid) < ID_LEN) {
+ log_errno (EINVAL, "Invalid UUID string length");
+ return NULL;
+ }
+ if (strlen(uuid) >= ID_LEN) {
+ if (!id_read_format(&id, uuid)) {
+ log_errno(EINVAL, "Invalid UUID format");
+ return NULL;
+ }
+ }
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (id_equal(&vg->id, &lvl->lv->lvid.id[0]) &&
+ id_equal(&id, &lvl->lv->lvid.id[1]))
+ return lvl->lv;
+ }
+ return NULL;
+}
+int lvm_lv_resize(const lv_t lv, uint64_t new_size)
+{
+ /* FIXME: add lv resize code here */
+ log_error("NOT IMPLEMENTED YET");
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2008,2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "properties.h"
+#include "lvm_misc.h"
+#include "lvm2app.h"
+
+struct dm_list *tag_list_copy(struct dm_pool *p, struct dm_list *tag_list)
+{
+ struct dm_list *list;
+ lvm_str_list_t *lsl;
+ struct str_list *sl;
+
+ if (!(list = dm_pool_zalloc(p, sizeof(*list)))) {
+ log_errno(ENOMEM, "Memory allocation fail for dm_list.");
+ return NULL;
+ }
+ dm_list_init(list);
+
+ dm_list_iterate_items(sl, tag_list) {
+ if (!(lsl = dm_pool_zalloc(p, sizeof(*lsl)))) {
+ log_errno(ENOMEM,
+ "Memory allocation fail for lvm_lv_list.");
+ return NULL;
+ }
+ if (!(lsl->str = dm_pool_strdup(p, sl->str))) {
+ log_errno(ENOMEM,
+ "Memory allocation fail for lvm_lv_list->str.");
+ return NULL;
+ }
+ dm_list_add(list, &lsl->list);
+ }
+ return list;
+}
+
+struct lvm_property_value get_property(const pv_t pv, const vg_t vg,
+ const lv_t lv, const lvseg_t lvseg,
+ const pvseg_t pvseg, const char *name)
+{
+ struct lvm_property_type prop;
+ struct lvm_property_value v;
+
+ prop.id = name;
+ if (pv) {
+ if (!pv_get_property(pv, &prop)) {
+ v.is_valid = 0;
+ return v;
+ }
+ } else if (vg) {
+ if (!vg_get_property(vg, &prop)) {
+ v.is_valid = 0;
+ return v;
+ }
+ } else if (lv) {
+ if (!lv_get_property(lv, &prop)) {
+ v.is_valid = 0;
+ return v;
+ }
+ } else if (lvseg) {
+ if (!lvseg_get_property(lvseg, &prop)) {
+ v.is_valid = 0;
+ return v;
+ }
+ } else if (pvseg) {
+ if (!pvseg_get_property(pvseg, &prop)) {
+ v.is_valid = 0;
+ return v;
+ }
+ }
+ v.is_settable = prop.is_settable;
+ v.is_string = prop.is_string;
+ v.is_integer = prop.is_integer;
+ if (v.is_string)
+ v.value.string = prop.value.string;
+ if (v.is_integer)
+ v.value.integer = prop.value.integer;
+ v.is_valid = 1;
+ return v;
+}
+
+
+int set_property(const pv_t pv, const vg_t vg, const lv_t lv,
+ const char *name, struct lvm_property_value *v)
+{
+ struct lvm_property_type prop;
+
+ prop.id = name;
+ if (v->is_string)
+ prop.value.string = v->value.string;
+ else
+ prop.value.integer = v->value.integer;
+ if (pv) {
+ if (!pv_set_property(pv, &prop)) {
+ v->is_valid = 0;
+ return -1;
+ }
+ } else if (vg) {
+ if (!vg_set_property(vg, &prop)) {
+ v->is_valid = 0;
+ return -1;
+ }
+ } else if (lv) {
+ if (!lv_set_property(lv, &prop)) {
+ v->is_valid = 0;
+ return -1;
+ }
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2008,2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _LVM2APP_MISC_H
+#define _LVM2APP_MISC_H
+
+#include "libdevmapper.h"
+#include "lvm2app.h"
+
+struct dm_list *tag_list_copy(struct dm_pool *p, struct dm_list *tag_list);
+struct lvm_property_value get_property(const pv_t pv, const vg_t vg,
+ const lv_t lv, const lvseg_t lvseg,
+ const pvseg_t pvseg, const char *name);
+int set_property(const pv_t pv, const vg_t vg, const lv_t lv,
+ const char *name, struct lvm_property_value *value);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2008,2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "metadata.h"
+#include "lvm-string.h"
+#include "lvm_misc.h"
+#include "lvm2app.h"
+
+const char *lvm_pv_get_uuid(const pv_t pv)
+{
+ return pv_uuid_dup(pv);
+}
+
+const char *lvm_pv_get_name(const pv_t pv)
+{
+ return dm_pool_strndup(pv->vg->vgmem,
+ (const char *)pv_dev_name(pv), NAME_LEN + 1);
+}
+
+uint64_t lvm_pv_get_mda_count(const pv_t pv)
+{
+ return (uint64_t) pv_mda_count(pv);
+}
+
+uint64_t lvm_pv_get_dev_size(const pv_t pv)
+{
+ return (uint64_t) SECTOR_SIZE * pv_dev_size(pv);
+}
+
+uint64_t lvm_pv_get_size(const pv_t pv)
+{
+ return (uint64_t) SECTOR_SIZE * pv_size_field(pv);
+}
+
+uint64_t lvm_pv_get_free(const pv_t pv)
+{
+ return (uint64_t) SECTOR_SIZE * pv_free(pv);
+}
+
+struct lvm_property_value lvm_pv_get_property(const pv_t pv, const char *name)
+{
+ return get_property(pv, NULL, NULL, NULL, NULL, name);
+}
+
+struct lvm_property_value lvm_pvseg_get_property(const pvseg_t pvseg,
+ const char *name)
+{
+ return get_property(NULL, NULL, NULL, NULL, pvseg, name);
+}
+
+struct dm_list *lvm_pv_list_pvsegs(pv_t pv)
+{
+ struct dm_list *list;
+ pvseg_list_t *pvseg;
+ struct pv_segment *pvl;
+
+ if (dm_list_empty(&pv->segments))
+ return NULL;
+
+ if (!(list = dm_pool_zalloc(pv->vg->vgmem, sizeof(*list)))) {
+ log_errno(ENOMEM, "Memory allocation fail for dm_list.");
+ return NULL;
+ }
+ dm_list_init(list);
+
+ dm_list_iterate_items(pvl, &pv->segments) {
+ if (!(pvseg = dm_pool_zalloc(pv->vg->vgmem, sizeof(*pvseg)))) {
+ log_errno(ENOMEM,
+ "Memory allocation fail for lvm_pvseg_list.");
+ return NULL;
+ }
+ pvseg->pvseg = pvl;
+ dm_list_add(list, &pvseg->list);
+ }
+ return list;
+}
+
+pv_t lvm_pv_from_name(vg_t vg, const char *name)
+{
+ struct pv_list *pvl;
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (!strcmp(name, pv_dev_name(pvl->pv)))
+ return pvl->pv;
+ }
+ return NULL;
+}
+
+pv_t lvm_pv_from_uuid(vg_t vg, const char *uuid)
+{
+ struct pv_list *pvl;
+ struct id id;
+
+ if (strlen(uuid) < ID_LEN) {
+ log_errno (EINVAL, "Invalid UUID string length");
+ return NULL;
+ }
+ if (strlen(uuid) >= ID_LEN) {
+ if (!id_read_format(&id, uuid)) {
+ log_errno(EINVAL, "Invalid UUID format");
+ return NULL;
+ }
+ }
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (id_equal(&id, &pvl->pv->id))
+ return pvl->pv;
+ }
+ return NULL;
+}
+
+
+int lvm_pv_resize(const pv_t pv, uint64_t new_size)
+{
+ /* FIXME: add pv resize code here */
+ log_error("NOT IMPLEMENTED YET");
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2008,2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "toolcontext.h"
+#include "metadata.h"
+#include "archiver.h"
+#include "locking.h"
+#include "lvmcache.h"
+#include "lvm_misc.h"
+#include "lvm2app.h"
+
+int lvm_vg_add_tag(vg_t vg, const char *tag)
+{
+ if (vg_read_error(vg))
+ return -1;
+
+ if (!vg_check_write_mode(vg))
+ return -1;
+
+ if (!vg_change_tag(vg, tag, 1))
+ return -1;
+ return 0;
+}
+
+
+int lvm_vg_remove_tag(vg_t vg, const char *tag)
+{
+ if (vg_read_error(vg))
+ return -1;
+
+ if (!vg_check_write_mode(vg))
+ return -1;
+
+ if (!vg_change_tag(vg, tag, 0))
+ return -1;
+ return 0;
+}
+
+
+vg_t lvm_vg_create(lvm_t libh, const char *vg_name)
+{
+ struct volume_group *vg;
+
+ vg = vg_create((struct cmd_context *)libh, vg_name);
+ /* FIXME: error handling is still TBD */
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ return NULL;
+ }
+ vg->open_mode = 'w';
+ return (vg_t) vg;
+}
+
+int lvm_vg_extend(vg_t vg, const char *device)
+{
+ struct pvcreate_params pp;
+
+ if (vg_read_error(vg))
+ return -1;
+
+ if (!vg_check_write_mode(vg))
+ return -1;
+
+ if (!lock_vol(vg->cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+ log_error("Can't get lock for orphan PVs");
+ return -1;
+ }
+
+ pvcreate_params_set_defaults(&pp);
+ if (!vg_extend(vg, 1, (char **) &device, &pp)) {
+ unlock_vg(vg->cmd, VG_ORPHANS);
+ return -1;
+ }
+ /*
+ * FIXME: Either commit to disk, or keep holding VG_ORPHANS and
+ * release in lvm_vg_close().
+ */
+ unlock_vg(vg->cmd, VG_ORPHANS);
+ return 0;
+}
+
+int lvm_vg_reduce(vg_t vg, const char *device)
+{
+ if (vg_read_error(vg))
+ return -1;
+ if (!vg_check_write_mode(vg))
+ return -1;
+
+ if (!vg_reduce(vg, (char *)device))
+ return -1;
+ return 0;
+}
+
+int lvm_vg_set_extent_size(vg_t vg, uint32_t new_size)
+{
+ if (vg_read_error(vg))
+ return -1;
+ if (!vg_check_write_mode(vg))
+ return -1;
+
+ if (!vg_set_extent_size(vg, new_size / SECTOR_SIZE))
+ return -1;
+ return 0;
+}
+
+int lvm_vg_write(vg_t vg)
+{
+ struct pv_list *pvl;
+
+ if (vg_read_error(vg))
+ return -1;
+ if (!vg_check_write_mode(vg))
+ return -1;
+
+ if (dm_list_empty(&vg->pvs)) {
+ if (!vg_remove(vg))
+ return -1;
+ return 0;
+ }
+
+ if (! dm_list_empty(&vg->removed_pvs)) {
+ if (!lock_vol(vg->cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+ log_error("Can't get lock for orphan PVs");
+ return 0;
+ }
+ }
+
+ if (!archive(vg))
+ return -1;
+
+ /* Store VG on disk(s) */
+ if (!vg_write(vg) || !vg_commit(vg))
+ return -1;
+
+ if (! dm_list_empty(&vg->removed_pvs)) {
+ dm_list_iterate_items(pvl, &vg->removed_pvs) {
+ pv_write_orphan(vg->cmd, pvl->pv);
+ /* FIXME: do pvremove / label_remove()? */
+ }
+ dm_list_init(&vg->removed_pvs);
+ unlock_vg(vg->cmd, VG_ORPHANS);
+ }
+
+ return 0;
+}
+
+int lvm_vg_close(vg_t vg)
+{
+ if (vg_read_error(vg) == FAILED_LOCKING)
+ free_vg(vg);
+ else
+ unlock_and_free_vg(vg->cmd, vg, vg->name);
+ return 0;
+}
+
+int lvm_vg_remove(vg_t vg)
+{
+ if (vg_read_error(vg))
+ return -1;
+ if (!vg_check_write_mode(vg))
+ return -1;
+
+ if (!vg_remove_check(vg))
+ return -1;
+
+ vg_remove_pvs(vg);
+
+ return 0;
+}
+
+vg_t lvm_vg_open(lvm_t libh, const char *vgname, const char *mode,
+ uint32_t flags)
+{
+ uint32_t internal_flags = 0;
+ struct volume_group *vg;
+
+ if (!strncmp(mode, "w", 1))
+ internal_flags |= READ_FOR_UPDATE;
+ else if (strncmp(mode, "r", 1)) {
+ log_errno(EINVAL, "Invalid VG open mode");
+ return NULL;
+ }
+
+ vg = vg_read((struct cmd_context *)libh, vgname, NULL, internal_flags);
+ if (vg_read_error(vg)) {
+ /* FIXME: use log_errno either here in inside vg_read */
+ free_vg(vg);
+ return NULL;
+ }
+ /* FIXME: combine this with locking ? */
+ vg->open_mode = mode[0];
+
+ return (vg_t) vg;
+}
+
+struct dm_list *lvm_vg_list_pvs(vg_t vg)
+{
+ struct dm_list *list;
+ pv_list_t *pvs;
+ struct pv_list *pvl;
+
+ if (dm_list_empty(&vg->pvs))
+ return NULL;
+
+ if (!(list = dm_pool_zalloc(vg->vgmem, sizeof(*list)))) {
+ log_errno(ENOMEM, "Memory allocation fail for dm_list.");
+ return NULL;
+ }
+ dm_list_init(list);
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (!(pvs = dm_pool_zalloc(vg->vgmem, sizeof(*pvs)))) {
+ log_errno(ENOMEM,
+ "Memory allocation fail for lvm_pv_list.");
+ return NULL;
+ }
+ pvs->pv = pvl->pv;
+ dm_list_add(list, &pvs->list);
+ }
+ return list;
+}
+
+struct dm_list *lvm_vg_list_lvs(vg_t vg)
+{
+ struct dm_list *list;
+ lv_list_t *lvs;
+ struct lv_list *lvl;
+
+ if (dm_list_empty(&vg->lvs))
+ return NULL;
+
+ if (!(list = dm_pool_zalloc(vg->vgmem, sizeof(*list)))) {
+ log_errno(ENOMEM, "Memory allocation fail for dm_list.");
+ return NULL;
+ }
+ dm_list_init(list);
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (!(lvs = dm_pool_zalloc(vg->vgmem, sizeof(*lvs)))) {
+ log_errno(ENOMEM,
+ "Memory allocation fail for lvm_lv_list.");
+ return NULL;
+ }
+ lvs->lv = lvl->lv;
+ dm_list_add(list, &lvs->list);
+ }
+ return list;
+}
+
+struct dm_list *lvm_vg_get_tags(const vg_t vg)
+{
+ return tag_list_copy(vg->vgmem, &vg->tags);
+}
+
+uint64_t lvm_vg_get_seqno(const vg_t vg)
+{
+ return vg_seqno(vg);
+}
+
+uint64_t lvm_vg_is_clustered(const vg_t vg)
+{
+ return vg_is_clustered(vg);
+}
+
+uint64_t lvm_vg_is_exported(const vg_t vg)
+{
+ return vg_is_exported(vg);
+}
+
+uint64_t lvm_vg_is_partial(const vg_t vg)
+{
+ return (vg_missing_pv_count(vg) != 0);
+}
+
+/* FIXME: invalid handle? return INTMAX? */
+uint64_t lvm_vg_get_size(const vg_t vg)
+{
+ return SECTOR_SIZE * vg_size(vg);
+}
+
+uint64_t lvm_vg_get_free_size(const vg_t vg)
+{
+ return SECTOR_SIZE * vg_free(vg);
+}
+
+uint64_t lvm_vg_get_extent_size(const vg_t vg)
+{
+ return SECTOR_SIZE * vg_extent_size(vg);
+}
+
+uint64_t lvm_vg_get_extent_count(const vg_t vg)
+{
+ return vg_extent_count(vg);
+}
+
+uint64_t lvm_vg_get_free_extent_count(const vg_t vg)
+{
+ return vg_free_count(vg);
+}
+
+uint64_t lvm_vg_get_pv_count(const vg_t vg)
+{
+ return vg_pv_count(vg);
+}
+
+uint64_t lvm_vg_get_max_pv(const vg_t vg)
+{
+ return vg_max_pv(vg);
+}
+
+uint64_t lvm_vg_get_max_lv(const vg_t vg)
+{
+ return vg_max_lv(vg);
+}
+
+const char *lvm_vg_get_uuid(const vg_t vg)
+{
+ return vg_uuid_dup(vg);
+}
+
+const char *lvm_vg_get_name(const vg_t vg)
+{
+ return dm_pool_strndup(vg->vgmem, (const char *)vg->name, NAME_LEN+1);
+}
+
+
+struct lvm_property_value lvm_vg_get_property(const vg_t vg, const char *name)
+{
+ return get_property(NULL, vg, NULL, NULL, NULL, name);
+}
+
+int lvm_vg_set_property(const vg_t vg, const char *name,
+ struct lvm_property_value *value)
+{
+ return set_property(NULL, vg, NULL, name, value);
+}
+
+struct dm_list *lvm_list_vg_names(lvm_t libh)
+{
+ return get_vgnames((struct cmd_context *)libh, 0);
+}
+
+struct dm_list *lvm_list_vg_uuids(lvm_t libh)
+{
+ return get_vgids((struct cmd_context *)libh, 0);
+}
+
+/*
+ * FIXME: Elaborate on when to use, side-effects, .cache file, etc
+ */
+int lvm_scan(lvm_t libh)
+{
+ if (!lvmcache_label_scan((struct cmd_context *)libh, 2))
+ return -1;
+ return 0;
+}
--- /dev/null
+# @configure_input@
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+SHELL = /bin/sh
+
+@SET_MAKE@
+
+CC ?= @CC@
+RANLIB = @RANLIB@
+INSTALL = @INSTALL@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+LCOV = @LCOV@
+GENHTML = @GENHTML@
+LN_S = @LN_S@
+SED = @SED@
+CFLOW_CMD = @CFLOW_CMD@
+AWK = @AWK@
+
+LIBS = @LIBS@
+# Extra libraries always linked with static binaries
+STATIC_LIBS = $(SELINUX_LIBS) $(UDEV_LIBS)
+DEFS += @DEFS@
+CFLAGS += @CFLAGS@
+CLDFLAGS += @CLDFLAGS@
+LDDEPS += @LDDEPS@
+LDFLAGS += @LDFLAGS@
+LIB_SUFFIX = @LIB_SUFFIX@
+LVMINTERNAL_LIBS = -llvm-internal $(DL_LIBS)
+DL_LIBS = @DL_LIBS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+READLINE_LIBS = @READLINE_LIBS@
+SELINUX_LIBS = @SELINUX_LIBS@
+UDEV_LIBS = @UDEV_LIBS@
+TESTING = @TESTING@
+
+# Setup directory variables
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+udev_prefix = @udev_prefix@
+bindir = $(DESTDIR)@bindir@
+confdir = $(DESTDIR)@CONFDIR@/lvm
+includedir = $(DESTDIR)@includedir@
+libdir = $(DESTDIR)@libdir@
+usrlibdir = $(DESTDIR)@usrlibdir@
+sbindir = $(DESTDIR)@sbindir@
+usrsbindir = $(DESTDIR)@usrsbindir@
+datarootdir = $(DESTDIR)@datarootdir@
+infodir = $(datarootdir)/info
+mandir = $(datarootdir)/man
+localedir = $(DESTDIR)@LOCALEDIR@
+staticdir = $(DESTDIR)@STATICDIR@
+udevdir = $(DESTDIR)@udevdir@
+pkgconfigdir = $(usrlibdir)/pkgconfig
+initdir = $(DESTDIR)@sysconfdir@/rc.d/init.d
+ocf_scriptdir = $(DESTDIR)@prefix@/usr/lib/ocf/resource.d/lvm2
+
+USRLIB_RELPATH = $(shell echo $(abspath $(usrlibdir) $(libdir)) | \
+ $(AWK) -f $(top_srcdir)/scripts/relpath.awk)
+
+DEFAULT_SYS_DIR = @DEFAULT_SYS_DIR@
+DEFAULT_ARCHIVE_DIR = $(DEFAULT_SYS_DIR)/@DEFAULT_ARCHIVE_SUBDIR@
+DEFAULT_BACKUP_DIR = $(DEFAULT_SYS_DIR)/@DEFAULT_BACKUP_SUBDIR@
+DEFAULT_CACHE_DIR = $(DEFAULT_SYS_DIR)/@DEFAULT_CACHE_SUBDIR@
+DEFAULT_LOCK_DIR = @DEFAULT_LOCK_DIR@
+DEFAULT_RUN_DIR = @DEFAULT_RUN_DIR@
+
+# Setup vpath search paths for some suffixes
+vpath %.c $(srcdir)
+vpath %.in $(srcdir)
+vpath %.po $(srcdir)
+vpath %.exported_symbols $(srcdir)
+
+interface = @interface@
+interfacebuilddir = $(top_builddir)/libdm/$(interface)
+
+# The number of jobs to run, if blank, defaults to the make standard
+ifndef MAKEFLAGS
+MAKEFLAGS = @JOBS@
+endif
+
+# Handle installation of files
+ifeq ("@WRITE_INSTALL@", "yes")
+# leaving defaults
+M_INSTALL_SCRIPT =
+M_INSTALL_DATA = -m 644
+else
+M_INSTALL_PROGRAM = -m 555
+M_INSTALL_DATA = -m 444
+endif
+INSTALL_PROGRAM = $(INSTALL) $(M_INSTALL_PROGRAM) $(STRIP)
+INSTALL_DATA = $(INSTALL) -p $(M_INSTALL_DATA)
+INSTALL_WDATA = $(INSTALL) -p -m 644
+
+INSTALL_DIR = $(INSTALL) -m 0755 -d
+INSTALL_ROOT_DIR = $(INSTALL) -m 0700 -d
+INSTALL_ROOT_DATA = $(INSTALL) -m 0600
+INSTALL_SCRIPT = $(INSTALL) -p $(M_INSTALL_PROGRAM)
+
+.SUFFIXES: .c .d .o .so .a .po .pot .mo .dylib
+
+CFLAGS += -fPIC -Wall -Wundef -Wshadow -Wcast-align -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Winline -Wmissing-noreturn -Wformat-security -Wredundant-decls
+
+#CFLAGS += -W -Wconversion -Wpointer-arith -Wbad-function-cast -Wcast-qual
+#CFLAGS += -pedantic -std=gnu99
+#CFLAGS += -DDEBUG_CRC32
+
+CFLAGS += @COPTIMISE_FLAG@
+
+ifeq ("@DEBUG@", "yes")
+ CFLAGS += -g -fno-omit-frame-pointer
+ DEFS += -DDEBUG
+ # memory debugging is not thread-safe yet
+ ifneq ("@DMEVENTD@", "yes")
+ DEFS += -DDEBUG_MEM
+ endif
+endif
+
+ifeq ("@INTL@", "yes")
+ DEFS += -DINTL_PACKAGE=\"@INTL_PACKAGE@\" -DLOCALEDIR=\"@LOCALEDIR@\"
+endif
+
+LDFLAGS += -L$(top_builddir)/libdm -L$(top_builddir)/lib
+CLDFLAGS += -L$(top_builddir)/libdm -L$(top_builddir)/lib
+
+ifeq ("@DMEVENTD@", "yes")
+ LDFLAGS += -L$(top_builddir)/daemons/dmeventd
+ CLDFLAGS += -L$(top_builddir)/daemons/dmeventd
+endif
+
+ifeq ("@DM_COMPAT@", "yes")
+ DEFS += -DDM_COMPAT
+endif
+
+ifeq ("@DM_IOCTLS@", "yes")
+ DEFS += -DDM_IOCTLS
+endif
+
+#DEFS += -DDEBUG_POOL
+#DEFS += -DBOUNDS_CHECK
+
+#CFLAGS += -pg
+#LDFLAGS += -pg
+
+STRIP=
+#STRIP = -s
+
+LVM_VERSION := $(shell cat $(top_srcdir)/VERSION)
+
+LIB_VERSION_LVM := $(shell $(AWK) -F '.' '{printf "%s.%s",$$1,$$2}' $(top_srcdir)/VERSION)
+
+LIB_VERSION_DM := $(shell $(AWK) -F '.' '{printf "%s.%s",$$1,$$2}' $(top_srcdir)/VERSION_DM)
+
+LIB_VERSION_APP := $(shell $(AWK) -F '[(). ]' '{printf "%s.%s",$$1,$$4}' $(top_srcdir)/VERSION)
+
+INCLUDES += -I. -I$(top_builddir)/include
+
+INC_LNS = $(top_builddir)/include/.symlinks_created
+
+DEPS = $(top_builddir)/make.tmpl $(top_srcdir)/VERSION \
+ $(top_builddir)/Makefile $(INC_LNS)
+
+OBJECTS = $(SOURCES:%.c=%.o)
+POTFILES = $(SOURCES:%.c=%.pot)
+
+.PHONY: all pofile distclean clean cleandir cflow device-mapper
+.PHONY: install install_cluster install_device-mapper install_lvm2
+.PHONY: install_lib_shared install_dm_plugin install_lvm2_plugin
+.PHONY: install_ocf
+.PHONY: $(SUBDIRS) $(SUBDIRS.install) $(SUBDIRS.clean) $(SUBDIRS.distclean)
+.PHONY: $(SUBDIRS.pofile) $(SUBDIRS.install_cluster) $(SUBDIRS.cflow)
+.PHONY: $(SUBDIRS.device-mapper) $(SUBDIRS.install-device-mapper)
+
+SUBDIRS.device-mapper := $(SUBDIRS:=.device-mapper)
+SUBDIRS.install := $(SUBDIRS:=.install)
+SUBDIRS.install_cluster := $(SUBDIRS:=.install_cluster)
+SUBDIRS.install_device-mapper := $(SUBDIRS:=.install_device-mapper)
+SUBDIRS.install_lvm2 := $(SUBDIRS:=.install_lvm2)
+SUBDIRS.install_ocf := $(SUBDIRS:=.install_ocf)
+SUBDIRS.pofile := $(SUBDIRS:=.pofile)
+SUBDIRS.cflow := $(SUBDIRS:=.cflow)
+SUBDIRS.clean := $(SUBDIRS:=.clean)
+SUBDIRS.distclean := $(SUBDIRS:=.distclean)
+
+TARGETS += $(LIB_SHARED) $(LIB_STATIC)
+
+all: $(SUBDIRS) $(TARGETS)
+
+install: all $(SUBDIRS.install)
+install_cluster: all $(SUBDIRS.install_cluster)
+install_device-mapper: $(SUBDIRS.install_device-mapper)
+install_lvm2: $(SUBDIRS.install_lvm2)
+install_ocf: $(SUBDIRS.install_ocf)
+cflow: $(SUBDIRS.cflow)
+
+$(SUBDIRS): $(SUBDIRS.device-mapper)
+ $(MAKE) -C $@
+
+$(SUBDIRS.device-mapper):
+ $(MAKE) -C $(@:.device-mapper=) device-mapper
+
+$(SUBDIRS.install): $(SUBDIRS)
+ $(MAKE) -C $(@:.install=) install
+
+$(SUBDIRS.install_cluster): $(SUBDIRS)
+ $(MAKE) -C $(@:.install_cluster=) install_cluster
+
+$(SUBDIRS.install_device-mapper): device-mapper
+ $(MAKE) -C $(@:.install_device-mapper=) install_device-mapper
+
+$(SUBDIRS.install_lvm2): $(SUBDIRS)
+ $(MAKE) -C $(@:.install_lvm2=) install_lvm2
+
+$(SUBDIRS.install_ocf):
+ $(MAKE) -C $(@:.install_ocf=) install_ocf
+
+$(SUBDIRS.clean):
+ -$(MAKE) -C $(@:.clean=) clean
+
+$(SUBDIRS.distclean):
+ -$(MAKE) -C $(@:.distclean=) distclean
+
+$(SUBDIRS.cflow):
+ $(MAKE) -C $(@:.cflow=) cflow
+
+ifeq ("@INTL@", "yes")
+pofile: $(SUBDIRS.pofile) $(POTFILES)
+
+$(SUBDIRS.pofile):
+ $(MAKE) -C $(@:.pofile=) pofile
+endif
+
+ifneq ("$(CFLOW_LIST_TARGET)", "")
+CLEAN_CFLOW += $(CFLOW_LIST_TARGET)
+$(CFLOW_LIST_TARGET): $(CFLOW_LIST)
+ echo "CFLOW_SOURCES += $(addprefix \
+ \$$(top_srcdir)$(subst $(top_srcdir),,$(srcdir))/, $(CFLOW_LIST))" > $@
+cflow: $(CFLOW_LIST_TARGET)
+endif
+
+ifneq ("$(CFLOW_TARGET)", "")
+CLEAN_CFLOW += \
+ $(CFLOW_TARGET).cflow \
+ $(CFLOW_TARGET).xref \
+ $(CFLOW_TARGET).tree \
+ $(CFLOW_TARGET).rtree \
+ $(CFLOW_TARGET).rxref
+
+ifneq ("$(CFLOW_CMD)", "")
+CFLOW_FLAGS +=\
+ --cpp="$(CC) -E" \
+ --symbol _ISbit:wrapper \
+ --symbol __attribute__:wrapper \
+ --symbol __const__:wrapper \
+ --symbol __const:type \
+ --symbol __restrict:type \
+ --symbol __extension__:wrapper \
+ --symbol __nonnull:wrapper \
+ --symbol __nothrow__:wrapper \
+ --symbol __pure__:wrapper \
+ --symbol __REDIRECT:wrapper \
+ --symbol __REDIRECT_NTH:wrapper \
+ --symbol __wur:wrapper \
+ -I$(top_srcdir)/libdm \
+ -I$(top_srcdir)/libdm/ioctl \
+ -I$(top_srcdir)/daemons/dmeventd/plugins/lvm2/ \
+ $(INCLUDES) $(DEFS)
+
+$(CFLOW_TARGET).cflow: $(CFLOW_SOURCES)
+ $(CFLOW_CMD) -o$@ $(CFLOW_FLAGS) $(CFLOW_SOURCES)
+$(CFLOW_TARGET).rxref: $(CFLOW_SOURCES)
+ $(CFLOW_CMD) -o$@ $(CFLOW_FLAGS) -r --omit-arguments $(CFLOW_SOURCES)
+$(CFLOW_TARGET).tree: $(CFLOW_SOURCES)
+ $(CFLOW_CMD) -o$@ $(CFLOW_FLAGS) --omit-arguments -T -b $(CFLOW_SOURCES)
+$(CFLOW_TARGET).xref: $(CFLOW_SOURCES)
+ $(CFLOW_CMD) -o$@ $(CFLOW_FLAGS) --omit-arguments -x $(CFLOW_SOURCES)
+#$(CFLOW_TARGET).rtree: $(CFLOW_SOURCES)
+# $(CFLOW_CMD) -o$@ $(CFLOW_FLAGS) -r --omit-arguments -T -b $(CFLOW_SOURCES)
+cflow: $(CFLOW_TARGET).cflow $(CFLOW_TARGET).tree $(CFLOW_TARGET).rxref $(CFLOW_TARGET).xref
+#$(CFLOW_TARGET).rtree
+endif
+endif
+
+$(TARGETS): $(OBJECTS)
+
+%.o: %.c
+ $(CC) -c $(INCLUDES) $(DEFS) $(CFLAGS) $< -o $@
+
+%.pot: %.c Makefile
+ $(CC) -E $(INCLUDES) -include $(top_srcdir)/include/pogen.h \
+ $(DEFS) $(CFLAGS) $< > $@
+
+%.so: %.o
+ $(CC) -c $(INCLUDES) $(DEFS) $(CFLAGS) $(CLDFLAGS) $< $(LIBS) -o $@
+
+ifneq (,$(LIB_SHARED))
+
+TARGETS += $(LIB_SHARED).$(LIB_VERSION)
+$(LIB_SHARED).$(LIB_VERSION): $(OBJECTS) $(LDDEPS)
+ifeq ("@LIB_SUFFIX@","so")
+ $(CC) -shared -Wl,-soname,$(notdir $@) \
+ $(CFLAGS) $(CLDFLAGS) $(OBJECTS) $(LIBS) -o $@
+endif
+ifeq ("@LIB_SUFFIX@","dylib")
+ $(CC) -dynamiclib -dylib_current_version,$(LIB_VERSION) \
+ $(CFLAGS) $(CLDFLAGS) $(OBJECTS) $(LIBS) -o $@
+endif
+
+$(LIB_SHARED): $(LIB_SHARED).$(LIB_VERSION)
+ $(LN_S) -f $(<F) $@
+
+install_lib_shared: $(LIB_SHARED)
+ $(INSTALL_PROGRAM) -D $< $(libdir)/$(<F).$(LIB_VERSION)
+ $(INSTALL_DIR) $(usrlibdir)
+ $(LN_S) -f $(USRLIB_RELPATH)$(<F).$(LIB_VERSION) $(usrlibdir)/$(<F)
+
+# FIXME: plugins are installed to subdirs
+# and for compatibility links in libdir are created
+# when the code is fixed links could be removed.
+install_dm_plugin: $(LIB_SHARED)
+ $(INSTALL_PROGRAM) -D $< $(libdir)/device-mapper/$(<F)
+ $(LN_S) -f device-mapper/$(<F) $(libdir)/$(<F)
+
+install_lvm2_plugin: $(LIB_SHARED)
+ $(INSTALL_PROGRAM) -D $< $(libdir)/lvm2/$(<F)
+ $(LN_S) -f lvm2/$(<F) $(libdir)/$(<F)
+ $(LN_S) -f $(<F) $(libdir)/$(<F).$(LIB_VERSION)
+endif
+
+$(LIB_STATIC): $(OBJECTS)
+ $(RM) $@
+ $(AR) rs $@ $(OBJECTS)
+
+%.d: %.c
+ $(MKDIR_P) $(dir $@); \
+ set -e; \
+ FILE=`echo $@ | sed 's/\\//\\\\\\//g;s/\\.d//g'`; \
+ DEPS=`echo $(DEPS) | sed -e 's/\\//\\\\\\//g'`; \
+ $(CC) -MM $(INCLUDES) $(DEFS) $(CFLAGS) -o $@ $<; \
+ sed -i "s/\(.*\)\.o[ :]*/$$FILE.o $$FILE.d $$FILE.pot: $$DEPS /g" $@; \
+ [ -s $@ ] || $(RM) $@
+
+%.mo: %.po
+ $(MSGFMT) -o $@ $<
+
+cleandir:
+ $(RM) $(OBJECTS) $(TARGETS) $(CLEAN_TARGETS) $(CLEAN_CFLOW) $(LDDEPS) \
+ $(POTFILES) $(SOURCES:%.c=%.d) $(SOURCES:%.c=%.gcno) $(SOURCES:%.c=%.gcda) \
+ $(SOURCES2:%.c=%.o) $(SOURCES2:%.c=%.d) $(SOURCES2:%.c=%.gcno) $(SOURCES2:%.c=%.gcda)
+
+clean: $(SUBDIRS.clean) cleandir
+
+distclean: cleandir $(SUBDIRS.distclean)
+ test -z "$(DISTCLEAN_DIRS)" || $(RM) -r $(DISTCLEAN_DIRS)
+ $(RM) $(DISTCLEAN_TARGETS) Makefile core
+
+.exported_symbols_generated: $(EXPORTED_HEADER) .exported_symbols
+ set -e; \
+ ( cat $(srcdir)/.exported_symbols; \
+ if test x$(EXPORTED_HEADER) != x; then \
+ $(CC) -E -P $(INCLUDES) $(DEFS) $(CFLAGS) $(EXPORTED_HEADER) | \
+ $(SED) -ne "/^typedef|}/!s/.*[ \*]\(\$(EXPORTED_FN_PREFIX)_[a-z0-9_]*\)(.*/\1/p"; \
+ fi \
+ ) > $@
+
+.export.sym: .exported_symbols_generated
+ set -e; (echo "Base {"; echo " global:"; \
+ sed "s/^/ /;s/$$/;/" < $<; \
+ echo " local:"; echo " *;"; echo "};") > $@
+
+ifeq (,$(findstring $(MAKECMDGOALS),cscope.out cflow clean distclean lcov))
+ ifdef SOURCES
+ -include $(SOURCES:.c=.d)
+ endif
+ ifdef SOURCES2
+ -include $(SOURCES2:.c=.d)
+ endif
+endif
--- /dev/null
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+ifeq ("@FSADM@", "yes")
+FSADMMAN = fsadm.8
+else
+FSADMMAN =
+endif
+
+ifeq ("@DMEVENTD@", "yes")
+DMEVENTDMAN = dmeventd.8
+else
+DMEVENTDMAN =
+endif
+
+MAN5=lvm.conf.5
+MAN8=lvchange.8 lvconvert.8 lvcreate.8 lvdisplay.8 lvextend.8 lvm.8 \
+ lvmchange.8 lvmconf.8 lvmdiskscan.8 lvmdump.8 lvmsadc.8 lvmsar.8 \
+ lvreduce.8 lvremove.8 lvrename.8 lvresize.8 lvs.8 \
+ lvscan.8 pvchange.8 pvck.8 pvcreate.8 pvdisplay.8 pvmove.8 pvremove.8 \
+ pvresize.8 pvs.8 pvscan.8 vgcfgbackup.8 vgcfgrestore.8 vgchange.8 \
+ vgck.8 vgcreate.8 vgconvert.8 vgdisplay.8 vgexport.8 vgextend.8 \
+ vgimport.8 vgimportclone.8 vgmerge.8 vgmknodes.8 vgreduce.8 vgremove.8 \
+ vgrename.8 vgs.8 vgscan.8 vgsplit.8 $(FSADMMAN)
+
+ifneq ("@CLVMD@", "none")
+ MAN8CLUSTER=clvmd.8
+else
+ MAN8CLUSTER=
+endif
+ifeq ("@BUILD_CMIRRORD@", "yes")
+ MAN8CLUSTER+=cmirrord.8
+endif
+
+MAN8DM=dmsetup.8 $(DMEVENTDMAN)
+MAN5DIR=$(mandir)/man5
+MAN8DIR=$(mandir)/man8
+
+CLEAN_TARGETS=$(MAN5) $(MAN8) $(MAN8CLUSTER) $(FSADMMAN) $(DMEVENTDMAN) $(MAN8DM)
+DISTCLEAN_TARGETS=fsadm.8 clvmd.8 cmirrord.8 dmeventd.8
+
+include $(top_builddir)/make.tmpl
+
+ifneq ("@CLVMD@", "none")
+ install: install_cluster
+endif
+
+all: man
+
+.PHONY: man install_man5 install_man8
+
+device-mapper: $(MAN8DM)
+
+man: $(MAN5) $(MAN8) $(MAN8CLUSTER)
+
+$(MAN5) $(MAN8) $(MAN8CLUSTER): Makefile
+
+%: %.in
+ @case "$@" in \
+ */*) ;; \
+ *) echo "Creating $@" ; $(SED) -e "s+#VERSION#+$(LVM_VERSION)+;s+#DEFAULT_SYS_DIR#+$(DEFAULT_SYS_DIR)+;s+#DEFAULT_ARCHIVE_DIR#+$(DEFAULT_ARCHIVE_DIR)+;s+#DEFAULT_BACKUP_DIR#+$(DEFAULT_BACKUP_DIR)+;s+#DEFAULT_CACHE_DIR#+$(DEFAULT_CACHE_DIR)+;s+#DEFAULT_LOCK_DIR#+$(DEFAULT_LOCK_DIR)+" $< > $@ ;; \
+ esac
+
+install_man5: $(MAN5)
+ $(INSTALL) -d $(MAN5DIR)
+ $(INSTALL_DATA) $(MAN5) $(MAN5DIR)/
+
+install_man8: $(MAN8)
+ $(INSTALL) -d $(MAN8DIR)
+ $(INSTALL_DATA) $(MAN8) $(MAN8DIR)/
+
+install_lvm2: install_man5 install_man8
+
+install_cluster: $(MAN8CLUSTER)
+ $(INSTALL) -d $(MAN8DIR)
+ $(INSTALL_DATA) $(MAN8CLUSTER) $(MAN8DIR)/
+
+install_device-mapper: $(MAN8DM)
+ $(INSTALL) -d $(MAN8DIR)
+ $(INSTALL_DATA) $(MAN8DM) $(MAN8DIR)/
+
+install: install_lvm2 install_device-mapper
--- /dev/null
+.TH CLVMD 8 "LVM TOOLS #VERSION#" "Red Hat Inc" \" -*- nroff -*-
+.SH NAME
+clvmd \- cluster LVM daemon
+.SH SYNOPSIS
+.B clvmd
+[\-d [<value>]] [\-C] [\-h]
+[\-R]
+[\-S]
+[\-t <timeout>]
+[\-T <start timeout>]
+[\-V]
+.SH DESCRIPTION
+clvmd is the daemon that distributes LVM metadata updates around a cluster.
+It must be running on all nodes in the cluster and will give an error
+if a node in the cluster does not have this daemon running.
+.SH OPTIONS
+.TP
+.I \-d [<value>]
+Enable debug logging. Value can be 0, 1 or 2.
+.br
+0 disables debug logging in a running clvmd
+.br
+1 sends debug logs to stderr (clvmd will not fork in this case)
+.br
+2 sends debug logs to syslog
+.br
+If
+.B -d
+is specified without a value then 1 is assumed if you are starting a
+new clvmd, 2 if you are enabling debug in a running clvmd.
+.TP
+.I \-C
+Only valid if
+.B -d
+is also specified. Tells all clvmds in a cluster to enable/disable debug logging.
+Without this switch, only the local clvmd will change its debug level to that
+given with
+.B -d.
+.br
+This does not work correctly if specified on the command-line that starts clvmd.
+If you want to start clvmd
+.B and
+enable cluster-wide logging then the command needs to be issued twice, eg:
+.br
+clvmd
+.br
+clvmd -d2
+.br
+.TP
+.I \-t <timeout>
+Specifies the timeout for commands to run around the cluster. This should not
+be so small that commands with many disk updates to do will fail, so you
+may need to increase this on systems with very large disk farms.
+The default is 30 seconds.
+.TP
+.I \-T <start timeout>
+Specifies the timeout for clvmd daemon startup. If the daemon does not report
+that it has started up within this time then the parent command will exit with
+status of 5. This does NOT mean that clvmd has not started! What it means is
+that the startup of clvmd has been delayed for some reason; the most likely
+cause of this is an inquorate cluster though it could be due to locking
+latencies on a cluster with large numbers of logical volumes. If you get the
+return code of 5 it is usually not necessary to restart clvmd - it will start
+as soon as that blockage has cleared. This flag is to allow startup scripts
+to exit in a timely fashion even if the cluster is stalled for some reason.
+.br
+The default is 0 (no timeout) and the value is in seconds. Don't set this too
+small or you will experience spurious errors. 10 or 20 seconds might be
+sensible.
+.br
+This timeout will be ignored if you start clvmd with the -d switch.
+.TP
+.I \-R
+Tells all the running clvmds in the cluster to reload their device cache and
+re-read the lvm configuration file. This command should be run whenever the
+devices on a cluster system are changed.
+.TP
+.I \-S
+Tells the running clvmd to exit and reexecute itself, for example at the
+end of a package upgrade. The new instance is instructed to reacquire
+any locks in the same state as they were previously held. (Alternative
+methods of restarting the daemon have the side effect of changing
+exclusive LV locks into shared locks.)
+.TP
+.I \-I
+Selects the cluster manager to use for locking and internal communications,
+the available managers will be listed as part of the 'clvmd -h' output.
+clvmd will use the first cluster manager that succeeds, and it checks them
+in the order cman,gulm,corosync,openais. As it is quite possible to have
+(eg) corosync and cman available on the same system you might have to
+manually specify this option to override the search.
+.TP
+.I \-V
+Display the version of the cluster LVM daemon.
+.SH SEE ALSO
+.BR lvm (8)
--- /dev/null
+.TH CMIRRORD 8 "LVM TOOLS #VERSION#" "Red Hat Inc" \" -*- nroff -*-
+.SH NAME
+cmirrord \- cluster mirror log daemon
+
+.SH SYNOPSIS
+.B cmirrord
+
+.SH DESCRIPTION
+cmirrord is the daemon that tracks mirror log information in a cluster.
+It is specific to device-mapper based mirrors (and by extension, LVM
+cluster mirrors). Cluster mirrors are not possible without this daemon
+running.
+
+This daemon relies on the cluster infrastructure provided by the
+Cluster MANager (CMAN), which must be set up and running in order for
+cmirrord to function. (The cluster infrastructure is also required for
+clvmd.)
+
+Output is logged via syslog. The USR1 signal can be issued to cmirrord
+to gather current status information for debugging purposes.
+
+Once started, cmirrord will run until it is shutdown via INT signal. If
+there are still active cluster mirrors, however, the signal will be
+ignored. Active cluster mirrors should be shutdown before stopping the
+cluster mirror log daemon.
+
+.SH SEE ALSO
+.BR lvm (8)
+.BR clvmd (8)
+.BR cluster.conf (5)
\ No newline at end of file
--- /dev/null
+.TH DMEVENTD 8 "DM TOOLS #VERSION#" "Red Hat Inc" \" -*- nroff -*-
+.SH NAME
+dmeventd \- Device-mapper event daemon
+.SH SYNOPSIS
+.B dmeventd
+[\-d]
+[\-f]
+[\-h]
+[\-V]
+[\-?]
+.SH DESCRIPTION
+dmeventd is the event monitoring daemon for device-mapper devices.
+Library plugins can register and carry out actions triggered when
+particular events occur.
+.SH
+LVM PLUGINS
+.TP
+.I Mirror
+Attempts to handle device failure automatically. See \fBlvm.conf\fP(5).
+.TP
+.I Snapshot
+Monitors how full a snapshot is becoming and emits a warning to
+syslog when it exceeds 80% full.
+The warning is repeated when 85%, 90% and 95% of the snapshot is filled.
+See \fBlvm.conf\fP(5).
+.SH OPTIONS
+.TP
+.I \-d
+Repeat from 1 to 3 times (-d, -dd, -ddd) to increase the detail of
+debug messages sent to syslog.
+Each extra d adds more debugging information.
+.TP
+.I \-f
+Don't fork, run in the foreground.
+.TP
+.I \-h, \-?
+Show help information.
+.TP
+.I \-V
+Show version of dmeventd.
+
+.SH SEE ALSO
+.BR lvm (8),
+.BR lvm.conf (5)
--- /dev/null
+.TH DMSETUP 8 "Apr 06 2006" "Linux" "MAINTENANCE COMMANDS"
+.SH NAME
+dmsetup \- low level logical volume management
+.SH SYNOPSIS
+.ad l
+.B dmsetup help
+.I [-c|-C|--columns]
+.br
+.B dmsetup create
+.I device_name [-u uuid] [--notable | --table <table> | table_file]
+.br
+.B dmsetup remove
+.I [-f|--force] device_name
+.br
+.B dmsetup remove_all
+.I [-f|--force]
+.br
+.B dmsetup suspend
+.I [--nolockfs] [--noflush] device_name
+.br
+.B dmsetup resume
+.I device_name
+.br
+.B dmsetup load
+.I device_name [--table <table> | table_file]
+.br
+.B dmsetup clear
+.I device_name
+.br
+.B dmsetup reload
+.I device_name [--table <table> | table_file]
+.br
+.B dmsetup rename
+.I device_name new_name
+.br
+.B dmsetup rename
+.I device_name --setuuid uuid
+.br
+.B dmsetup message
+.I device_name sector message
+.br
+.B dmsetup ls
+.I [--target target_type] [--exec command] [--tree [-o options]]
+.br
+.B dmsetup info
+.I [device_name]
+.br
+.B dmsetup info -c|-C|--columns
+.I [--noheadings] [--separator separator] [-o fields] [-O|--sort sort_fields]
+.I [device_name]
+.br
+.B dmsetup deps
+.I [device_name]
+.br
+.B dmsetup status
+.I [--target target_type]
+.I [device_name]
+.br
+.B dmsetup table
+.I [--target target_type] [--showkeys]
+.I [device_name]
+.br
+.B dmsetup wait
+.I device_name
+.I [event_nr]
+.br
+.B dmsetup mknodes
+.I [device_name]
+.br
+.B dmsetup udevcreatecookie
+.br
+.B dmsetup udevreleasecookie
+.I [cookie]
+.br
+.B dmsetup udevflags
+.I cookie
+.br
+.B dmsetup udevcomplete
+.I cookie
+.br
+.B dmsetup udevcomplete_all
+.br
+.B dmsetup udevcookies
+.br
+.B dmsetup targets
+.br
+.B dmsetup version
+.br
+.B dmsetup setgeometry
+.I device_name cyl head sect start
+.br
+.B dmsetup splitname
+.I device_name
+.I [subsystem]
+.br
+
+.B devmap_name
+.I major minor
+.br
+.B devmap_name
+.I major:minor
+.ad b
+.SH DESCRIPTION
+dmsetup manages logical devices that use the device-mapper driver.
+Devices are created by loading a table that specifies a target for
+each sector (512 bytes) in the logical device.
+
+The first argument to dmsetup is a command.
+The second argument is the logical device name or uuid.
+
+Invoking the command as \fBdevmap_name\fP is equivalent to
+.br
+\fBdmsetup info -c --noheadings -j \fImajor\fB -m \fIminor\fP.
+.SH OPTIONS
+.IP \fB-c|-C|--columns
+.br
+Display output in columns rather than as Field: Value lines.
+.IP \fB-h|--help
+.br
+Outputs a summary of the commands available, optionally including
+the list of report fields (synonym with \fBhelp\fP command).
+.IP \fB--inactive
+.br
+When returning any table information from the kernel report on the
+inactive table instead of the live table.
+Requires kernel driver version 4.16.0 or above.
+.IP \fB-j|--major\ \fImajor
+.br
+Specify the major number.
+.IP \fB-m|--minor\ \fIminor
+.br
+Specify the minor number.
+.IP \fB-n|--noheadings
+.br
+Suppress the headings line when using columnar output.
+.IP \fB--noopencount
+.br
+Tell the kernel not to supply the open reference count for the device.
+.IP \fB--notable
+.br
+When creating a device, don't load any table.
+.IP \fB--udevcookie\ \fIcookie
+.br
+Use cookie for udev synchronisation.
+.IP \fB--noudevrules
+Do not allow udev to manage nodes for devices in device-mapper directory.
+.br
+.IP \fB--noudevsync
+Do not synchronise with udev when creating, renaming or removing devices.
+.br
+.IP \fB-o|--options
+.br
+Specify which fields to display.
+.IP \fB-r|--readonly
+.br
+Set the table being loaded read-only.
+.IP \fB--readahead\ [+]<sectors>|auto|none
+.br
+Specify read ahead size in units of sectors.
+The default value is "auto" which allows the kernel to choose
+a suitable value automatically. The + prefix lets you
+specify a minimum value which will not be used if it is
+smaller than the value chosen by the kernel.
+"None" is equivalent to specifying zero.
+.IP \fB--table\ <table>
+.br
+Specify a one-line table directly on the command line.
+.IP \fB-u|--uuid
+.br
+Specify the uuid.
+.IP \fB-y|--yes
+.br
+Answer yes to all prompts automatically.
+.IP \fB-v|--verbose\ [-v|--verbose]
+.br
+Produce additional output.
+.IP \fB--version
+.br
+Display the library and kernel driver version.
+.SH COMMANDS
+.IP \fBclear
+.I device_name
+.br
+Destroys the table in the inactive table slot for device_name.
+.IP \fBcreate
+.I device_name [-u uuid] [--notable | --table <table> | table_file]
+.br
+Creates a device with the given name.
+If table_file or <table> is supplied, the table is loaded and made live.
+Otherwise a table is read from standard input unless --notable is used.
+The optional uuid can be used in place of
+device_name in subsequent dmsetup commands.
+If successful a device will appear as
+/dev/device-mapper/<device-name>.
+See below for information on the table format.
+.IP \fBdeps
+.I [device_name]
+.br
+Outputs a list of (major, minor) pairs for devices referenced by the
+live table for the specified device.
+.IP \fBhelp
+.I [-c|-C|--columns]
+.br
+Outputs a summary of the commands available, optionally including
+the list of report fields.
+.IP \fBinfo
+.I [device_name]
+.br
+Outputs some brief information about the device in the form:
+.br
+ State: SUSPENDED|ACTIVE, READ-ONLY
+.br
+ Tables present: LIVE and/or INACTIVE
+.br
+ Open reference count
+.br
+ Last event sequence number (used by \fBwait\fP)
+.br
+ Major and minor device number
+.br
+ Number of targets in the live table
+.br
+ UUID
+.IP \fBinfo
+.I -c|-C|--columns
+.I [--noheadings] [--separator separator] [-o fields] [-O|--sort sort_fields]
+.I [device_name]
+.br
+Output you can customise.
+Fields are comma-separated and chosen from the following list:
+name, major, minor, attr, open, segments, events, uuid.
+Attributes are: (L)ive, (I)nactive, (s)uspended, (r)ead-only, read-(w)rite.
+Precede the list with '+' to append
+to the default selection of columns instead of replacing it.
+Precede any sort_field with - for a reverse sort on that column.
+.IP \fBls
+.I [--target target_type]
+.I [--exec command]
+.I [--tree [-o options]]
+.br
+List device names. Optionally only list devices that have at least
+one target of the specified type. Optionally execute a command for
+each device. The device name is appended to the supplied command.
+--tree displays dependencies between devices as a tree.
+It accepts a comma-separate list of options.
+Some specify the information displayed against each node:
+device/nodevice; active, open, rw, uuid.
+Others specify how the tree is displayed:
+ascii, utf, vt100; compact, inverted, notrunc.
+.IP \fBload|reload
+.I device_name [--table <table> | table_file]
+.br
+Loads <table> or table_file into the inactive table slot for device_name.
+If neither is supplied, reads a table from standard input.
+.IP \fBmessage
+.I device_name sector message
+.br
+Send message to target. If sector not needed use 0.
+.IP \fBmknodes
+.I [device_name]
+.br
+Ensure that the node in /dev/mapper for device_name is correct.
+If no device_name is supplied, ensure that all nodes in /dev/mapper
+correspond to mapped devices currently loaded by the device-mapper kernel
+driver, adding, changing or removing nodes as necessary.
+.IP \fBremove
+.I [-f|--force] device_name
+.br
+Removes a device. It will no longer be visible to dmsetup.
+Open devices cannot be removed except with older kernels
+that contain a version of device-mapper prior to 4.8.0.
+In this case the device will be deleted when its open_count
+drops to zero. From version 4.8.0 onwards, if a device can't
+be removed because an uninterruptible process is waiting for
+I/O to return from it, adding --force will replace the table
+with one that fails all I/O, which might allow the
+process to be killed.
+.IP \fBremove_all
+.I [-f|--force]
+.br
+Attempts to remove all device definitions i.e. reset the driver.
+Use with care! From version 4.8.0 onwards, if devices can't
+be removed because uninterruptible processes are waiting for
+I/O to return from them, adding --force will replace the table
+with one that fails all I/O, which might allow the
+process to be killed. This also runs \fBmknodes\fP afterwards.
+.IP \fBrename
+.I device_name new_name
+.br
+Renames a device.
+.IP \fBrename
+.I device_name --setuuid uuid
+.br
+Sets the uuid of a device that was created without a uuid.
+After a uuid has been set it cannot be changed.
+.IP \fBresume
+.I device_name
+.br
+Un-suspends a device.
+If an inactive table has been loaded, it becomes live.
+Postponed I/O then gets re-queued for processing.
+.IP \fBsetgeometry
+.I device_name cyl head sect start
+.br
+Sets the device geometry to C/H/S.
+.IP \fBsplitname
+.I device_name
+.I [subsystem]
+.br
+Splits given device name into subsystem constituents.
+Default subsystem is LVM.
+.IP \fBstatus
+.I [--target target_type]
+.I [device_name]
+.br
+Outputs status information for each of the device's targets.
+With --target, only information relating to the specified target type
+is displayed.
+.IP \fBsuspend
+.I [--nolockfs] [--noflush]
+.I device_name
+.br
+Suspends a device. Any I/O that has already been mapped by the device
+but has not yet completed will be flushed. Any further I/O to that
+device will be postponed for as long as the device is suspended.
+If there's a filesystem on the device which supports the operation,
+an attempt will be made to sync it first unless --nolockfs is specified.
+Some targets such as recent (October 2006) versions of multipath may support
+the --noflush option. This lets outstanding I/O that has not yet reached the
+device to remain unflushed.
+.IP \fBtable
+.I [--target target_type] [--showkeys]
+.I [device_name]
+.br
+Outputs the current table for the device in a format that can be fed
+back in using the create or load commands.
+With --target, only information relating to the specified target type
+is displayed.
+Encryption keys are suppressed in the table output for the crypt
+target unless the --showkeys parameter is supplied.
+.IP \fBtargets
+.br
+Displays the names and versions of the currently-loaded targets.
+.br
+.IP \fBudevcreatecookie
+.br
+Creates a new cookie to synchronize actions with udev processing.
+The output is a cookie value. Normally we don't need to create cookies since
+dmsetup creates and destroys them for each action automatically. However, we can
+generate one explicitly to group several actions together and use only one
+cookie instead. We can define a cookie to use for each relevant command by using
+--udevcookie option. Alternatively, we can export this value into the environment
+of the dmsetup process as DM_UDEV_COOKIE variable and it will be used automatically
+with all subsequent commands until it is unset.
+Invoking this command will create system-wide semaphore that needs to be cleaned
+up explicitly by calling udevreleasecookie command.
+.br
+.IP \fBudevreleasecookie
+.I [cookie]
+.br
+Waits for all pending udev processing bound to given cookie value and clean up
+the cookie with underlying semaphore. If the cookie is not given directly,
+the command will try to use a value defined by DM_UDEV_COOKIE environment variable.
+.br
+.IP \fBudevflags
+.I cookie
+.br
+Parses given cookie value and extracts any udev control flags encoded.
+The output is in environment key format that is suitable for use in udev
+rules. If the flag has its symbolic name assigned then the ouput is
+DM_UDEV_FLAG_<flag_name>='1', DM_UDEV_FLAG<flag_position>='1' otherwise.
+Subsystem udev flags don't have symbolic names assigned and these ones are
+always reported as DM_SUBSYSTEM_UDEV_FLAG<flag_position>='1'. There are
+16 udev flags altogether.
+.br
+.IP \fBudevcomplete
+.I cookie
+.br
+Wake any processes that are waiting for udev to complete processing the specified cookie.
+.br
+.IP \fBudevcomplete_all
+Remove all cookies. Any process waiting on a cookie will be resumed immediately.
+.br
+.IP \fBudevcookies
+List all existing cookies. Cookies are system-wide semaphores with keys
+prefixed by two predefined bytes (0x0D4D).
+.br
+.IP \fBversion
+.br
+Outputs version information.
+.IP \fBwait
+.I device_name
+.I [event_nr]
+.br
+Sleeps until the event counter for device_name exceeds event_nr.
+Use -v to see the event number returned.
+To wait until the next event is triggered, use \fBinfo\fP to find
+the last event number.
+.SH TABLE FORMAT
+Each line of the table specifies a single target and is of the form:
+.br
+ logical_start_sector num_sectors target_type target_args
+.br
+.br
+
+There are currently three simple target types available together
+with more complex optional ones that implement snapshots and mirrors.
+
+.IP \fBlinear
+.I destination_device start_sector
+.br
+The traditional linear mapping.
+
+.IP \fBstriped
+.I num_stripes chunk_size [destination start_sector]+
+.br
+Creates a striped area.
+.br
+e.g. striped 2 32 /dev/hda1 0 /dev/hdb1 0
+will map the first chunk (16k) as follows:
+.br
+ LV chunk 1 -> hda1, chunk 1
+.br
+ LV chunk 2 -> hdb1, chunk 1
+.br
+ LV chunk 3 -> hda1, chunk 2
+.br
+ LV chunk 4 -> hdb1, chunk 2
+.br
+ etc.
+
+.IP \fBerror
+.br
+Errors any I/O that goes to this area. Useful for testing or
+for creating devices with holes in them.
+
+.SH EXAMPLES
+
+# A table to join two disks together
+.br
+.br
+0 1028160 linear /dev/hda 0
+.br
+1028160 3903762 linear /dev/hdb 0
+
+
+# A table to stripe across the two disks,
+.br
+# and add the spare space from
+.br
+# hdb to the back of the volume
+
+0 2056320 striped 2 32 /dev/hda 0 /dev/hdb 0
+.br
+2056320 2875602 linear /dev/hdb 1028160
+
+.SH ENVIRONMENT VARIABLES
+.TP
+\fBDM_DEV_DIR\fP
+The device directory name.
+Defaults to "/dev" and must be an absolute path.
+.TP
+\fBDM_UDEV_COOKIE\fP
+A cookie to use for all relevant commands to synchronize with udev processing.
+It is an alternative to using --udevcookie option.
+
+.SH AUTHORS
+Original version: Joe Thornber (thornber@sistina.com)
+
+.SH SEE ALSO
+Device-mapper resource page: http://sources.redhat.com/dm/
--- /dev/null
+.TH "FSADM" "8" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
+.SH "NAME"
+fsadm \- utility to resize or check filesystem on a device
+.SH "SYNOPSIS"
+.B fsadm
+.RI [options]\ check\ device
+
+.B fsadm
+.RI [options]\ resize\ device\ [new_size[BKMGTEP]]
+
+.SH "DESCRIPTION"
+\fBfsadm\fR utility resizes or checks the filesystem on a device.
+It tries to use the same API for \fBext2/ext3/ext4\fR,
+\fBReiserFS\fR and \fBXFS\fR filesystem.
+.SH "OPTIONS"
+.TP
+\fB\-h \-\-help\fR
+\(em print help message
+.TP
+\fB\-v \-\-verbose\fR
+\(em be more verbose
+.TP
+\fB\-e \-\-ext\-offline\fR
+\(em unmount ext2/ext3/ext4 filesystem before doing resize
+.TP
+\fB\-f \-\-force\fR
+\(em bypass some sanity checks
+.TP
+\fB\-n \-\-dry\-run\fR
+\(em print commands without running them
+.TP
+\fB\-y \-\-yes\fR
+\(em answer "yes" at any prompts
+.TP
+\fBnew_size\fR
+\(em Absolute number of filesystem blocks to be in the filesystem,
+or an absolute size using a suffix (in powers of 1024).
+If new_size is not supplied, the whole device is used.
+
+.SH "DIAGNOSTICS"
+On successful completion, the status code is 0.
+A status code of 2 indicates the operation was interrupted by the user.
+A status code of 3 indicates the requested check operation could not be performed
+because the filesystem is mounted and does not support an online fsck.
+A status code of 1 is used for other failures.
+
+.SH "EXAMPLES"
+"fsadm \-e \-y resize /dev/vg/test 1000M" tries to resize the filesystem
+on logical volume /dev/vg/test. If /dev/vg/test contains ext2/ext3/ext4
+filesystem it will be unmounted prior the resize.
+All [y|n] questions will be answered 'y'.
+.SH "ENVIRONMENT VARIABLES"
+.TP
+\fBTMPDIR\fP
+Where the temporary directory should be created.
+.TP
+.BR
+.SH "SEE ALSO"
+.BR lvm (8),
+.BR lvresize (8),
+.BR lvm.conf (5),
+.BR tune2fs (8),
+.BR resize2fs (8),
+.BR reiserfstune (8),
+.BR resize_reiserfs (8),
+.BR xfs_info (8),
+.BR xfs_growfs (8),
+.BR xfs_check (8)
--- /dev/null
+.TH LVCHANGE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvchange \- change attributes of a logical volume
+.SH SYNOPSIS
+.B lvchange
+[\-\-addtag Tag]
+[\-A|\-\-autobackup y|n] [\-a|\-\-available y|n|ey|en|ly|ln]
+[\-\-alloc AllocationPolicy]
+[\-C|\-\-contiguous y|n] [\-d|\-\-debug] [\-\-deltag Tag]
+[\-\-resync]
+[\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure]
+[\-\-ignoremonitoring]
+[\-\-monitor {y|n}]
+[\-\-poll {y|n}]
+[\-\-sysinit]
+[\-\-noudevsync]
+[\-M|\-\-persistent y|n] [\-\-minor minor]
+[\-P|\-\-partial]
+[\-p|\-\-permission r|rw] [\-r/\-\-readahead ReadAheadSectors|auto|none]
+[\-\-refresh]
+[\-t|\-\-test]
+[\-v|\-\-verbose] LogicalVolumePath [LogicalVolumePath...]
+.SH DESCRIPTION
+lvchange allows you to change the attributes of a logical volume
+including making them known to the kernel ready for use.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-a, \-\-available y|n|ey|en|ly|ln
+Controls the availability of the logical volumes for use.
+Communicates with the kernel device-mapper driver via
+libdevmapper to activate (-ay) or deactivate (-an) the
+logical volumes.
+.IP
+If clustered locking is enabled, -aey will activate exclusively
+on one node and -aly will activate only on the local node.
+To deactivate only on the local node use -aln.
+Logical volumes with single-host snapshots are always activated
+exclusively because they can only be used on one node at once.
+.TP
+.I \-C, \-\-contiguous y|n
+Tries to set or reset the contiguous allocation policy for
+logical volumes. It's only possible to change a non-contiguous
+logical volume's allocation policy to contiguous, if all of the
+allocated physical extents are already contiguous.
+.TP
+.I \-\-resync
+Forces the complete resynchronization of a mirror. In normal
+circumstances you should not need this option because synchronization
+happens automatically. Data is read from the primary mirror device
+and copied to the others, so this can take a considerable amount of
+time - and during this time you are without a complete redundant copy
+of your data.
+.TP
+.I \-\-minor minor
+Set the minor number.
+.TP
+.I \-\-monitor y|n
+Start or stop monitoring a mirrored or snapshot logical volume with
+dmeventd, if it is installed.
+If a device used by a monitored mirror reports an I/O error,
+the failure is handled according to
+\fBmirror_image_fault_policy\fP and \fBmirror_log_fault_policy\fP
+set in \fBlvm.conf\fP.
+.TP
+.I \-\-poll y|n
+Without polling a logical volume's backgrounded transformation process
+will never complete. If there is an incomplete pvmove or lvconvert (for
+example, on rebooting after a crash), use \fB--poll y\fP to restart the
+process from its last checkpoint. However, it may not be appropriate to
+immediately poll a logical volume when it is activated, use \fB--poll
+n\fP to defer and then \fB--poll y\fP to restart the process.
+.TP
+.I \-\-sysinit
+Indicates that lvchange(8) is being invoked from early system initialisation
+scripts (e.g. rc.sysinit or an initrd), before writeable filesystems are
+available. As such, some functionality needs to be disabled and this option
+acts as a shortcut which selects an appropriate set of options. Currently
+this is equivalent to using \fB--ignorelockingfailure\fP, \fB--ignoremonitoring\fP,
+\fB--poll n\fP and setting \fBLVM_SUPPRESS_LOCKING_FAILURE_MESSAGES\fP
+environment variable.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background. You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.I \-\-ignoremonitoring
+Make no attempt to interact with dmeventd unless \-\-monitor
+is specified.
+Do not use this if dmeventd is already monitoring a device.
+.TP
+.I \-M, \-\-persistent y|n
+Set to y to make the minor number specified persistent.
+.TP
+.I \-p, \-\-permission r|rw
+Change access permission to read-only or read/write.
+.TP
+.I \-r, \-\-readahead ReadAheadSectors|auto|none
+Set read ahead sector count of this logical volume.
+For volume groups with metadata in lvm1 format, this must
+be a value between 2 and 120 sectors.
+The default value is "auto" which allows the kernel to choose
+a suitable value automatically.
+"None" is equivalent to specifying zero.
+.TP
+.I \-\-refresh
+If the logical volume is active, reload its metadata.
+This is not necessary in normal operation, but may be useful
+if something has gone wrong or if you're doing clustering
+manually without a clustered lock manager.
+.SH Examples
+"lvchange -pr vg00/lvol1" changes the permission on
+volume lvol1 in volume group vg00 to be read-only.
+
+.SH SEE ALSO
+.BR lvm (8),
+.BR lvcreate (8),
+.BR vgchange (8)
--- /dev/null
+.TH LVCONVERT 8 "LVM TOOLS #VERSION#" "Red Hat, Inc" \" -*- nroff -*-
+.SH NAME
+lvconvert \- convert a logical volume from linear to mirror or snapshot
+.SH SYNOPSIS
+.B lvconvert
+\-m|\-\-mirrors Mirrors [\-\-mirrorlog {disk|core|mirrored}] [\-\-corelog] [\-R|\-\-regionsize MirrorLogRegionSize]
+[\-A|\-\-alloc AllocationPolicy]
+[\-b|\-\-background] [\-f|\-\-force] [\-i|\-\-interval Seconds]
+[\-h|\-?|\-\-help]
+[\-\-stripes Stripes [\-I|\-\-stripesize StripeSize]]
+[\-\-noudevsync]
+[\-v|\-\-verbose] [\-y|\-\-yes]
+[\-\-version]
+.br
+LogicalVolume[Path] [PhysicalVolume[Path][:PE[-PE]]...]
+.br
+
+.br
+.B lvconvert
+\-\-splitmirrors Images \-\-name SplitLogicalVolumeName
+.br
+MirrorLogicalVolume[Path] [SplittablePhysicalVolume[Path][:PE[-PE]]...]
+.br
+
+.br
+.B lvconvert
+\-s|\-\-snapshot [\-c|\-\-chunksize ChunkSize]
+[\-h|\-?|\-\-help]
+[\-\-noudevsync]
+[\-v|\-\-verbose]
+[\-Z|\-\-zero y|n]
+[\-\-version]
+.br
+OriginalLogicalVolume[Path] SnapshotLogicalVolume[Path]
+.br
+
+.br
+.B lvconvert
+\-\-merge [\-b|\-\-background] [\-i|\-\-interval Seconds]
+[\-h|\-?|\-\-help]
+[\-v|\-\-verbose]
+[\-\-version]
+SnapshotLogicalVolume[Path]...
+.br
+
+.br
+.B lvconvert
+\-\-repair
+[\-h|\-?|\-\-help]
+[\-v|\-\-verbose]
+[\-\-version]
+LogicalVolume[Path] [PhysicalVolume[Path]...]
+.SH DESCRIPTION
+lvconvert will change a linear logical volume to a mirror
+logical volume or to a snapshot of linear volume and vice versa.
+It is also used to add and remove disk logs from mirror devices.
+.br
+If the conversion requires allocation of physical extents (for
+example, when converting from linear to mirror) and you specify
+one or more PhysicalVolumes (optionally with ranges of physical
+extents), allocation of physical extents will be restricted to
+these physical extents. If the conversion frees physical extents
+(for example, when converting from a mirror to a linear, or reducing
+mirror legs) and you specify one or more PhysicalVolumes,
+the freed extents come first from the specified PhysicalVolumes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.br
+Exactly one of \-\-splitmirrors, \-\-mirrors, \-\-repair, \-\-snapshot
+or \-\-merge arguments is required.
+.br
+.TP
+.I \-m, \-\-mirrors Mirrors
+Specifies the degree of the mirror you wish to create.
+For example, "-m 1" would convert the original logical
+volume to a mirror volume with 2-sides; that is, a
+linear volume plus one copy.
+.TP
+.I \-\-mirrorlog {disk|core|mirrored}
+Specifies the type of log to use.
+The default is disk, which is persistent and requires
+a small amount of storage space, usually on a separate device
+from the data being mirrored.
+Core may be useful for short-lived mirrors: It means the mirror is
+regenerated by copying the data from the first device again every
+time the device is activated - perhaps, for example, after every reboot.
+Using "mirrored" will create a persistent log that is itself mirrored.
+.TP
+.I \-\-corelog
+The optional argument "--corelog" is the same as specifying "--mirrorlog core".
+.TP
+.I \-R, \-\-regionsize MirrorLogRegionSize
+A mirror is divided into regions of this size (in MB), and the mirror log
+uses this granularity to track which regions are in sync.
+.TP
+.I \-b, \-\-background
+Run the daemon in the background.
+.TP
+.I \-i, \-\-interval Seconds
+Report progress as a percentage at regular intervals.
+.br
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background. You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.br
+
+
+.TP
+.I \-\-splitmirrors Images
+The number of redundant Images of a mirror to be split off and used
+to form a new logical volume. A name must be supplied for the
+newly-split-off logical volume using the \-\-name argument.
+
+.TP
+.I \-n Name
+The name to apply to a logical volume which has been split off from
+a mirror logical volume.
+.br
+
+
+.TP
+.I \-s, \-\-snapshot
+Create a snapshot from existing logical volume using another
+existing logical volume as its origin.
+.TP
+.I \-c, \-\-chunksize ChunkSize
+Power of 2 chunk size for the snapshot logical volume between 4k and 512k.
+.TP
+.I \-Z, \-\-zero y|n
+Controls zeroing of the first KB of data in the snapshot.
+If the volume is read-only the snapshot will not be zeroed.
+.TP
+.I \-\-merge
+Merges a snapshot into its origin volume. To check if your kernel
+supports this feature, look for 'snapshot-merge' in the output
+of 'dmsetup targets'. If both the origin and snapshot volume are not
+open the merge will start immediately. Otherwise, the merge will start
+the first time either the origin or snapshot are activated and both are closed.
+Merging a snapshot into an origin that cannot be closed, for example a root
+filesystem, is deferred until the next time the origin volume is activated.
+When merging starts, the resulting logical volume will have the origin's name,
+minor number and UUID. While the merge is in progress, reads or writes to the
+origin appear as they were directed to the snapshot being merged. When the
+merge finishes, the merged snapshot is removed. Multiple snapshots may
+be specified on the commandline or a @tag may be used to specify
+multiple snapshots be merged to their respective origin.
+.br
+
+
+.TP
+.I \-\-repair
+Repair a mirror after suffering a disk failure. The mirror will be brought back
+into a consistent state. By default, the original number of mirrors will be
+restored if possible. Specify \-y on the command line to skip the prompts.
+Use \-f if you do not want any replacement. Additionally, you may use
+\-\-use-policies to use the device replacement policy specified in lvm.conf,
+viz. activation/mirror_log_fault_policy or
+activation/mirror_device_fault_policy.
+.br
+.SH Examples
+"lvconvert -m1 vg00/lvol1"
+.br
+converts the linear logical volume "vg00/lvol1" to
+a two-way mirror logical volume.
+
+"lvconvert --mirrorlog core vg00/lvol1"
+.br
+converts a mirror with a disk log to a
+mirror with an in-memory log.
+
+"lvconvert --mirrorlog disk vg00/lvol1"
+.br
+converts a mirror with an in-memory log
+to a mirror with a disk log.
+
+"lvconvert -m0 vg00/lvol1"
+.br
+converts a mirror logical volume to a linear logical
+volume.
+.br
+
+.br
+"lvconvert -s vg00/lvol1 vg00/lvol2"
+.br
+converts logical volume "vg00/lvol2" to snapshot of original volume "vg00/lvol1"
+
+.br
+"lvconvert -m1 vg00/lvol1 /dev/sda:0-15 /dev/sdb:0-15"
+.br
+converts linear logical volume "vg00/lvol1" to a two-way mirror, using physical
+extents /dev/sda:0-15 and /dev/sdb:0-15 for allocation of new extents.
+
+.br
+"lvconvert -m0 vg00/lvmirror1 /dev/sda
+.br
+converts mirror logical volume "vg00/lvmirror1" to linear, freeing physical
+extents from /dev/sda.
+
+.br
+"lvconvert --merge vg00/lvol1_snap"
+.br
+merges "vg00/lvol1_snap" into its origin.
+
+.br
+"lvconvert --merge @some_tag"
+.br
+If vg00/lvol1, vg00/lvol2, and vg00/lvol3 are all tagged with "some_tag"
+each snapshot logical volume will be merged serially, e.g.: vg00/lvol1,
+then vg00/lvol2, then vg00/lvol3. If --background were used it would start
+all snapshot logical volume merges in parallel.
+
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8),
+.BR lvremove (8),
+.BR lvrename (8),
+.BR lvextend (8),
+.BR lvreduce (8),
+.BR lvdisplay (8),
+.BR lvscan (8)
--- /dev/null
+.TH LVCREATE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvcreate \- create a logical volume in an existing volume group
+.SH SYNOPSIS
+.B lvcreate
+[\-\-addtag Tag]
+[\-\-alloc AllocationPolicy]
+[\-A|\-\-autobackup y|n] [\-C|\-\-contiguous y|n] [\-d|\-\-debug]
+[\-h|\-?|\-\-help] [\-\-noudevsync]
+[\-\-ignoremonitoring]
+[\-\-monitor {y|n}]
+[\-i|\-\-stripes Stripes [\-I|\-\-stripesize StripeSize]]
+{\-l|\-\-extents LogicalExtentsNumber[%{VG|PVS|FREE}] |
+ \-L|\-\-size LogicalVolumeSize[bBsSkKmMgGtTpPeE]}
+[\-M|\-\-persistent y|n] [\-\-minor minor]
+[\-m|\-\-mirrors Mirrors [\-\-nosync] [\-\-mirrorlog {disk|core|mirrored}] [\-\-corelog]
+[\-R|\-\-regionsize MirrorLogRegionSize]]
+[\-n|\-\-name LogicalVolumeName]
+[\-p|\-\-permission r|rw] [\-r|\-\-readahead ReadAheadSectors|auto|none]
+[\-t|\-\-test]
+[\-\-type SegmentType]
+[\-v|\-\-verbose] [\-Z|\-\-zero y|n]
+VolumeGroupName [PhysicalVolumePath[:PE[-PE]]...]
+.br
+
+.br
+.B lvcreate
+{\-l|\-\-extents LogicalExtentsNumber[%{VG|FREE|ORIGIN}] |
+ \-L|\-\-size LogicalVolumeSize[bBsSkKmMgGtTpPeE]}
+[\-c|\-\-chunksize ChunkSize]
+[\-\-noudevsync]
+[\-\-ignoremonitoring]
+[\-\-monitor {y|n}]
+\-n|\-\-name SnapshotLogicalVolumeName
+{{\-s|\-\-snapshot}
+OriginalLogicalVolumePath |
+[\-s|\-\-snapshot]
+VolumeGroupName \-\-virtualsize VirtualSize}
+.SH DESCRIPTION
+lvcreate creates a new logical volume in a volume group ( see
+.B vgcreate(8), vgchange(8)
+) by allocating logical extents from the free physical extent pool
+of that volume group. If there are not enough free physical extents then
+the volume group can be extended ( see
+.B vgextend(8)
+) with other physical volumes or by reducing existing logical volumes
+of this volume group in size ( see
+.B lvreduce(8)
+). If you specify one or more PhysicalVolumes, allocation of physical
+extents will be restricted to these volumes.
+.br
+.br
+The second form supports the creation of snapshot logical volumes which
+keep the contents of the original logical volume for backup purposes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-c, \-\-chunksize ChunkSize
+Power of 2 chunk size for the snapshot logical volume between 4k and 512k.
+.TP
+.I \-C, \-\-contiguous y|n
+Sets or resets the contiguous allocation policy for
+logical volumes. Default is no contiguous allocation based
+on a next free principle.
+.TP
+.I \-i, \-\-stripes Stripes
+Gives the number of stripes.
+This is equal to the number of physical volumes to scatter
+the logical volume.
+.TP
+.I \-I, \-\-stripesize StripeSize
+Gives the number of kilobytes for the granularity of the stripes.
+.br
+StripeSize must be 2^n (n = 2 to 9) for metadata in LVM1 format.
+For metadata in LVM2 format, the stripe size may be a larger
+power of 2 but must not exceed the physical extent size.
+.TP
+.I \-l, \-\-extents LogicalExtentsNumber[%{VG|PVS|FREE|ORIGIN}]
+Gives the number of logical extents to allocate for the new
+logical volume.
+The number can also be expressed as a percentage of the total space
+in the Volume Group with the suffix %VG, as a percentage of the
+remaining free space in the Volume Group with the suffix %FREE, as a
+percentage of the remaining free space for the specified
+PhysicalVolume(s) with the suffix %PVS, or (for a snapshot) as a
+percentage of the total space in the Origin Logical Volume with the
+suffix %ORIGIN.
+.TP
+.I \-L, \-\-size LogicalVolumeSize[bBsSkKmMgGtTpPeE]
+Gives the size to allocate for the new logical volume.
+A size suffix of K for kilobytes, M for megabytes,
+G for gigabytes, T for terabytes, P for petabytes
+or E for exabytes is optional.
+.br
+Default unit is megabytes.
+.TP
+.I \-\-minor minor
+Set the minor number.
+.TP
+.I \-M, \-\-persistent y|n
+Set to y to make the minor number specified persistent.
+.TP
+.I \-m, \-\-mirrors Mirrors
+Creates a mirrored logical volume with Mirrors copies. For example,
+specifying "-m 1" would result in a mirror with two-sides; that is, a
+linear volume plus one copy.
+
+Specifying the optional argument --nosync will cause the creation
+of the mirror to skip the initial resynchronization. Any data written
+afterwards will be mirrored, but the original contents will not be
+copied. This is useful for skipping a potentially long and resource
+intensive initial sync of an empty device.
+
+The optional argument --mirrorlog specifies the type of log to be used.
+The default is disk, which is persistent and requires
+a small amount of storage space, usually on a separate device from the
+data being mirrored. Using core means the mirror is regenerated
+by copying the data from the first device again each time the
+device is activated, for example, after every reboot. Using "mirrored"
+will create a persistent log that is itself mirrored.
+
+The optional argument --corelog is equivalent to --mirrorlog core.
+
+.TP
+.I \-n, \-\-name LogicalVolumeName
+The name for the new logical volume.
+.br
+Without this option a default names of "lvol#" will be generated where
+# is the LVM internal number of the logical volume.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background. You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.I \-\-monitor y|n
+Start or avoid monitoring a mirrored or snapshot logical volume with
+dmeventd, if it is installed.
+If a device used by a monitored mirror reports an I/O error,
+the failure is handled according to
+\fBmirror_image_fault_policy\fP and \fBmirror_log_fault_policy\fP
+set in \fBlvm.conf\fP.
+.TP
+.I \-\-ignoremonitoring
+Make no attempt to interact with dmeventd unless \-\-monitor
+is specified.
+.TP
+.I \-p, \-\-permission r|rw
+Set access permissions to read only or read and write.
+.br
+Default is read and write.
+.TP
+.I \-r, \-\-readahead ReadAheadSectors|auto|none
+Set read ahead sector count of this logical volume.
+For volume groups with metadata in lvm1 format, this must
+be a value between 2 and 120.
+The default value is "auto" which allows the kernel to choose
+a suitable value automatically.
+"None" is equivalent to specifying zero.
+.TP
+.I \-R, \-\-regionsize MirrorLogRegionSize
+A mirror is divided into regions of this size (in MB), and the mirror log
+uses this granularity to track which regions are in sync.
+.TP
+.I \-s, \-\-snapshot
+Create a snapshot logical volume (or snapshot) for an existing, so called
+original logical volume (or origin).
+Snapshots provide a 'frozen image' of the contents of the origin
+while the origin can still be updated. They enable consistent
+backups and online recovery of removed/overwritten data/files. The snapshot
+does not need the same amount of storage the origin has. In a typical scenario,
+15-20% might be enough. In case the snapshot runs out of storage, use
+.B lvextend(8)
+to grow it. Shrinking a snapshot is supported by
+.B lvreduce(8)
+as well. Run
+.B lvdisplay(8)
+on the snapshot in order to check how much data is allocated to it.
+Note that a small amount of the space you allocate to the snapshot is
+used to track the locations of the chunks of data, so you should
+allocate slightly more space than you actually need and monitor the
+rate at which the snapshot data is growing so you can avoid running out
+of space.
+.TP
+.I \-\-type SegmentType
+Create a logical volume that uses the specified segment type
+(e.g. "mirror", "snapshot", "striped"). Especially useful when no
+existing commandline switch alias enables the use of the desired type
+(e.g. "error" or "zero" types). Many segment types already have a
+commandline switch alias that will enable their use (-s is an alias for
+--type snapshot).
+.TP
+.I \-\-virtualsize VirtualSize
+Create a sparse device of the given size (in MB by default) using a snapshot.
+Anything written to the device will be returned when reading from it.
+Reading from other areas of the device will return blocks of zeros.
+It is implemented by creating a hidden virtual device of the
+requested size using the zero target. A suffix of _vorigin is used for
+this device.
+.TP
+.I \-Z, \-\-zero y|n
+Controls zeroing of the first KB of data in the new logical volume.
+.br
+Default is yes.
+.br
+Volume will not be zeroed if read only flag is set.
+.br
+Snapshot volumes are zeroed always.
+
+.br
+Warning: trying to mount an unzeroed logical volume can cause the system to
+hang.
+.SH Examples
+"lvcreate -i 3 -I 8 -L 100M vg00" tries to create a striped logical
+volume with 3 stripes, a stripesize of 8KB and a size of 100MB in the volume
+group named vg00. The logical volume name will be chosen by lvcreate.
+
+"lvcreate -m1 -L 500M vg00" tries to create a mirror logical volume
+with 2 sides with a useable size of 500 MiB. This operation would
+require 3 devices - two for the mirror devices and one for the disk
+log.
+
+"lvcreate -m1 --mirrorlog core -L 500M vg00" tries to create a mirror logical volume
+with 2 sides with a useable size of 500 MiB. This operation would
+require 2 devices - the log is "in-memory".
+
+"lvcreate --size 100m --snapshot --name snap /dev/vg00/lvol1"
+.br
+creates a snapshot logical volume named /dev/vg00/snap which has access to the
+contents of the original logical volume named /dev/vg00/lvol1
+at snapshot logical volume creation time. If the original logical volume
+contains a file system, you can mount the snapshot logical volume on an
+arbitrary directory in order to access the contents of the filesystem to run
+a backup while the original filesystem continues to get updated.
+
+"lvcreate --virtualsize 1T --size 100M --snapshot --name sparse vg1"
+.br
+creates a sparse device named /dev/vg1/sparse of size 1TB with space for just
+under 100MB of actual data on it.
+.br
+
+"lvcreate -L 64M -n lvol1 vg00 /dev/sda:0-7 /dev/sdb:0-7"
+.br
+creates a linear logical volume "vg00/lvol1" using physical extents
+/dev/sda:0-7 and /dev/sdb:0-7 for allocation of extents.
+
+
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8),
+.BR lvremove (8),
+.BR lvrename (8)
+.BR lvextend (8),
+.BR lvreduce (8),
+.BR lvdisplay (8),
+.BR lvscan (8)
--- /dev/null
+.TH LVDISPLAY 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvdisplay \- display attributes of a logical volume
+.SH SYNOPSIS
+.B lvdisplay
+[\-a|\-\-all]
+[\-c|\-\-colon] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure]
+[\-\-maps]
+[\-\-nosuffix]
+[\-P|\-\-partial]
+[\-\-units hHbBsSkKmMgGtTpPeE]
+[\-v|\-\-verbose]
+[\-\-version] [LogicalVolumePath [LogicalVolumePath...]]
+.br
+
+.br
+.B lvdisplay \-\-columns | \-C
+[\-\-aligned]
+[\-a|\-\-all]
+[\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure]
+[\-\-noheadings]
+[\-\-nosuffix]
+[\-o|\-\-options [+]Field[,Field]]
+[\-O|\-\-sort [+|-]Key1[,[+|-]Key2[,...]]]
+[\-P|\-\-partial]
+[\-\-segments]
+[\-\-separator Separator]
+[\-\-unbuffered]
+[\-\-units hHbBsSkKmMgGtTpPeE]
+[\-v|\-\-verbose]
+[\-\-version] [LogicalVolumePath [LogicalVolumePath...]]
+.SH DESCRIPTION
+lvdisplay allows you to see the attributes of a logical volume
+like size, read/write status, snapshot information etc.
+.P
+\fBlvs\fP (8) is an alternative that provides the same information
+in the style of \fBps\fP (1). \fBlvs\fP is recommended over
+\fBlvdisplay\fP.
+
+.SH OPTIONS
+See \fBlvm\fP for common options and \fBlvs\fP for options given with
+\fB\-\-columns\fP.
+.TP
+.I \-\-all
+Include information in the output about internal Logical Volumes that
+are components of normally-accessible Logical Volumes, such as mirrors,
+but which are not independently accessible (e.g. not mountable).
+For example, after creating a mirror using 'lvcreate -m1 --mirrorlog disk',
+this option will reveal three internal Logical Volumes, with suffixes
+mimage_0, mimage_1, and mlog.
+.TP
+.I \-c, \-\-colon
+Generate colon separated output for easier parsing in scripts or programs.
+N.B. \fBlvs\fP (8) provides considerably more control over the output.
+.nf
+
+The values are:
+
+* logical volume name
+* volume group name
+* logical volume access
+* logical volume status
+* internal logical volume number
+* open count of logical volume
+* logical volume size in sectors
+* current logical extents associated to logical volume
+* allocated logical extents of logical volume
+* allocation policy of logical volume
+* read ahead sectors of logical volume
+* major device number of logical volume
+* minor device number of logical volume
+
+.fi
+.TP
+.I \-m, \-\-maps
+Display the mapping of logical extents to physical volumes and
+physical extents. To map physical extents
+to logical extents use
+.BR
+\fBpvs --segments -o+lv_name,seg_start_pe,segtype\fP.
+.TP
+.I \-\-columns | \-C
+Display output in columns, the equivalent of \fBlvs\fP. Options listed
+are the same as options given in \fBlvs (8)\fP.
+.SH Examples
+"lvdisplay -v /dev/vg00/lvol2" shows attributes of that logical volume.
+If snapshot
+logical volumes have been created for this original logical volume,
+this command shows a list of all snapshot logical volumes and their
+status (active or inactive) as well.
+
+"lvdisplay /dev/vg00/snapshot" shows the attributes of this snapshot
+logical volume and also which original logical volume
+it is associated with.
+
+.SH SEE ALSO
+.BR lvm (8),
+.BR lvcreate (8),
+.BR lvscan (8),
+.BR pvs (8)
--- /dev/null
+.TH LVEXTEND 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvextend \- extend the size of a logical volume
+.SH SYNOPSIS
+.B lvextend
+[\-\-alloc AllocationPolicy]
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-noudevsync]
+[\-i|\-\-stripes Stripes [\-I|\-\-stripesize StripeSize]]
+{\-l|\-\-extents [+]LogicalExtentsNumber[%{VG|LV|PVS|FREE|ORIGIN}] |
+\-L|\-\-size [+]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}
+[\-f|\-\-force]
+[\-n|\-\-nofsck]
+[\-r|\-\-resizefs]
+[\-t|\-\-test]
+[\-v|\-\-verbose] LogicalVolumePath [PhysicalVolumePath[:PE[-PE]]...]
+.SH DESCRIPTION
+lvextend allows you to extend the size of a logical volume.
+Extension of snapshot logical volumes (see
+.B lvcreate(8)
+for information to create snapshots) is supported as well.
+But to change the number of copies in a mirrored logical
+volume use
+.BR lvconvert (8).
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background. You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.I \-l, \-\-extents [+]LogicalExtentsNumber[%{VG|LV|PVS|FREE|ORIGIN}]
+Extend or set the logical volume size in units of logical extents.
+With the + sign the value is added to the actual size
+of the logical volume and without it, the value is taken as an absolute one.
+The number can also be expressed as a percentage of the total space
+in the Volume Group with the suffix %VG, relative to the existing
+size of the Logical Volume with the suffix %LV, of the remaining
+free space for the specified PhysicalVolume(s) with the suffix %PVS,
+as a percentage of the remaining free space in the Volume Group
+with the suffix %FREE, or (for a snapshot) as a percentage of the total
+space in the Origin Logical Volume with the suffix %ORIGIN.
+.TP
+.I \-L, \-\-size [+]LogicalVolumeSize[bBsSkKmMgGtTpPeE]
+Extend or set the logical volume size in units of megabytes.
+A size suffix of M for megabytes,
+G for gigabytes, T for terabytes, P for petabytes
+or E for exabytes is optional.
+With the + sign the value is added to the actual size
+of the logical volume and without it, the value is taken as an absolute one.
+.TP
+.I \-i, \-\-stripes Stripes
+Gives the number of stripes for the extension.
+Not applicable to LVs using the original metadata LVM format, which must
+use a single value throughout.
+.TP
+.I \-I, \-\-stripesize StripeSize
+Gives the number of kilobytes for the granularity of the stripes.
+Not applicable to LVs using the original metadata LVM format, which must
+use a single value throughout.
+.br
+StripeSize must be 2^n (n = 2 to 9)
+.TP
+.I \-f, \-\-force
+Proceed with size extension without prompting.
+.TP
+.I \-n, \-\-nofsck
+Do not perform fsck before extending filesystem when filesystem
+requires it. You may need to use \fB--force\fR to proceed with
+this option.
+.TP
+.I \-r, \-\-resizefs
+Resize underlying filesystem together with the logical volume using
+\fBfsadm\fR(8).
+.SH Examples
+"lvextend -L +54 /dev/vg01/lvol10 /dev/sdk3" tries to extend the size of
+that logical volume by 54MB on physical volume /dev/sdk3.
+This is only possible if /dev/sdk3 is a member of volume group vg01 and
+there are enough free physical extents in it.
+
+"lvextend /dev/vg01/lvol01 /dev/sdk3" tries to extend the size of that
+logical volume by the amount of free space on physical volume /dev/sdk3.
+This is equivalent to specifying "-l +100%PVS" on the command line.
+
+.br
+"lvextend -L+16M vg01/lvol01 /dev/sda:8-9 /dev/sdb:8-9"
+.br
+tries to extend a logical volume "vg01/lvol01" by 16MB using physical extents
+/dev/sda:8-9 and /dev/sdb:8-9 for allocation of extents.
+
+.SH SEE ALSO
+.BR fsadm (8),
+.BR lvm (8),
+.BR lvcreate (8),
+.BR lvconvert (8),
+.BR lvreduce (8),
+.BR lvresize (8),
+.BR lvchange (8)
--- /dev/null
+.TH LVM 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvm \- LVM2 tools
+.SH SYNOPSIS
+.B lvm
+[command | file]
+.SH DESCRIPTION
+\fBlvm\fP provides the command-line tools for LVM2. A separate
+manual page describes each command in detail.
+.LP
+If \fBlvm\fP is invoked with no arguments it presents a readline prompt
+(assuming it was compiled with readline support).
+LVM commands may be entered interactively at this prompt with
+readline facilities including history and command name and option
+completion. Refer to \fBreadline\fP(3) for details.
+.LP
+If \fBlvm\fP is invoked with argv[0] set to the name of a specific
+LVM command (for example by using a hard or soft link) it acts as
+that command.
+.LP
+On invocation, \fBlvm\fP requires that only the standard file descriptors
+stdin, stdout and stderr are available. If others are found, they
+get closed and messages are issued warning about the leak.
+.LP
+Where commands take VG or LV names as arguments, the full path name is
+optional. An LV called "lvol0" in a VG called "vg0" can be specified
+as "vg0/lvol0". Where a list of VGs is required but is left empty,
+a list of all VGs will be substituted. Where a list of LVs is required
+but a VG is given, a list of all the LVs in that VG will be substituted.
+So "lvdisplay vg0" will display all the LVs in "vg0".
+Tags can also be used - see \fBaddtag\fP below.
+.LP
+One advantage of using the built-in shell is that configuration
+information gets cached internally between commands.
+.LP
+A file containing a simple script with one command per line
+can also be given on the command line. The script can also be
+executed directly if the first line is #! followed by the absolute
+path of \fBlvm\fP.
+.SH BUILT-IN COMMANDS
+The following commands are built into lvm without links normally
+being created in the filesystem for them.
+.TP
+\fBdumpconfig\fP \(em Display the configuration information after
+loading \fBlvm.conf\fP (5) and any other configuration files.
+.TP
+\fBformats\fP \(em Display recognised metadata formats.
+.TP
+\fBhelp\fP \(em Display the help text.
+.TP
+\fBpvdata\fP \(em Not implemented in LVM2.
+.TP
+\fBsegtypes\fP \(em Display recognised logical volume segment types.
+.TP
+\fBversion\fP \(em Display version information.
+.LP
+.SH COMMANDS
+The following commands implement the core LVM functionality.
+.TP
+\fBpvchange\fP \(em Change attributes of a physical volume.
+.TP
+\fBpvck\fP \(em Check physical volume metadata.
+.TP
+\fBpvcreate\fP \(em Initialize a disk or partition for use by LVM.
+.TP
+\fBpvdisplay\fP \(em Display attributes of a physical volume.
+.TP
+\fBpvmove\fP \(em Move physical extents.
+.TP
+\fBpvremove\fP \(em Remove a physical volume.
+.TP
+\fBpvresize\fP \(em Resize a disk or partition in use by LVM2.
+.TP
+\fBpvs\fP \(em Report information about physical volumes.
+.TP
+\fBpvscan\fP \(em Scan all disks for physical volumes.
+.TP
+\fBvgcfgbackup\fP \(em Backup volume group descriptor area.
+.TP
+\fBvgcfgrestore\fP \(em Restore volume group descriptor area.
+.TP
+\fBvgchange\fP \(em Change attributes of a volume group.
+.TP
+\fBvgck\fP \(em Check volume group metadata.
+.TP
+\fBvgconvert\fP \(em Convert volume group metadata format.
+.TP
+\fBvgcreate\fP \(em Create a volume group.
+.TP
+\fBvgdisplay\fP \(em Display attributes of volume groups.
+.TP
+\fBvgexport\fP \(em Make volume groups unknown to the system.
+.TP
+\fBvgextend\fP \(em Add physical volumes to a volume group.
+.TP
+\fBvgimport\fP \(em Make exported volume groups known to the system.
+.TP
+\fBvgimportclone\fP \(em Import and rename duplicated volume group (e.g. a hardware snapshot).
+.TP
+\fBvgmerge\fP \(em Merge two volume groups.
+.TP
+\fBvgmknodes\fP \(em Recreate volume group directory and logical volume special files
+.TP
+\fBvgreduce\fP \(em Reduce a volume group by removing one or more physical volumes.
+.TP
+\fBvgremove\fP \(em Remove a volume group.
+.TP
+\fBvgrename\fP \(em Rename a volume group.
+.TP
+\fBvgs\fP \(em Report information about volume groups.
+.TP
+\fBvgscan\fP \(em Scan all disks for volume groups and rebuild caches.
+.TP
+\fBvgsplit\fP \(em Split a volume group into two, moving any logical volumes from one volume group to another by moving entire physical volumes.
+.TP
+\fBlvchange\fP \(em Change attributes of a logical volume.
+.TP
+\fBlvconvert\fP \(em Convert a logical volume from linear to mirror or snapshot.
+.TP
+\fBlvcreate\fP \(em Create a logical volume in an existing volume group.
+.TP
+\fBlvdisplay\fP \(em Display attributes of a logical volume.
+.TP
+\fBlvextend\fP \(em Extend the size of a logical volume.
+.TP
+\fBlvmchange\fP \(em Change attributes of the logical volume manager.
+.TP
+\fBlvmdiskscan\fP \(em Scan for all devices visible to LVM2.
+.TP
+\fBlvmdump\fP \(em Create lvm2 information dumps for diagnostic purposes.
+.TP
+\fBlvreduce\fP \(em Reduce the size of a logical volume.
+.TP
+\fBlvremove\fP \(em Remove a logical volume.
+.TP
+\fBlvrename\fP \(em Rename a logical volume.
+.TP
+\fBlvresize\fP \(em Resize a logical volume.
+.TP
+\fBlvs\fP \(em Report information about logical volumes.
+.TP
+\fBlvscan\fP \(em Scan (all disks) for logical volumes.
+.TP
+The following commands are not implemented in LVM2 but might be in the future: lvmsadc, lvmsar, pvdata.
+.SH OPTIONS
+The following options are available for many of the commands.
+They are implemented generically and documented here rather
+than repeated on individual manual pages.
+.TP
+\fB-h | --help\fP \(em Display the help text.
+.TP
+\fB--version\fP \(em Display version information.
+.TP
+\fB-v | --verbose\fP \(em Set verbose level.
+Repeat from 1 to 3 times to increase the detail of messages
+sent to stdout and stderr. Overrides config file setting.
+.TP
+\fB-d | --debug\fP \(em Set debug level.
+Repeat from 1 to 6 times to increase the detail of messages sent
+to the log file and/or syslog (if configured).
+Overrides config file setting.
+.TP
+\fB--quiet\fP \(em Suppress output and log messages.
+Overrides -d and -v.
+.TP
+\fB-t | --test\fP \(em Run in test mode.
+Commands will not update metadata.
+This is implemented by disabling all metadata writing but nevertheless
+returning success to the calling function. This may lead to unusual
+error messages in multi-stage operations if a tool relies on reading
+back metadata it believes has changed but hasn't.
+.TP
+\fB--driverloaded\fP { \fBy\fP | \fBn\fP }
+Whether or not the device-mapper kernel driver is loaded.
+If you set this to \fBn\fP, no attempt will be made to contact the driver.
+.TP
+\fB-A | --autobackup\fP { \fBy\fP | \fBn\fP }
+Whether or not to metadata should be backed up automatically after a change.
+You are strongly advised not to disable this!
+See
+.B vgcfgbackup (8).
+.TP
+\fB-P | --partial\fP
+When set, the tools will do their best to provide access to volume groups
+that are only partially available (one or more physical volumes belonging
+to the volume group are missing from the system). Where part of a logical
+volume is missing, \fB/dev/ioerror\fP will be substituted, and you could use
+\fBdmsetup (8)\fP to set this up to return I/O errors when accessed,
+or create it as a large block device of nulls. Metadata may not be
+changed with this option. To insert a replacement physical volume
+of the same or large size use \fBpvcreate -u\fP to set the uuid to
+match the original followed by \fBvgcfgrestore (8)\fP.
+.TP
+\fB-M | --metadatatype type\fP
+Specifies which type of on-disk metadata to use, such as \fBlvm1\fP
+or \fBlvm2\fP, which can be abbreviated to \fB1\fP or \fB2\fP respectively.
+The default (lvm2) can be changed by setting \fBformat\fP in the \fBglobal\fP
+section of the config file.
+.TP
+\fB--ignorelockingfailure\fP
+This lets you proceed with read-only metadata operations such as
+\fBlvchange -ay\fP and \fBvgchange -ay\fP even if the locking module fails.
+One use for this is in a system init script if the lock directory
+is mounted read-only when the script runs.
+.TP
+\fB--addtag tag\fP
+Add the tag \fBtag\fP to a PV, VG or LV.
+Supply this argument multiple times to add more than one tag at once.
+A tag is a word that can be used to group LVM2 objects of the same type
+together.
+Tags can be given on the command line in place of PV, VG or LV
+arguments. Tags should be prefixed with @ to avoid ambiguity.
+Each tag is expanded by replacing it with all objects possessing
+that tag which are of the type expected by its position on the command line.
+PVs can only possess tags while they are part of a Volume Group:
+PV tags are discarded if the PV is removed from the VG.
+As an example, you could tag some LVs as \fBdatabase\fP and others
+as \fBuserdata\fP and then activate the database ones
+with \fBlvchange -ay @database\fP.
+Objects can possess multiple tags simultaneously.
+Only the new LVM2 metadata format supports tagging: objects using the
+LVM1 metadata format cannot be tagged because the on-disk format does not
+support it.
+Snapshots cannot be tagged.
+Characters allowed in tags are: A-Z a-z 0-9 _ + . - and
+as of version 2.02.78 the following characters are also
+accepted: / = ! : # &
+.TP
+\fB--deltag tag\fP
+Delete the tag \fBtag\fP from a PV, VG or LV, if it's present.
+Supply this argument multiple times to remove more than one tag at once.
+.TP
+\fB--alloc AllocationPolicy\fP
+The allocation policy to use: \fBcontiguous\fP, \fBcling\fP, \fBnormal\fP, \fBanywhere\fP or \fBinherit\fP.
+When a command needs to allocate physical extents from the volume group,
+the allocation policy controls how they are chosen.
+Each volume group and logical volume has an allocation policy.
+The default for a volume group is \fBnormal\fP which applies
+common-sense rules such as not placing parallel stripes on the same
+physical volume. The default for a logical volume is \fBinherit\fP
+which applies the same policy as for the volume group. These policies can
+be changed using \fBlvchange\fP (8) and \fBvgchange\fP (8) or over-ridden
+on the command line of any command that performs allocation.
+The \fBcontiguous\fP policy requires that new extents be placed adjacent
+to existing extents.
+The \fBcling\fP policy places new extents on the same physical
+volume as existing extents in the same stripe of the Logical Volume.
+If there are sufficient free extents to satisfy
+an allocation request but \fBnormal\fP doesn't use them,
+\fBanywhere\fP will - even if that reduces performance by
+placing two stripes on the same physical volume.
+.IP
+N.B. The policies described above are not implemented fully yet.
+In particular, contiguous free space cannot be broken up to
+satisfy allocation attempts.
+.SH ENVIRONMENT VARIABLES
+.TP
+\fBLVM_SYSTEM_DIR\fP
+Directory containing lvm.conf and other LVM
+system files.
+Defaults to "#DEFAULT_SYS_DIR#".
+.TP
+\fBHOME\fP
+Directory containing .lvm_history if the internal readline shell
+is invoked.
+.TP
+\fBLVM_VG_NAME\fP
+The volume group name that is assumed for
+any reference to a logical volume that doesn't specify a path.
+Not set by default.
+.SH VALID NAMES
+The following characters are valid for VG and LV names:
+\fBa-z A-Z 0-9 + _ . -\fP
+.LP
+VG and LV names cannot begin with a hyphen.
+There are also various reserved names that are used internally by lvm that can not be used as LV or VG names.
+A VG cannot be called anything that exists in /dev/ at the time of creation, nor can it be called '.' or '..'.
+A LV cannot be called '.' '..' 'snapshot' or 'pvmove'. The LV name may also not contain the strings '_mlog' or '_mimage'
+
+
+.SH DIAGNOSTICS
+All tools return a status code of zero on success or non-zero on failure.
+.SH FILES
+.I #DEFAULT_SYS_DIR#/lvm.conf
+.br
+.I $HOME/.lvm_history
+.SH SEE ALSO
+.BR clvmd (8),
+.BR lvchange (8),
+.BR lvcreate (8),
+.BR lvdisplay (8),
+.BR lvextend (8),
+.BR lvmchange (8),
+.BR lvmdiskscan (8),
+.BR lvreduce (8),
+.BR lvremove (8),
+.BR lvrename (8),
+.BR lvresize (8),
+.BR lvs (8),
+.BR lvscan (8),
+.BR pvchange (8),
+.BR pvck (8),
+.BR pvcreate (8),
+.BR pvdisplay (8),
+.BR pvmove (8),
+.BR pvremove (8),
+.BR pvs (8),
+.BR pvscan (8),
+.BR vgcfgbackup (8),
+.BR vgchange (8),
+.BR vgck (8),
+.BR vgconvert (8),
+.BR vgcreate (8),
+.BR vgdisplay (8),
+.BR vgextend (8),
+.BR vgimport (8),
+.BR vgimportclone (8),
+.BR vgmerge (8),
+.BR vgmknodes (8),
+.BR vgreduce (8),
+.BR vgremove (8),
+.BR vgrename (8),
+.BR vgs (8),
+.BR vgscan (8),
+.BR vgsplit (8),
+.BR readline (3),
+.BR lvm.conf (5)
+
--- /dev/null
+.TH LVM.CONF 5 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvm.conf \- Configuration file for LVM2
+.SH SYNOPSIS
+.B #DEFAULT_SYS_DIR#/lvm.conf
+.SH DESCRIPTION
+lvm.conf is loaded during the initialisation phase of
+\fBlvm\fP (8). This file can in turn lead to other files
+being loaded - settings read in later override earlier
+settings. File timestamps are checked between commands and if
+any have changed, all the files are reloaded.
+.LP
+Use \fBlvm dumpconfig\fP to check what settings are in use.
+.SH SYNTAX
+.LP
+This section describes the configuration file syntax.
+.LP
+Whitespace is not significant unless it is within quotes.
+This provides a wide choice of acceptable indentation styles.
+Comments begin with # and continue to the end of the line.
+They are treated as whitespace.
+.LP
+Here is an informal grammar:
+.TP
+\fBfile = value*\fP
+.br
+A configuration file consists of a set of values.
+.TP
+\fBvalue = section | assignment\fP
+.br
+A value can either be a new section, or an assignment.
+.TP
+\fBsection = identifier '{' value* '}'\fP
+.br
+A section is groups associated values together.
+.br
+It is denoted by a name and delimited by curly brackets.
+.br
+e.g. backup {
+.br
+ ...
+.br
+ }
+.TP
+\fBassignment = identifier '=' (array | type)\fP
+.br
+An assignment associates a type with an identifier.
+.br
+e.g. max_archives = 42
+.br
+.TP
+\fBarray = '[' (type ',')* type ']' | '[' ']'\fP
+.br
+Inhomogeneous arrays are supported.
+.br
+Elements must be separated by commas.
+.br
+An empty array is acceptable.
+.TP
+\fBtype = integer | float | string\fP
+\fBinteger = [0-9]*\fP
+.br
+\fBfloat = [0-9]*\.[0-9]*\fP
+.br
+\fBstring = '"' .* '"'\fP
+.IP
+Strings must be enclosed in double quotes.
+
+.SH SECTIONS
+.LP
+The sections that may be present in the file are:
+.TP
+\fBdevices\fP \(em Device settings
+.IP
+\fBdir\fP \(em Directory in which to create volume group device nodes.
+Defaults to "/dev". Commands also accept this as a prefix on volume
+group names.
+.IP
+\fBscan\fP \(em List of directories to scan recursively for
+LVM physical volumes.
+Devices in directories outside this hierarchy will be ignored.
+Defaults to "/dev".
+.IP
+\fBpreferred_names\fP \(em List of patterns compared in turn against
+all the pathnames referencing the same device in in the scanned directories.
+The pathname that matches the earliest pattern in the list is the
+one used in any output. As an example, if device-mapper multipathing
+is used, the following will select multipath device names:
+.br
+\fBdevices { preferred_names = [ "^/dev/mapper/mpath" ] }\fP
+.IP
+\fBfilter\fP \(em List of patterns to apply to devices found by a scan.
+Patterns are regular expressions delimited by any character and preceded
+by \fBa\fP (for accept) or \fBr\fP (for reject). The list is traversed
+in order, and the first regex that matches determines if the device
+will be accepted or rejected (ignored). Devices that don't match
+any patterns are accepted. If you want to reject patterns that
+don't match, end the list with "r/.*/".
+If there are several names for the same device (e.g. symbolic links
+in /dev), if any name matches any \fBa\fP pattern, the
+device is accepted; otherwise if any name matches any \fBr\fP
+pattern it is rejected; otherwise it is accepted.
+As an example, to ignore /dev/cdrom you could use:
+.br
+\fBdevices { filter=["r|cdrom|"] }\fP
+.IP
+\fBcache_dir\fP \(em Persistent filter cache file directory.
+Defaults to "#DEFAULT_CACHE_DIR#".
+.IP
+\fBwrite_cache_state\fP \(em Set to 0 to disable the writing out of the
+persistent filter cache file when \fBlvm\fP exits.
+Defaults to 1.
+.IP
+\fBtypes\fP \(em List of pairs of additional acceptable block device types
+found in /proc/devices together with maximum (non-zero) number of
+partitions (normally 16). By default, LVM2 supports ide, sd, md, loop,
+dasd, dac960, nbd, ida, cciss, ubd, ataraid, drbd, power2, i2o_block
+and iseries/vd. Block devices with major
+numbers of different types are ignored by LVM2.
+Example: \fBtypes = ["fd", 16]\fP.
+To create physical volumes on device-mapper volumes
+created outside LVM2, perhaps encrypted ones from \fBcryptsetup\fP,
+you'll need \fBtypes = ["device-mapper", 16]\fP. But if you do this,
+be careful to avoid recursion within LVM2. The figure for number
+of partitions is not currently used in LVM2 - and might never be.
+.IP
+\fBsysfs_scan\fP \(em If set to 1 and your kernel supports sysfs and
+it is mounted, sysfs will be used as a quick way of filtering out
+block devices that are not present.
+.IP
+\fBmd_component_detection\fP \(em If set to 1, LVM2 will ignore devices
+used as components of software RAID (md) devices by looking for md
+superblocks. This doesn't always work satisfactorily e.g. if a device
+has been reused without wiping the md superblocks first.
+.IP
+\fBmd_chunk_alignment\fP \(em If set to 1, and a Physical Volume is placed
+directly upon an md device, LVM2 will align its data blocks with the
+md device's stripe-width.
+.IP
+\fBdata_alignment_detection\fP \(em If set to 1, and your kernel provides
+topology information in sysfs for the Physical Volume, the start of data
+area will be aligned on a multiple of the ’minimum_io_size’ or
+’optimal_io_size’ exposed in sysfs. minimum_io_size is the smallest
+request the device can perform without incurring a read-modify-write
+penalty (e.g. MD's chunk size). optimal_io_size is the device's
+preferred unit of receiving I/O (e.g. MD's stripe width). minimum_io_size
+is used if optimal_io_size is undefined (0). If both \fBmd_chunk_alignment\fP
+and \fBdata_alignment_detection\fP are enabled the result of
+\fBdata_alignment_detection\fP is used.
+.IP
+\fBdata_alignment\fP \(em Default alignment (in KB) of start of data area
+when creating a new Physical Volume using the \fBlvm2\fP format.
+If a Physical Volume is placed directly upon an md device and
+\fBmd_chunk_alignment\fP or \fBdata_alignment_detection\fP is enabled
+this parameter is ignored. Set to 0 to use the default alignment of
+64KB or the page size, if larger.
+.IP
+\fBdata_alignment_offset_detection\fP \(em If set to 1, and your kernel
+provides topology information in sysfs for the Physical Volume, the
+start of the aligned data area of the Physical Volume will be shifted
+by the alignment_offset exposed in sysfs.
+.sp
+To see the location of the first Physical Extent of an existing Physical Volume
+use \fBpvs -o +pe_start\fP . It will be a multiple of the requested
+\fBdata_alignment\fP plus the alignment_offset from
+\fBdata_alignment_offset_detection\fP (if enabled) or the pvcreate
+commandline.
+.IP
+\fBdisable_after_error_count\fP \(em During each LVM operation errors received
+from each device are counted. If the counter of a particular device exceeds
+the limit set here, no further I/O is sent to that device for the remainder of
+the respective operation. Setting the parameter to 0 disables the counters
+altogether.
+.TP
+\fBallocation\fP \(em Space allocation policies
+.IP
+\fBcling_tag_list\fP \(em List of PV tags matched by the \fBcling\fP allocation policy.
+.IP
+When searching for free space to extend an LV, the \fBcling\fP
+allocation policy will choose space on the same PVs as the last
+segment of the existing LV. If there is insufficient space and a
+list of tags is defined here, it will check whether any of them are
+attached to the PVs concerned and then seek to match those PV tags
+between existing extents and new extents.
+.IP
+The @ prefix for tags is required.
+Use the special tag "@*" as a wildcard to match any PV tag and so use
+all PV tags for this purpose.
+.IP
+For example, LVs are mirrored between two sites within a single VG.
+PVs are tagged with either @site1 or @site2 to indicate where
+they are situated and these two PV tags are selected for use with this
+allocation policy:
+.IP
+cling_tag_list = [ "@site1", "@site2" ]
+.TP
+\fBlog\fP \(em Default log settings
+.IP
+\fBfile\fP \(em Location of log file. If this entry is not present, no
+log file is written.
+.IP
+\fBoverwrite\fP \(em Set to 1 to overwrite the log file each time a tool
+is invoked. By default tools append messages to the log file.
+.IP
+\fBlevel\fP \(em Log level (0-9) of messages to write to the file.
+9 is the most verbose; 0 should produce no output.
+.IP
+\fBverbose\fP \(em Default level (0-3) of messages sent to stdout or stderr.
+3 is the most verbose; 0 should produce the least output.
+.IP
+\fBsyslog\fP \(em Set to 1 (the default) to send log messages through syslog.
+Turn off by setting to 0. If you set to an integer greater than one,
+this is used - unvalidated - as the facility. The default is LOG_USER.
+See /usr/include/sys/syslog.h for safe facility values to use.
+For example, LOG_LOCAL0 might be 128.
+.IP
+\fBindent\fP \(em When set to 1 (the default) messages are indented
+according to their severity, two spaces per level.
+Set to 0 to turn off indentation.
+.IP
+\fBcommand_names\fP \(em When set to 1, the command name is used as a
+prefix for each message.
+Default is 0 (off).
+.IP
+\fBprefix\fP \(em Prefix used for all messages (after the command name).
+Default is two spaces.
+.IP
+\fBactivation\fP \(em Set to 1 to log messages while
+devices are suspended during activation.
+Only set this temporarily while debugging a problem because
+in low memory situations this setting can cause your machine to lock up.
+.TP
+\fBbackup\fP \(em Configuration for metadata backups.
+.IP
+\fBarchive_dir\fP \(em Directory used for automatic metadata archives.
+Backup copies of former metadata for each volume group are archived here.
+Defaults to "#DEFAULT_ARCHIVE_DIR#".
+.IP
+\fBbackup_dir\fP \(em Directory used for automatic metadata backups.
+A single backup copy of the current metadata for each volume group
+is stored here.
+Defaults to "#DEFAULT_BACKUP_DIR#".
+.IP
+\fBarchive\fP \(em Whether or not tools automatically archive existing
+metadata into \fBarchive_dir\fP before making changes to it.
+Default is 1 (automatic archives enabled).
+Set to 0 to disable.
+Disabling this might make metadata recovery difficult or impossible
+if something goes wrong.
+.IP
+\fBbackup\fP \(em Whether or not tools make an automatic backup
+into \fBbackup_dir\fP after changing metadata.
+Default is 1 (automatic backups enabled). Set to 0 to disable.
+Disabling this might make metadata recovery difficult or impossible
+if something goes wrong.
+.IP
+\fBretain_min\fP \(em Minimum number of archives to keep.
+Defaults to 10.
+.IP
+\fBretain_days\fP \(em Minimum number of days to keep archive files.
+Defaults to 30.
+.TP
+\fBshell\fP \(em LVM2 built-in readline shell settings
+.IP
+\fBhistory_size\fP \(em Maximum number of lines of shell history to retain (default 100) in $HOME/.lvm_history
+.TP
+\fBglobal\fP \(em Global settings
+.IP
+\fBtest\fP \(em If set to 1, run tools in test mode i.e. no changes to
+the on-disk metadata will get made. It's equivalent to having the
+-t option on every command.
+.IP
+\fBactivation\fP \(em Set to 0 to turn off all communication with
+the device-mapper driver. Useful if you want to manipulate logical
+volumes while device-mapper is not present in your kernel.
+.IP
+\fBproc\fP \(em Mount point of proc filesystem.
+Defaults to /proc.
+.IP
+\fBumask\fP \(em File creation mask for any files and directories created.
+Interpreted as octal if the first digit is zero.
+Defaults to 077.
+Use 022 to allow other users to read the files by default.
+.IP
+\fBformat\fP \(em The default value of \fB--metadatatype\fP used
+to determine which format of metadata to use when creating new
+physical volumes and volume groups. \fBlvm1\fP or \fBlvm2\fP.
+.IP
+\fBfallback_to_lvm1\fP \(em Set this to 1 if you need to
+be able to switch between 2.4 kernels using LVM1 and kernels
+including device-mapper.
+The LVM2 tools should be installed as normal and
+the LVM1 tools should be installed with a .lvm1 suffix e.g.
+vgscan.lvm1.
+If an LVM2 tool is then run but unable to communicate
+with device-mapper, it will automatically invoke the equivalent LVM1
+version of the tool. Note that for LVM1 tools to
+manipulate physical volumes and volume groups created by LVM2 you
+must use \fB--metadataformat lvm1\fP when creating them.
+.IP
+\fBlibrary_dir\fP \(em A directory searched for LVM2's shared libraries
+ahead of the places \fBdlopen\fP (3) searches.
+.IP
+\fBformat_libraries\fP \(em A list of shared libraries to load that contain
+code to process different formats of metadata. For example, liblvm2formatpool.so
+is needed to read GFS pool metadata if LVM2 was configured \fB--with-pool=shared\fP.
+.IP
+\fBlocking_type\fP \(em What type of locking to use.
+1 is the default, which use flocks on files in \fBlocking_dir\fP
+(see below) to
+avoid conflicting LVM2 commands running concurrently on a single
+machine. 0 disables locking and risks corrupting your metadata.
+If set to 2, the tools will load the external \fBlocking_library\fP
+(see below).
+If the tools were configured \fB--with-cluster=internal\fP
+(the default) then 3 means to use built-in cluster-wide locking.
+Type 4 enforces read-only metadata and forbids any operations that
+might want to modify Volume Group metadata.
+All changes to logical volumes and their states are communicated
+using locks.
+.IP
+\fBwait_for_locks\fP \(em When set to 1, the default, the tools
+wait if a lock request cannot be satisfied immediately.
+When set to 0, the operation is aborted instead.
+.IP
+\fBlocking_dir\fP \(em The directory LVM2 places its file locks
+if \fBlocking_type\fP is set to 1. The default is \fB/var/lock/lvm\fP.
+.IP
+\fBlocking_library\fP \(em The name of the external locking
+library to load if \fBlocking_type\fP is set to 2.
+The default is \fBliblvm2clusterlock.so\fP. If you need to write
+such a library, look at the lib/locking source code directory.
+.TP
+\fBtags\fP \(em Host tag settings
+.IP
+\fBhosttags\fP \(em If set to 1, create a host tag with the machine name.
+Setting this to 0 does nothing, neither creating nor destroying any tag.
+The machine name used is the nodename as returned by \fBuname\fP (2).
+.IP
+Additional host tags to be set can be listed here as subsections.
+The @ prefix for tags is optional.
+Each of these host tag subsections can contain a \fBhost_list\fP
+array of host names. If any one of these entries matches the machine
+name exactly then the host tag gets defined on this particular host,
+otherwise it doesn't.
+.IP
+After lvm.conf has been processed, LVM2 works through each host
+tag that has been defined in turn, and if there is a configuration
+file called lvm_\fB<host_tag>\fP.conf it attempts to load it.
+Any settings read in override settings found in earlier files.
+Any additional host tags defined get appended to the search list,
+so in turn they can lead to further configuration files being processed.
+Use \fBlvm dumpconfig\fP to check the result of config
+file processing.
+.IP
+The following example always sets host tags \fBtag1\fP and
+sets \fBtag2\fP on machines fs1 and fs2:
+.IP
+tags { tag1 { } tag2 { host_list = [ "fs1", "fs2" ] } }
+.IP
+These options are useful if you are replicating configuration files
+around a cluster. Use of \fBhosttags = 1\fP means every machine
+can have static and identical local configuration files yet use
+different settings and activate different logical volumes by
+default. See also \fBvolume_list\fP below and \fB--addtag\fP
+in \fBlvm\fP (8).
+.TP
+\fBactivation\fP \(em Settings affecting device-mapper activation
+.IP
+\fBmissing_stripe_filler\fP \(em When activating an incomplete logical
+volume in partial mode, this option dictates how the missing data is
+replaced. A value of "error" will cause activation to create error
+mappings for the missing data, meaning that read access to missing
+portions of the volume will result in I/O errors. You can instead also
+use a device path, and in that case this device will be used in place of
+missing stripes. However, note that using anything other than
+"error" with mirrored or snapshotted volumes is likely to result in data
+corruption. For instructions on how to create a device that always
+returns zeros, see \fBlvcreate\fP (8).
+.IP
+\fBmirror_region_size\fP \(em Unit size in KB for copy operations
+when mirroring.
+.IP
+\fBreadahead\fP \(em Used when there is no readahead value stored
+in the volume group metadata. Set to \fBnone\fP to disable
+readahead in these circumstances or \fBauto\fP to use the default
+value chosen by the kernel.
+.IP
+\fBreserved_memory\fP, \fBreserved_stack\fP \(em How many KB to reserve
+for LVM2 to use while logical volumes are suspended. If insufficient
+memory is reserved before suspension, there is a risk of machine deadlock.
+.IP
+\fBprocess_priority\fP \(em The nice value to use while devices are
+suspended. This is set to a high priority so that logical volumes
+are suspended (with I/O generated by other processes to those
+logical volumes getting queued) for the shortest possible time.
+.IP
+\fBvolume_list\fP \(em This acts as a filter through which
+all requests to activate a logical volume on this machine
+are passed. A logical volume is only activated if it matches
+an item in the list. Tags must be preceded by @ and are checked
+against all tags defined in the logical volume and volume group
+metadata for a match.
+@* is short-hand to check every tag set on the host machine (see
+\fBtags\fP above).
+Logical volume and volume groups can also be included in the list
+by name e.g. vg00, vg00/lvol1.
+.TP
+\fBmetadata\fP \(em Advanced metadata settings
+.IP
+\fBpvmetadatacopies\fP \(em When creating a physical volume using the
+LVM2 metadata format, this is the default number of copies of metadata
+to store on each physical volume.
+Currently it can be set to 0, 1 or 2. The default is 1.
+If set to 2, one copy is placed at the beginning of the disk
+and the other is placed at the end.
+It can be overridden on the command line with \fB--pvmetadatacopies\fP
+(see \fBpvcreate\fP).
+If creating a volume group with just one physical volume, it's a
+good idea to have 2 copies. If creating a large volume group with
+many physical volumes, you may decide that 3 copies of the metadata
+is sufficient, i.e. setting it to 1 on three of the physical volumes,
+and 0 on the rest. Every volume group must contain at least one
+physical volume with at least 1 copy of the metadata (unless using
+the text files described below). The disadvantage of having lots
+of copies is that every time the tools access the volume group, every
+copy of the metadata has to be accessed, and this slows down the
+tools.
+.IP
+\fBpvmetadatasize\fP \(em Approximate number of sectors to set aside
+for each copy of the metadata. Volume groups with large numbers of
+physical or logical volumes, or volumes groups containing complex
+logical volume structures will need additional space for their metadata.
+The metadata areas are treated as circular buffers, so
+unused space becomes filled with an archive of the most recent
+previous versions of the metadata.
+.IP
+\fBpvmetadataignore\fP When creating a physical volume using the LVM2
+metadata format, this states whether metadata areas should be ignored.
+The default is "n". If metadata areas on a physical volume are ignored,
+LVM will not not store metadata in the metadata areas present on newly
+created Physical Volumes. The option can be overridden on the command
+line with \fB--metadataignore\fP (See \fBpvcreate\fP and \fBpvchange\fP).
+Metadata areas cannot be created or extended after Logical Volumes have
+been allocated on the device.
+If you do not want to store metadata on this device, it is still wise
+always to allocate a metadata area (use a non-zero value for
+\fB--pvmetadatacopies\fP) in case you need it in the future and to use
+this option to instruct LVM2 to ignore it.
+.IP
+\fBvgmetadatacopies\fP \(em When creating a volume group using the
+LVM2 metadata format, this is the default number of copies of metadata
+desired across all the physical volumes in the volume group. If set to
+a non-zero value, LVM will automatically set or clear the metadataignore
+flag on the physical volumes (see \fBpvcreate\fP and \fBpvchange\fP
+\fB--metadataignore\fP) in order to achieve the desired number of metadata
+copies. An LVM command that adds or removes physical volumes (for example,
+\fBvgextend\fP, \fBvgreduce\fP, \fBvgsplit\fP, or \fBvgmerge\fP), may cause
+LVM to automatically set or clear the metadataignore flags. Also, if
+physical volumes go missing or reappear, or a new number of copies is
+explicitly set (see \fBvgchange --vgmetadatacopies\fP), LVM may adjust
+the metadataignore flags.
+Set \fBvgmetadatacopies\fP to 0 instructs LVM not to set or clear the
+metadataignore flags automatically. You may set a value larger than the
+sum of all metadata areas on all physical volumes. The value can
+be overridden on the command line with \fB--vgmetadatacopies\fP for various
+commands (for example, \fBvgcreate\fP and \fBvgchange\fP), and can be
+queryied with the \fBvg_mda_copies\fP field of \fBvgs\fP. This option
+is useful for volume groups containing large numbers of physical volumes
+with metadata as it may be used to minimize metadata read and write overhead.
+.IP
+\fBdirs\fP \(em List of directories holding live copies of LVM2
+metadata as text files. These directories must not be on logical
+volumes. It is possible to use LVM2 with a couple of directories
+here, preferably on different (non-logical-volume) filesystems
+and with no other on-disk metadata, \fBpvmetadatacopies = 0\fP.
+Alternatively these directories can be in addition to the
+on-disk metadata areas. This feature was created during the
+development of the LVM2 metadata before the new on-disk metadata
+areas were designed and no longer gets tested.
+It is not supported under low-memory conditions, and it is
+important never to edit these metadata files unless you fully
+understand how things work: to make changes you should always use
+the tools as normal, or else vgcfgbackup, edit backup, vgcfgrestore.
+.SH FILES
+.I #DEFAULT_SYS_DIR#/lvm.conf
+.I #DEFAULT_ARCHIVE_DIR#
+.I #DEFAULT_BACKUP_DIR#
+.I #DEFAULT_CACHE_DIR#/.cache
+.I #DEFAULT_LOCK_DIR#
+.SH SEE ALSO
+.BR lvm (8),
+.BR umask (2),
+.BR uname (2),
+.BR dlopen (3),
+.BR syslog (3),
+.BR syslog.conf (5)
--- /dev/null
+.TH LVMCHANGE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvmchange \- change attributes of the logical volume manager
+.SH SYNOPSIS
+.B lvmchange
+.SH DESCRIPTION
+lvmchange is not currently supported under LVM2, although
+\fBdmsetup (8)\fP has a \fBremove_all\fP command.
+.SH SEE ALSO
+.BR dmsetup (8)
--- /dev/null
+.TH "LVMCONF" "8" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
+
+.SH "NAME"
+.B lvmconf
+\- LVM configuration modifier
+
+.SH "SYNOPSIS"
+.B lvmconf
+[\-\-disable-cluster]
+[\-\-enable-cluster]
+[\-\-file <configfile>]
+[\-\-lockinglib <lib>]
+[\-\-lockinglibdir <dir>]
+
+.SH "DESCRIPTION"
+.B lvmconf
+is a script that modifies the locking configuration in an lvm configuration file. See \fBlvm.conf\fP(5).
+
+.SH "OPTIONS"
+.TP
+.BR \-\-disable-cluster
+Set \fBlocking_type\fR to the default non-clustered type.
+.TP
+.BR \-\-enable-cluster
+Set \fBlocking_type\fR to the default clustered type on this system.
+.TP
+.BR \-\-file " " \fI<configfile>\fR
+Apply the changes to \fBconfigfile\fR instead of the default \fB#DEFAULT_SYS_DIR#/lvm.conf\fR.
+.TP
+.BR \-\-lockinglib " " \fI<lib>\fR
+Set external \fBlocking_library\fR locking library to load if an external locking type is used.
+.TP
+.BR \-\-lockinglibdir " " \fI<dir>\fR
+.SH FILES
+.I #DEFAULT_SYS_DIR#/lvm.conf
+
+.SH "SEE ALSO"
+.BR lvm (8),
+.BR lvm.conf (5)
--- /dev/null
+.TH LVMDISKSCAN 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvmdiskscan \- scan for all devices visible to LVM2
+.SH SYNOPSIS
+.B lvmdiskscan
+[\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-l|\-\-lvmpartition]
+[\-v|\-\-verbose]
+.SH DESCRIPTION
+\fBlvmdiskscan\fP scans all SCSI, (E)IDE disks, multiple devices and a bunch
+of other block devices in the system looking for LVM physical volumes.
+The size reported is the real device size.
+Define a filter in \fBlvm.conf\fP(5) to restrict
+the scan to avoid a CD ROM, for example.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-l, \-\-lvmpartition
+Only reports Physical Volumes.
+.SH SEE ALSO
+.BR lvm (8),
+.BR lvm.conf (5),
+.BR pvscan (8),
+.BR vgscan (8)
--- /dev/null
+.TH LVMDUMP 8 "LVM TOOLS #VERSION#" "Red Hat, Inc."
+.SH NAME
+lvmdump - create lvm2 information dumps for diagnostic purposes
+.SH SYNOPSIS
+\fBlvmdump\fP [options] [-d directory]
+.SH DESCRIPTION
+\fBlvmdump\fP is a tool to dump various information concerning LVM2. By default, it creates a tarball suitable for submission along with a problem report.
+.PP
+The content of the tarball is as follows:
+.br
+- dmsetup info
+.br
+- table of currently running processes
+.br
+- recent entries from /var/log/messages (containing system messages)
+.br
+- complete lvm configuration and cache (content of /etc/lvm)
+.br
+- list of device nodes present under /dev
+.br
+- list of files present /sys/block
+.br
+- list of files present /sys/devices/virtual/block
+.br
+- if enabled with -m, metadata dump will be also included
+.br
+- if enabled with -a, debug output of vgscan, pvscan and list of all available volume groups, physical volumes and logical volumes will be included
+.br
+- if enabled with -c, cluster status info
+.SH OPTIONS
+.TP
+\fB\-h\fR \(em print help message
+.TP
+\fB\-a\fR \(em advanced collection
+\fBWARNING\fR: if lvm is already hung, then this script may hang as well if \fB\-a\fR is used
+.TP
+\fB\-m\fR \(em gather LVM metadata from the PVs
+This option generates a 1:1 dump of the metadata area from all PVs visible to the system, which can cause the dump to increase in size considerably. However, the metadata dump may represent a valuable diagnostic resource.
+.TP
+\fB\-d\fR directory \(em dump into a directory instead of tarball
+By default, lvmdump will produce a single compressed tarball containing all the information. Using this option, it can be instructed to only produce the raw dump tree, rooted in \fBdirectory\fP.
+.TP
+\fB\-c\fR \(em if clvmd is running, gather cluster data as well
+.SH ENVIRONMENT VARIABLES
+.TP
+\fBLVM_BINARY\fP
+The LVM2 binary to use.
+Defaults to "lvm".
+Sometimes you might need to set this to "/sbin/lvm.static", for example.
+.TP
+\fBDMSETUP_BINARY\fP
+The dmsetup binary to use.
+Defaults to "dmsetup".
+.PP
--- /dev/null
+.TH "LVMSADC" "8" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
+
+.SH "NAME"
+lvmsadc \- LVM system activity data collector
+
+.SH "SYNOPSIS"
+.B lvmsadc
+
+.SH "DESCRIPTION"
+.B lvmsadc
+is not currently supported under LVM2.
+
+.SH "SEE ALSO"
+.BR lvm (8)
--- /dev/null
+.TH "LVMSAR" "8" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
+
+.SH "NAME"
+lvmsar \- LVM system activity reporter
+
+.SH "SYNOPSIS"
+.B lvmsar
+
+.SH "DESCRIPTION"
+.B lvmsar
+is not currently supported under LVM2.
+
+.SH "SEE ALSO"
+.BR lvm (8)
--- /dev/null
+.TH LVREDUCE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvreduce \- reduce the size of a logical volume
+.SH SYNOPSIS
+.B lvreduce
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-f|\-\-force]
+[\-h|\-?|\-\-help]
+[\-\-noudevsync]
+{\-l|\-\-extents [\-]LogicalExtentsNumber[%{VG|LV|FREE|ORIGIN}] |
+\-L|\-\-size [\-]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}
+[\-n|\-\-nofsck]
+[\-r|\-\-resizefs]
+[\-t|\-\-test]
+[\-v|\-\-verbose] LogicalVolume[Path]
+.SH DESCRIPTION
+lvreduce allows you to reduce the size of a logical volume.
+Be careful when reducing a logical volume's size, because data in the
+reduced part is lost!!!
+.br
+You should therefore ensure that any filesystem on the volume is
+resized
+.I before
+running lvreduce so that the extents that are to be removed are not in use.
+.br
+Shrinking snapshot logical volumes (see
+.B lvcreate(8)
+for information to create snapshots) is supported as well.
+But to change the number of copies in a mirrored logical
+volume use
+.B lvconvert (8).
+.br
+Sizes will be rounded if necessary - for example, the volume size must
+be an exact number of extents and the size of a striped segment must
+be a multiple of the number of stripes.
+.br
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-f, \-\-force
+Force size reduction without prompting even when it may cause data loss.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background. You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.I \-l, \-\-extents [\-]LogicalExtentsNumber[%{VG|LV|FREE|ORIGIN}]
+Reduce or set the logical volume size in units of logical extents.
+With the - sign the value will be subtracted from
+the logical volume's actual size and without it the value will be taken
+as an absolute size.
+The number can also be expressed as a percentage of the total space
+in the Volume Group with the suffix %VG, relative to the existing
+size of the Logical Volume with the suffix %LV, as a percentage of the
+remaining free space in the Volume Group with the suffix %FREE, or (for
+a snapshot) as a percentage of the total space in the Origin Logical
+Volume with the suffix %ORIGIN.
+.TP
+.I \-L, \-\-size [\-]LogicalVolumeSize[bBsSkKmMgGtTpPeE]
+Reduce or set the logical volume size in units of megabytes.
+A size suffix of k for kilobyte, m for megabyte,
+g for gigabytes, t for terabytes, p for petabytes
+or e for exabytes is optional.
+With the - sign the value will be subtracted from
+the logical volume's actual size and without it it will be taken as
+an absolute size.
+.TP
+.I \-n, \-\-nofsck
+Do not perform fsck before resizing filesystem when filesystem
+requires it. You may need to use \fB--force\fR to proceed with
+this option.
+.TP
+.I \-r, \-\-resizefs
+Resize underlying filesystem together with the logical volume using
+\fBfsadm\fR(8).
+.SH Example
+"lvreduce -l -3 vg00/lvol1" reduces the size of logical volume lvol1
+in volume group vg00 by 3 logical extents.
+.SH SEE ALSO
+.BR fsadm (8),
+.BR lvchange (8),
+.BR lvconvert (8),
+.BR lvcreate (8),
+.BR lvextend (8),
+.BR lvm (8),
+.BR lvresize (8),
+.BR vgreduce (8)
--- /dev/null
+.TH LVREMOVE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvremove \- remove a logical volume
+.SH SYNOPSIS
+.B lvremove
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-f|\-\-force]
+[\-h|\-?|\-\-help]
+[\-\-noudevsync]
+[\-t|\-\-test]
+[\-v|\-\-verbose] LogicalVolumePath [LogicalVolumePath...]
+.SH DESCRIPTION
+\fBlvremove\fP removes one or more logical volumes.
+Confirmation will be requested before deactivating any active logical
+volume prior to removal. Logical volumes cannot be deactivated
+or removed while they are open (e.g. if they contain a mounted filesystem).
+Removing an origin logical volume will also remove all dependent snapshots.
+.sp
+If the logical volume is clustered then it must be deactivated on all
+nodes in the cluster before it can be removed. A single lvchange command
+issued from one node can do this.
+.SH OPTIONS
+See \fBlvm\fP(8) for common options.
+.TP
+.I \-f, \-\-force
+Remove active logical volumes without confirmation.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background. You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.SH EXAMPLES
+Remove the active logical volume lvol1 in volume group vg00
+without asking for confirmation:
+.sp
+\ \fBlvremove -f vg00/lvol1\fP
+.sp
+Remove all logical volumes in volume group vg00:
+.sp
+\ \fBlvremove vg00\fP
+.SH SEE ALSO
+.BR lvcreate (8),
+.BR lvdisplay (8),
+.BR lvchange (8),
+.BR lvm (8),
+.BR lvs (8),
+.BR lvscan (8),
+.BR vgremove (8)
--- /dev/null
+.TH LVRENAME 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvrename \- rename a logical volume
+.SH SYNOPSIS
+.B lvrename
+.RB [ \-A | \-\-autobackup " {" y | n }]
+.RB [ \-d | \-\-debug ]
+.RB [ \-f | \-\-force ]
+.RB [ \-h | \-\-help ]
+.RB [ \-\-noudevsync ]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+.RB [ \-\-version ]
+.TP
+.IR "OldLogicalVolumePath NewLogicalVolume" { Path | Name }
+.TP
+.I VolumeGroupName OldLogicalVolumeName NewLogicalVolumeName
+.SH DESCRIPTION
+.B lvrename
+renames an existing logical volume from
+.IR OldLogicalVolume { Name | Path }
+to
+.IR NewLogicalVolume { Name | Path }.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background. You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.SH EXAMPLE
+To rename
+.B lvold
+in volume group
+.B vg02
+to
+.BR lvnew :
+.nf
+
+\ lvrename /dev/vg02/lvold /dev/vg02/lvnew
+
+.fi
+An alternate syntax to rename this logical volume is
+.nf
+
+\ lvrename vg02 lvold lvnew
+
+.fi
+.SH SEE ALSO
+.BR lvm (8),
+.BR lvchange (8),
+.BR vgcreate (8),
+.BR vgrename (8)
--- /dev/null
+.TH LVRESIZE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvresize \- resize a logical volume
+.SH SYNOPSIS
+.B lvresize
+[\-\-alloc AllocationPolicy]
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-noudevsync]
+[\-i|\-\-stripes Stripes [\-I|\-\-stripesize StripeSize]]
+{\-l|\-\-extents [+]LogicalExtentsNumber[%{VG|LV|PVS|FREE|ORIGIN}] |
+\-L|\-\-size [+]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}
+[\-f|\-\-force]
+[\-n|\-\-nofsck]
+[\-r|\-\-resizefs]
+[\-t|\-\-test]
+[\-v|\-\-verbose] LogicalVolumePath [PhysicalVolumePath[:PE[-PE]]...]
+.SH DESCRIPTION
+lvresize allows you to resize a logical volume.
+Be careful when reducing a logical volume's size, because data in the reduced
+part is lost!!!
+You should therefore ensure that any filesystem on the volume is
+shrunk first so that the extents that are to be removed are not in use.
+Resizing snapshot logical volumes (see
+.B lvcreate(8)
+for information about creating snapshots) is supported as well.
+But to change the number of copies in a mirrored logical
+volume use
+.BR lvconvert (8).
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-f, \-\-force
+Force resize without prompting even when it may cause data loss.
+.TP
+.I \-l, \-\-extents [+|-]LogicalExtentsNumber[%{VG|LV|PVS|FREE|ORIGIN}]
+Change or set the logical volume size in units of logical extents.
+With the + or - sign the value is added to or subtracted from the actual size
+of the logical volume and without it, the value is taken as an absolute one.
+The number can also be expressed as a percentage of the total space
+in the Volume Group with the suffix %VG, relative to the existing
+size of the Logical Volume with the suffix %LV, as a percentage of
+the remaining free space of the PhysicalVolumes on the command line with the
+suffix %PVS, as a percentage of the remaining free space in the
+Volume Group with the suffix %FREE, or (for a snapshot) as a percentage
+of the total space in the Origin Logical Volume with the suffix %ORIGIN.
+.TP
+.I \-n, \-\-nofsck
+Do not perform fsck before resizing filesystem when filesystem
+requires it. You may need to use \fB--force\fR to proceed with
+this option.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background. You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.I \-r, \-\-resizefs
+Resize underlying filesystem together with the logical volume using
+\fBfsadm\fR(8).
+.TP
+.I \-L, \-\-size [+|-]LogicalVolumeSize[bBsSkKmMgGtTpPeE]
+Change or set the logical volume size in units of megabytes.
+A size suffix of M for megabytes,
+G for gigabytes, T for terabytes, P for petabytes
+or E for exabytes is optional.
+With the + or - sign the value is added to or subtracted from
+the actual size of the logical volume and without it, the value is taken as an
+absolute one.
+.TP
+.I \-i, \-\-stripes Stripes
+Gives the number of stripes to use when extending a Logical Volume.
+Defaults to whatever the last segment of the Logical Volume uses.
+Not applicable to LVs using the original metadata LVM format, which must
+use a single value throughout.
+.TP
+.I \-I, \-\-stripesize StripeSize
+Gives the number of kilobytes for the granularity of the stripes.
+Defaults to whatever the last segment of the Logical Volume uses.
+Not applicable to LVs using the original metadata LVM format, which
+must use a single value throughout.
+.br
+StripeSize must be 2^n (n = 2 to 9)
+.SH Examples
+.br
+"lvresize -L+16M vg1/lv1 /dev/sda:0-1 /dev/sdb:0-1"
+.br
+tries to extend a logical volume "vg1/lv1" by 16MB using physical extents
+/dev/sda:0-1 and /dev/sdb:0-1 for allocation of extents.
+
+.SH SEE ALSO
+.BR fsadm (8),
+.BR lvm (8),
+.BR lvconvert (8),
+.BR lvcreate (8),
+.BR lvreduce (8),
+.BR lvchange (8)
--- /dev/null
+.TH LVS 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvs \- report information about logical volumes
+.SH SYNOPSIS
+.B lvs
+[\-a|\-\-all]
+[\-\-aligned] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure] [\-\-nameprefixes] [\-\-noheadings] [\-\-nosuffix]
+[\-o|\-\-options [+]Field[,Field]]
+[\-O|\-\-sort [+|-]Key1[,[+|-]Key2[,...]]]
+[\-P|\-\-partial] [\-\-rows] [\-\-segments]
+[\-\-separator Separator]
+[\-\-unbuffered]
+[\-\-units hHbBsSkKmMgGtTpPeE]
+[\-\-unquoted]
+[\-v|\-\-verbose]
+[\-\-version] [VolumeGroupName [VolumeGroupName...]]
+.SH DESCRIPTION
+lvs produces formatted output about logical volumes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-\-all
+Include information in the output about internal Logical Volumes that
+are components of normally-accessible Logical Volumes, such as mirrors,
+but which are not independently accessible (e.g. not mountable).
+The names of such Logical Volumes are enclosed within square brackets
+in the output. For example, after creating a mirror using 'lvcreate -m1
+--mirrorlog disk', this option will reveal three internal Logical
+Volumes, with suffixes mimage_0, mimage_1, and mlog.
+.TP
+.I \-\-aligned
+Use with \-\-separator to align the output columns.
+.TP
+.I \-\-nameprefixes
+Add an "LVM2_" prefix plus the field name to the output. Useful
+with --noheadings to produce a list of field=value pairs that can
+be used to set environment variables (for example, in \fBudev (7)\fP rules).
+.TP
+.I \-\-noheadings
+Suppress the headings line that is normally the first line of output.
+Useful if grepping the output.
+.TP
+.I \-\-nosuffix
+Suppress the suffix on output sizes. Use with \-\-units (except h and H)
+if processing the output.
+.TP
+.I \-o, \-\-options
+Comma-separated ordered list of columns. Precede the list with '+' to append
+to the default selection of columns instead of replacing it.
+.IP
+Use \fb-o lv_all\fP to select all logical volume columns, and \fb-o seg_all\fP
+to select all logical volume segment columns.
+.IP
+Use \fb-o help\fP to view the full list of columns available.
+.IP
+Column names include:
+lv_uuid, lv_name, lv_path, lv_attr, lv_major, lv_minor, lv_read_ahead, lv_kernel_major,
+lv_kernel_minor, lv_kernel_read_ahead, lv_size, seg_count, origin, origin_size,
+snap_percent, copy_percent, move_pv, convert_lv, lv_tags, mirror_log, modules,
+segtype, stripes, stripesize, regionsize, chunksize, seg_start, seg_start_pe,
+seg_size, seg_tags, seg_pe_ranges, devices.
+.IP
+With \-\-segments, any "seg_" prefixes are optional; otherwise any "lv_"
+prefixes are optional. Columns mentioned in \fBvgs (8)\fP
+can also be chosen.
+.IP
+The lv_attr bits are:
+.RS
+.IP 1 3
+Volume type: (m)irrored, (M)irrored without initial sync, (o)rigin,
+(O)rigin with merging snapshot, (s)napshot, merging (S)napshot, (p)vmove,
+(v)irtual, mirror (i)mage, mirror (I)mage out-of-sync, under (c)onversion
+.IP 2 3
+Permissions: (w)riteable, (r)ead-only
+.IP 3 3
+Allocation policy: (c)ontiguous, c(l)ing, (n)ormal, (a)nywhere, (i)nherited
+This is capitalised if the volume is currently locked against allocation
+changes, for example during \fBpvmove\fP (8).
+.IP 4 3
+fixed (m)inor
+.IP 5 3
+State: (a)ctive, (s)uspended, (I)nvalid snapshot, invalid (S)uspended snapshot,
+mapped (d)evice present without tables, mapped device present with (i)nactive table
+.IP 6 3
+device (o)pen
+.RE
+.TP
+.I \-\-segments
+Use default columns that emphasize segment information.
+.TP
+.I \-O, \-\-sort
+Comma-separated ordered list of columns to sort by. Replaces the default
+selection. Precede any column with - for a reverse sort on that column.
+.TP
+.I \-\-rows
+Output columns as rows.
+.TP
+.I \-\-separator Separator
+String to use to separate each column. Useful if grepping the output.
+.TP
+.I \-\-unbuffered
+Produce output immediately without sorting or aligning the columns properly.
+.TP
+.I \-\-units hHbBsSkKmMgGtTpPeE
+All sizes are output in these units: (h)uman-readable, (b)ytes, (s)ectors,
+(k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes, (p)etabytes, (e)xabytes.
+Capitalise to use multiples of 1000 (S.I.) instead of 1024. Can also specify
+custom units e.g. \-\-units 3M
+.TP
+.I \-\-unquoted
+When used with --nameprefixes, output values in the field=value pairs are not quoted.
+.SH SEE ALSO
+.BR lvm (8),
+.BR lvdisplay (8),
+.BR pvs (8),
+.BR vgs (8)
--- /dev/null
+.TH LVSCAN 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+lvscan \- scan (all disks) for logical volumes
+.SH SYNOPSIS
+.B lvscan
+.RB [ \-a | \-\-all]
+.RB [ \-b | \-\-blockdevice ]
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-\-ignorelockingfailure ]
+.RB [ \-P | \-\-partial ]
+.RB [ \-v | \-\-verbose ]
+.SH DESCRIPTION
+.B lvscan
+scans all known volume groups or all supported LVM block devices
+in the system for defined logical volumes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-\-all
+Include information in the output about internal Logical Volumes that
+are components of normally-accessible Logical Volumes, such as mirrors,
+but which are not independently accessible (e.g. not mountable).
+For example, after creating a mirror using 'lvcreate -m1 --mirrorlog disk',
+this option will reveal three internal Logical Volumes, with suffixes
+mimage_0, mimage_1, and mlog.
+.TP
+.BR \-b ", " \-\-blockdevice
+Adds the device major and minor numbers to the display
+of each logical volume.
+.SH SEE ALSO
+.BR lvm (8),
+.BR lvcreate (8),
+.BR lvdisplay (8)
--- /dev/null
+.TH PVCHANGE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvchange \- change attributes of a physical volume
+.SH SYNOPSIS
+.B pvchange
+[\-\-addtag Tag]
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug]
+[\-f|\-\-force]
+[\-\-deltag Tag]
+[\-\-metadataignore y|n]
+[\-h|\-?|\-\-help]
+[\-t|\-\-test]
+[\-v|\-\-verbose] [\-a|\-\-all] [\-x|\-\-allocatable y|n]
+[\-u|\-\-uuid] [PhysicalVolumePath...]
+.SH DESCRIPTION
+pvchange allows you to change the allocation permissions of one or
+more physical volumes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-a, \-\-all
+If PhysicalVolumePath is not specified on the command line all
+physical volumes are searched for and used.
+.TP
+.I \-\-metadataignore " y|n"
+Ignore or un-ignore metadata areas on this physical volume.
+If metadata areas on a physical volume are ignored, LVM will
+not not store metadata in the metadata areas present on this Physical
+Volume.
+.TP
+.I \-u, \-\-uuid
+Generate new random UUID for specified physical volumes.
+.TP
+.I \-x, \-\-allocatable y|n
+Enable or disable allocation of physical extents on this physical volume.
+.SH Example
+"pvchange -x n /dev/sdk1" disallows the allocation of physical extents
+on this physical volume (possibly because of disk errors, or because it will
+be removed after freeing it.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvcreate (8)
--- /dev/null
+.TH PVCK 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvck \- check physical volume metadata
+.SH SYNOPSIS
+.B pvck
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-v | \-\-verbose ]
+.RB [ \-\-labelsector ]
+.IR PhysicalVolume " [" PhysicalVolume ...]
+.SH DESCRIPTION
+pvck checks physical volume LVM metadata for consistency.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-\-labelsector " sector"
+By default, 4 sectors of \fBPhysicalVolume\fP are scanned for an LVM label,
+starting at sector 0. This parameter allows you to specify a different
+starting sector for the scan and is useful for recovery situations. For
+example, suppose the partition table is corrupted or lost on /dev/sda,
+but you suspect there was an LVM partition at approximately 100 MB. This
+area of the disk may be scanned by using the \fB--labelsector\fP parameter
+with a value of 204800 (100 * 1024 * 1024 / 512 = 204800):
+.sp
+.BI "pvck --labelsector 204800 /dev/sda"
+.sp
+Note that a script can be used with \fB--labelsector\fP to automate the
+process of finding LVM labels.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvcreate (8),
+.BR pvscan (8)
+.BR vgck (8)
--- /dev/null
+.TH PVCREATE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvcreate \- initialize a disk or partition for use by LVM
+.SH SYNOPSIS
+.B pvcreate
+.RB [ \-d | \-\-debug ]
+.RB [ \-f [ f ]| \-\-force " [" \-\-force ]]
+.RB [ \-y | \-\-yes ]
+.RB [ \-h | \-\-help ]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+.RB [ \-\-labelsector ]
+.RB [ \-M | \-\-metadatatype type ]
+.RB [ \-\-[pv]metadatacopies #copies ]
+.RB [ \-\-metadatasize size ]
+.RB [ \-\-metadataignore y|n ]
+.RB [ \-\-dataalignment alignment ]
+.RB [ \-\-dataalignmentoffset alignment_offset ]
+.RB [ \-\-restorefile file ]
+.RB [ \-\-norestorefile ]
+.RB [ \-\-setphysicalvolumesize size ]
+.RB [ \-u | \-\-uuid uuid ]
+.RB [ \-\-version ]
+.RB [ \-Z | \-\-zero y|n ]
+.IR PhysicalVolume " [" PhysicalVolume ...]
+.SH DESCRIPTION
+.B pvcreate
+initializes
+.I PhysicalVolume
+for later use by the Logical Volume Manager (LVM). Each
+.I PhysicalVolume
+can be a disk partition, whole disk, meta device, or loopback file.
+For DOS disk partitions, the partition id should be set to 0x8e using
+.BR fdisk "(8), " cfdisk "(8), "
+or a equivalent. For
+.B whole disk devices only
+the partition table must be erased, which will effectively destroy all
+data on that disk. This can be done by zeroing the first sector with:
+.sp
+.BI "dd if=/dev/zero of=" PhysicalVolume " bs=512 count=1"
+.sp
+Continue with
+.BR vgcreate (8)
+to create a new volume group on
+.IR PhysicalVolume ,
+or
+.BR vgextend (8)
+to add
+.I PhysicalVolume
+to an existing volume group.
+.SH OPTIONS
+See \fBlvm\fP(8) for common options.
+.TP
+.BR \-f ", " \-\-force
+Force the creation without any confirmation. You can not recreate
+(reinitialize) a physical volume belonging to an existing volume group.
+In an emergency you can override this behaviour with -ff.
+.TP
+.BR \-u ", " \-\-uuid " uuid"
+Specify the uuid for the device.
+Without this option, \fBpvcreate\fP generates a random uuid.
+All of your physical volumes must have unique uuids.
+You need to use this option before restoring a backup of LVM metadata
+onto a replacement device - see \fBvgcfgrestore\fP(8). As such, use of
+\fB--restorefile\fP is compulsory unless the \fB--norestorefile\fP is
+used.
+.TP
+.BR \-y ", " \-\-yes
+Answer yes to all questions.
+.TP
+.BR \-Z ", " \-\-zero " y|n"
+Whether or not the first 4 sectors (2048 bytes) of the device should be
+wiped.
+If this option is not given, the
+default is to wipe these sectors unless either or both of the --restorefile
+or --uuid options were specified.
+.SH NEW METADATA OPTIONS
+LVM2 introduces a new format for storing metadata on disk.
+This new format is more efficient and resilient than the format the
+original version of LVM used and offers the advanced user greater
+flexibility and control.
+.sp
+The new format may be selected on the command line with \fB-M2\fP or by
+setting \fBformat = "lvm2"\fP in the \fBglobal\fP section of \fBlvm.conf\fP.
+Each physical volume in the same volume group must use the same format, but
+different volume groups on a machine may use different formats
+simultaneously: the tools can handle both formats.
+Additional formats can be added as shared libraries.
+.sp
+Additional tools for manipulating the locations and sizes of metadata areas
+will be written in due course. Use the verbose/debug options on the tools
+to see where the metadata areas are placed.
+.TP
+.BR \-\-metadatasize " size"
+The approximate amount of space to be set aside for each metadata area.
+(The size you specify may get rounded.)
+.TP
+.BR \-\-dataalignment " alignment"
+Align the start of the data to a multiple of this number.
+You should also specify an appropriate \fBPhysicalExtentSize\fP when creating
+the Volume Group with \fBvgcreate\fP.
+.sp
+To see the location of the first Physical Extent of an existing Physical Volume
+use \fBpvs -o +pe_start\fP . It will be a multiple of the requested
+\fBalignment\fP. In addition it may be shifted by \fBalignment_offset\fP from
+\fBdata_alignment_offset_detection\fP (if enabled in \fBlvm.conf\fP) or
+\fB--dataalignmentoffset\fP.
+.TP
+.BR \-\-dataalignmentoffset " alignment_offset"
+Shift the start of the data area by this additional \fBalignment_offset\fP.
+.TP
+.BR \-\-[pv]metadatacopies " copies"
+The number of metadata areas to set aside on each PV. Currently
+this can be 0, 1 or 2.
+If set to 2, two copies of the volume group metadata
+are held on the PV, one at the front of the PV and one at the end.
+If set to 1 (the default), one copy is kept at the front of the PV
+(starting in the 5th sector).
+If set to 0, no copies are kept on this PV - you might wish to use this
+with VGs containing large numbers of PVs. But if you do this and
+then later use \fBvgsplit\fP you must ensure that each VG is still going
+to have a suitable number of copies of the metadata after the split!
+.TP
+.BR \-\-metadataignore " y|n"
+Ignore or un-ignore metadata areas on this physical volume.
+The default is "n". This setting can be changed with \fBpvchange\fP.
+If metadata areas on a physical volume are ignored, LVM will
+not not store metadata in the metadata areas present on this Physical
+Volume. Metadata areas cannot be created or extended after Logical
+Volumes have been allocated on the device. If you do not want to store
+metadata on this device, it is still wise always to allocate a metadata
+area in case you need it in the future and to use this option to instruct
+LVM2 to ignore it.
+.TP
+.BR \-\-restorefile " file"
+In conjunction with \fB--uuid\fP, this extracts the location and size
+of the data on the PV from the file (produced by \fBvgcfgbackup\fP)
+and ensures that the metadata that the program produces is consistent
+with the contents of the file i.e. the physical extents will be in
+the same place and not get overwritten by new metadata. This provides
+a mechanism to upgrade the metadata format or to add/remove metadata
+areas. Use with care. See also \fBvgconvert\fP(8).
+.TP
+.BR \-\-norestorefile
+In conjunction with \fB--uuid\fP, this allows a uuid to be specified
+without also requiring that a backup of the metadata be provided.
+.TP
+.BR \-\-labelsector " sector"
+By default the PV is labelled with an LVM2 identifier in its second
+sector (sector 1). This lets you use a different sector near the
+start of the disk (between 0 and 3 inclusive - see LABEL_SCAN_SECTORS
+in the source). Use with care.
+.TP
+.BR \-\-setphysicalvolumesize " size"
+Overrides the automatically-detected size of the PV. Use with care.
+.SH EXAMPLES
+Initialize partition #4 on the third SCSI disk and the entire fifth
+SCSI disk for later use by LVM:
+.sp
+.B pvcreate /dev/sdc4 /dev/sde
+.sp
+If the 2nd SCSI disk is a 4KB sector drive that compensates for windows
+partitioning (sector 7 is the lowest aligned logical block, the 4KB
+sectors start at LBA -1, and consequently sector 63 is aligned on a 4KB
+boundary) manually account for this when initializing for use by LVM:
+.sp
+.B pvcreate --dataalignmentoffset 7s /dev/sdb
+.sp
+.SH SEE ALSO
+.BR lvm.conf (5),
+.BR lvm (8),
+.BR vgcreate (8),
+.BR vgextend (8),
+.BR lvcreate (8),
+.BR cfdisk (8),
+.BR fdisk (8),
+.BR losetup (8),
+.BR mdadm (8),
+.BR vgcfgrestore (8),
+.BR vgconvert (8)
--- /dev/null
+.TH PVDISPLAY 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvdisplay \- display attributes of a physical volume
+.SH SYNOPSIS
+.B pvdisplay
+[\-c|\-\-colon]
+[\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure]
+[\-\-maps]
+[\-\-nosuffix]
+[\-s|\-\-short]
+[\-\-units hsbkmgtHKMGT]
+[\-v[v]|\-\-verbose [\-\-verbose]]
+[\-\-version]
+[PhysicalVolumePath [PhysicalVolumePath...]]
+.br
+
+.br
+.B pvdisplay \-\-columns | \-C
+[\-\-aligned]
+[\-a|\-\-all]
+[\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure]
+[\-\-noheadings]
+[\-\-nosuffix]
+[\-o|\-\-options [+]Field[,Field]]
+[\-O|\-\-sort [+|-]Key1[,[+|-]Key2[,...]]]
+[\-\-separator Separator]
+[\-\-unbuffered]
+[\-\-units hHbBsSkKmMgGtTpPeE]
+[\-v[v]|\-\-verbose [\-\-verbose]]
+[\-\-version]
+[PhysicalVolumePath [PhysicalVolumePath...]]
+.SH DESCRIPTION
+pvdisplay allows you to see the attributes of one or more physical volumes
+like size, physical extent size, space used for the volume group descriptor
+area and so on.
+.P
+\fBpvs\fP (8) is an alternative that provides the same information
+in the style of \fBps\fP (1).
+.SH OPTIONS
+See \fBlvm\fP for common options and \fBpvs\fP for options given with
+\fB\-\-columns\fP.
+.TP
+.I \-c, \-\-colon
+Generate colon separated output for easier parsing in scripts or programs.
+N.B. \fBpvs\fP (8) provides considerably more control over the output.
+.nf
+
+The values are:
+
+* physical volume device name
+* volume group name
+* physical volume size in kilobytes
+* internal physical volume number (obsolete)
+* physical volume status
+* physical volume (not) allocatable
+* current number of logical volumes on this physical volume
+* physical extent size in kilobytes
+* total number of physical extents
+* free number of physical extents
+* allocated number of physical extents
+
+.fi
+.TP
+.I \-s, \-\-short
+Only display the size of the given physical volumes.
+.TP
+.I \-m, \-\-maps
+Display the mapping of physical extents to logical volumes and
+logical extents.
+.TP
+.I \-\-columns | \-C
+Display output in columns, the equivalent of \fBpvs\fP (8). See
+\fBpvs (8)\fP for a description of other options with this form of
+\fBpvdisplay\fP.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvcreate (8),
+.BR lvcreate (8),
+.BR vgcreate (8)
--- /dev/null
+.TH PVMOVE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvmove \- move physical extents
+.SH SYNOPSIS
+.B pvmove
+[\-\-abort]
+[\-\-alloc AllocationPolicy]
+[\-b|\-\-background]
+[\-d|\-\-debug] [\-h|\-\-help] [\-i|\-\-interval Seconds]
+[\-\-noudevsync] [\-v|\-\-verbose] [\-n|\-\-name LogicalVolume]
+[SourcePhysicalVolume[:PE[-PE]...] [DestinationPhysicalVolume[:PE[-PE]...]...]]
+.SH DESCRIPTION
+.B pvmove
+allows you to move the allocated physical extents (PEs) on
+.I SourcePhysicalVolume
+to one or more other physical volumes (PVs).
+You can optionally specify a source
+.I LogicalVolume
+in which case only extents used by that LV will be moved to
+free (or specified) extents on
+.IR DestinationPhysicalVolume (s).
+If no
+.I DestinationPhysicalVolume
+is specifed, the normal allocation rules for the volume group are used.
+
+If \fBpvmove\fP gets interrupted for any reason (e.g. the machine crashes)
+then run \fBpvmove\fP again without any PhysicalVolume arguments to
+restart any moves that were in progress from the last checkpoint.
+Alternatively use \fBpvmove --abort\fP at any time to abort them
+at the last checkpoint.
+
+You can run more than one pvmove at once provided they are moving data
+off different SourcePhysicalVolumes, but additional pvmoves will ignore
+any logical volumes already in the process of being changed, so some
+data might not get moved.
+
+\fBpvmove\fP works as follows:
+
+1. A temporary 'pvmove' logical volume is created to store
+details of all the data movements required.
+
+2. Every logical volume in the volume group is searched
+for contiguous data that need moving
+according to the command line arguments.
+For each piece of data found, a new segment is added to the end of the
+pvmove LV.
+This segment takes the form of a temporary mirror to copy the data
+from the original location to a newly-allocated location.
+The original LV is updated to use the new temporary mirror segment
+in the pvmove LV instead of accessing the data directly.
+
+3. The volume group metadata is updated on disk.
+
+4. The first segment of the pvmove logical volume is activated and starts
+to mirror the first part of the data. Only one segment is mirrored at once
+as this is usually more efficient.
+
+5. A daemon repeatedly checks progress at the specified time interval.
+When it detects that the first temporary mirror is in-sync,
+it breaks that mirror so that only the new location for that data gets used
+and writes a checkpoint into the volume group metadata on disk.
+Then it activates the mirror for the next segment of the pvmove LV.
+
+6. When there are no more segments left to be mirrored,
+the temporary logical volume is removed and the volume group metadata
+is updated so that the logical volumes reflect the new data locations.
+
+Note that this new process cannot support the original LVM1
+type of on-disk metadata. Metadata can be converted using \fBvgconvert\fP(8).
+
+.SH OPTIONS
+.TP
+.I \-\-abort
+Abort any moves in progress.
+.TP
+.I \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background. You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.I \-b, \-\-background
+Run the daemon in the background.
+.TP
+.I \-i, \-\-interval Seconds
+Report progress as a percentage at regular intervals.
+.TP
+.I \-n, \-\-name " \fILogicalVolume\fR"
+Move only the extents belonging to
+.I LogicalVolume
+from
+.I SourcePhysicalVolume
+instead of all allocated extents to the destination physical volume(s).
+
+.SH EXAMPLES
+To move all logical extents of any logical volumes on
+.B /dev/hda4
+to free physical extents elsewhere in the volume group, giving verbose
+runtime information, use:
+.sp
+\ pvmove -v /dev/hda4
+.sp
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgconvert (8)
--- /dev/null
+.TH PVREMOVE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvremove \- remove a physical volume
+.SH SYNOPSIS
+.B pvremove
+.RB [ \-d | \-\-debug]
+.RB [ \-f [ f ]| \-\-force " [" \-\-force ]]
+.RB [\-h | \-\-help]
+.RB [ \-t | \-\-test ]
+.RB [ \-v [ v ]| \-\-verbose " [" \-\-verbose ]]
+.RB [ \-y | \-\-yes ]
+.IR PhysicalVolume " [" PhysicalVolume ...]
+.SH DESCRIPTION
+.B pvremove
+wipes the label on a device so that LVM will no longer recognise it
+as a physical volume.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvcreate (8),
+.BR pvdisplay (8)
--- /dev/null
+.TH PVRESIZE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvresize \- resize a disk or partition in use by LVM2
+.SH SYNOPSIS
+.B pvresize
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+.RB [ \-\-setphysicalvolumesize size ]
+.IR PhysicalVolume " [" PhysicalVolume ...]
+.SH DESCRIPTION
+.B pvresize
+resizes
+.I PhysicalVolume
+which may already be in a volume group and have active logical volumes
+allocated on it.
+.SH OPTIONS
+See \fBlvm\fP(8) for common options.
+.TP
+.BR \-\-setphysicalvolumesize " size"
+Overrides the automatically-detected size of the PV. Use with care, or
+prior to reducing the physical size of the device.
+.SH EXAMPLES
+Expand the PV on /dev/sda1 after enlarging the partition with fdisk:
+.sp
+.B pvresize /dev/sda1
+.sp
+Shrink the PV on /dev/sda1 prior to shrinking the partition with fdisk
+(ensure that the PV size is appropriate for your intended new partition
+size):
+.sp
+.B pvresize --setphysicalvolumesize 40G /dev/sda1
+.sp
+.SH RESTRICTIONS
+.B pvresize
+will refuse to shrink
+.I PhysicalVolume
+if it has allocated extents after where its new end would be. In the future,
+it should relocate these elsewhere in the volume group if there is sufficient
+free space, like
+.B pvmove
+does.
+.sp
+.B pvresize
+won't currently work correctly on LVM1 volumes or PVs with extra
+metadata areas.
+.SH SEE ALSO
+.BR lvm "(8), " pvmove "(8), " lvresize "(8), " fdisk "(8)"
--- /dev/null
+.TH PVS 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvs \- report information about physical volumes
+.SH SYNOPSIS
+.B pvs
+[\-a|\-\-all]
+[\-\-aligned] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure] [\-\-nameprefixes] [\-\-noheadings] [\-\-nosuffix]
+[\-o|\-\-options [+]Field[,Field]]
+[\-O|\-\-sort [+|-]Key1[,[+|-]Key2[,...]]]
+[\-P|\-\-partial]
+[\-\-rows]
+[\-\-segments]
+[\-\-separator Separator]
+[\-\-unbuffered]
+[\-\-units hHbBsSkKmMgGtTpPeE]
+[\-\-unquoted]
+[\-v|\-\-verbose]
+[\-\-version] [PhysicalVolume [PhysicalVolume...]]
+.SH DESCRIPTION
+pvs produces formatted output about physical volumes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+\fB\-\-columns\fP.
+.TP
+.I \-\-all
+Include information in the output about devices that have not been
+initialized with \fBpvcreate\fP.
+.TP
+.I \-\-aligned
+Use with \-\-separator to align the output columns.
+.TP
+.I \-\-nameprefixes
+Add an "LVM2_" prefix plus the field name to the output. Useful
+with --noheadings to produce a list of field=value pairs that can
+be used to set environment variables (for example, in \fBudev (7)\fP rules).
+.TP
+.I \-\-noheadings
+Suppress the headings line that is normally the first line of output.
+Useful if grepping the output.
+.TP
+.I \-\-nosuffix
+Suppress the suffix on output sizes. Use with \-\-units (except h and H)
+if processing the output.
+.TP
+.I \-o, \-\-options
+Comma-separated ordered list of columns. Precede the list with '+' to append
+to the default selection of columns.
+.IP
+Use \fb-o pv_all\fP to select all physical volume columns, and \fb-o pvseg_all\fP
+to select all Physical Volume segment columns.
+.IP
+Use \fb-o help\fP to view the full list of columns available.
+.IP
+Column names include: pv_fmt, pv_uuid, dev_size, pv_name, pv_mda_free,
+pv_mda_size, pe_start, pv_size, pv_free, pv_used, pv_attr, pv_pe_count,
+pv_pe_alloc_count, pv_tags, pv_mda_count, pv_mda_used_count,
+pvseg_start, and pvseg_size.
+.IP
+With --segments, any "pvseg_" prefixes are optional; otherwise any
+"pv_" prefixes are optional. Columns mentioned in \fBvgs (8)\fP can also
+be chosen. The pv_attr bits are: (a)llocatable and e(x)ported.
+.TP
+.I \-\-segments
+Produces one line of output for each contiguous allocation of space on each
+Physical Volume, showing the start (pvseg_start) and length (pvseg_size) in
+units of physical extents.
+.TP
+.I \-O, \-\-sort
+Comma-separated ordered list of columns to sort by. Replaces the default
+selection. Precede any column with - for a reverse sort on that column.
+.TP
+.I \-\-rows
+Output columns as rows.
+.TP
+.I \-\-separator Separator
+String to use to separate each column. Useful if grepping the output.
+.TP
+.I \-\-unbuffered
+Produce output immediately without sorting or aligning the columns properly.
+.TP
+.I \-\-units hHbBsSkKmMgGtTpPeE
+All sizes are output in these units: (h)uman-readable, (b)ytes, (s)ectors,
+(k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes, (p)etabytes, (e)xabytes.
+Capitalise to use multiples of 1000 (S.I.) instead of 1024. Can also specify
+custom units e.g. \-\-units 3M
+.TP
+.I \-\-unquoted
+When used with --nameprefixes, output values in the field=value pairs are not quoted.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvdisplay (8),
+.BR lvs (8),
+.BR vgs (8)
--- /dev/null
+.TH PVSCAN 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+pvscan \- scan all disks for physical volumes
+.SH SYNOPSIS
+.B pvscan
+.RB [ \-d | \-\-debug]
+.RB [\-e | \-\-exported]
+.RB [\-h | \-\-help]
+.RB [\-\-ignorelockingfailure]
+.RB [ \-n | \-\-novolumegroup]
+.RB [\-s | \-\-short]
+.RB [\-u | \-\-uuid]
+.RB [ \-v [ v ]| \-\-verbose " [" \-\-verbose ]]
+.SH DESCRIPTION
+.B pvscan
+scans all supported LVM block devices in the system for physical volumes.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-e ", " \-\-exported
+Only show physical volumes belonging to exported volume groups.
+.TP
+.BR \-n ", " \-\-novolumegroup
+Only show physical volumes not belonging to any volume group.
+.TP
+.BR \-s ", " \-\-short
+Short listing format.
+.TP
+.BR \-u ", " \-\-uuid
+Show UUIDs (Uniform Unique Identifiers) in addition to device special names.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvcreate (8),
+.BR pvdisplay (8)
--- /dev/null
+.TH VGCFGBACKUP 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgcfgbackup \- backup volume group descriptor area
+.SH SYNOPSIS
+.B vgcfgbackup
+.RB [ \-d | \-\-debug ]
+.RB [ \-f | \-\-file " filename" ]
+.RB [ \-h | \-\-help ]
+.RB [ \-\-ignorelockingfailure ]
+.RB [ \-P | \-\-partial ]
+.RB [ \-v | \-\-verbose ]
+.RI [ VolumeGroupName ...]
+.SH DESCRIPTION
+.B vgcfgbackup
+allows you to backup the metadata
+of your volume groups.
+If you don't name any volume groups on the command line, all of them
+will be backed up.
+.sp
+In a default installation, each volume group gets backed up into a separate
+file bearing the name of the volume group in the directory #DEFAULT_BACKUP_DIR#.
+You can write the backup to an alternative file using -f. In this case
+if you are backing up more than one volume group the filename is
+treated as a template, and %s gets replaced by the volume group name.
+.sp
+NB. This DOESN'T backup user/system data in logical
+volume(s)! Backup #DEFAULT_SYS_DIR# regularly too.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcfgrestore (8)
--- /dev/null
+.TH VGCFGRESTORE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgcfgrestore \- restore volume group descriptor area
+.SH SYNOPSIS
+.B vgcfgrestore
+.RB [ \-d | \-\-debug ]
+.RB [ \-f | \-\-file " filename" ]
+.RB [ \-l[l] | \-\-list ]
+.RB [ \-h | \-\-help ]
+.RB [ \-M | \-\-Metadatatype 1|2]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+.RI \fIVolumeGroupName\fP
+.SH DESCRIPTION
+.B vgcfgrestore
+allows you to restore the metadata of \fIVolumeGroupName\fP from a text
+backup file produced by \fBvgcfgbackup\fP. You can specify a backup file
+with \fP--file\fP. If no backup file is specified, the most recent
+one is used. Use \fB--list\fP for a list of the available
+backup and archive files of \fIVolumeGroupName\fP.
+.SH OPTIONS
+.TP
+\fB-l | --list\fP \(em List files pertaining to \fIVolumeGroupName\fP
+List metadata backup and archive files pertaining to \fIVolumeGroupName\fP.
+May be used with the \fB-f\fP option. Does not restore \fIVolumeGroupName\fP.
+.TP
+\fB-f | --file\fP filename \(em Name of LVM metadata backup file
+Specifies a metadata backup or archive file to be used for restoring
+VolumeGroupName. Often this file has been created with \fBvgcfgbackup\fP.
+.TP
+See \fBlvm\fP for common options.
+.SH REPLACING PHYSICAL VOLUMES
+\fBvgdisplay --partial --verbose\fP will show you the UUIDs and sizes of
+any PVs that are no longer present.
+If a PV in the VG is lost and you wish to substitute
+another of the same size, use
+\fBpvcreate --restorefile filename --uuid uuid\fP (plus additional
+arguments as appropriate) to initialise it with the same UUID as
+the missing PV. Repeat for all other missing PVs in the VG.
+Then use \fBvgcfgrestore --file filename\fP to restore the volume
+group's metadata.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8)
--- /dev/null
+.TH VGCHANGE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgchange \- change attributes of a volume group
+.SH SYNOPSIS
+.B vgchange
+.RB [ \-\-addtag
+.IR Tag ]
+.RB [ \-\-alloc
+.IR AllocationPolicy ]
+.RB [ \-A | \-\-autobackup " {" y | n }]
+.RB [ \-a | \-\-available " [e|l] {" y | n }]
+.RB [ \-\-monitor " {" y | n }]
+.RB [ \-\-poll " {" y | n }]
+.RB [ \-c | \-\-clustered " {" y | n }]
+.RB [ \-u | \-\-uuid ]
+.RB [ \-d | \-\-debug]
+.RB [ \-\-deltag
+.IR Tag ]
+.RB [ \-h | \-\-help]
+.RB [ \-\-ignorelockingfailure]
+.RB [ \-\-ignoremonitoring]
+.RB [ \-\-sysinit]
+.RB [ \-\-noudevsync ]
+.RB [ \-l | \-\-logicalvolume
+.IR MaxLogicalVolumes ]
+.RB [ -p | \-\-maxphysicalvolumes
+.IR MaxPhysicalVolumes ]
+.RB [ \-\-[vg]metadatacopies ]
+.IR NumberOfCopies|unmanaged|all ]
+.RB [ \-P | \-\-partial]
+.RB [ \-s | \-\-physicalextentsize
+.IR PhysicalExtentSize [ \fBbBsSkKmMgGtTpPeE\fR ]]
+.RB [ \-\-refresh]
+.RB [ -t | \-\-test]
+.RB [ \-v | \-\-verbose]
+.RB [ \-\-version ]
+.RB [ \-x | \-\-resizeable " {" y | n }]
+.RI [ VolumeGroupName ...]
+.SH DESCRIPTION
+.B vgchange
+allows you to change the attributes of one or more volume groups.
+Its main purpose is to activate and deactivate
+.IR VolumeGroupName ,
+or all volume groups if none is specified. Only active volume groups
+are subject to changes and allow access to their logical volumes.
+[Not yet implemented: During volume group activation, if
+.B vgchange
+recognizes snapshot logical volumes which were dropped because they ran
+out of space, it displays a message informing the administrator that such
+snapshots should be removed (see
+.BR lvremove (8)).
+]
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-A ", " \-\-autobackup " " { y | n }
+Controls automatic backup of metadata after the change. See
+.B vgcfgbackup (8).
+Default is yes.
+.TP
+.BR \-a ", " \-\-available " " [e|l] { y | n }
+Controls the availability of the logical volumes in the volume
+group for input/output.
+In other words, makes the logical volumes known/unknown to the kernel.
+.IP
+If clustered locking is enabled, add 'e' to activate/deactivate
+exclusively on one node or 'l' to activate/deactivate only
+on the local node.
+Logical volumes with single-host snapshots are always activated
+exclusively because they can only be used on one node at once.
+.TP
+.BR \-c ", " \-\-clustered " " { y | n }
+If clustered locking is enabled, this indicates whether this
+Volume Group is shared with other nodes in the cluster or whether
+it contains only local disks that are not visible on the other nodes.
+If the cluster infrastructure is unavailable on a particular node at a
+particular time, you may still be able to use Volume Groups that
+are not marked as clustered.
+.TP
+.BR \-u ", " \-\-uuid
+Generate new random UUID for specified Volume Groups.
+.TP
+.BR \-\-monitor " " { y | n }
+Start or stop monitoring a mirrored or snapshot logical volume with
+dmeventd, if it is installed.
+If a device used by a monitored mirror reports an I/O error,
+the failure is handled according to
+.BR mirror_image_fault_policy
+and
+.BR mirror_log_fault_policy
+set in
+.BR lvm.conf (5).
+.TP
+.BR \-\-poll " " { y | n }
+Without polling a logical volume's backgrounded transformation process
+will never complete. If there is an incomplete pvmove or lvconvert (for
+example, on rebooting after a crash), use \fB--poll y\fP to restart the
+process from its last checkpoint. However, it may not be appropriate to
+immediately poll a logical volume when it is activated, use \fB--poll
+n\fP to defer and then \fB--poll y\fP to restart the process.
+.TP
+.BR \-\-sysinit
+Indicates that vgchange(8) is being invoked from early system initialisation
+scripts (e.g. rc.sysinit or an initrd), before writeable filesystems are
+available. As such, some functionality needs to be disabled and this option
+acts as a shortcut which selects an appropriate set of options. Currently
+this is equivalent to using \fB--ignorelockingfailure\fP, \fB--ignoremonitoring\fP,
+\fB--poll n\fP and setting \fBLVM_SUPPRESS_LOCKING_FAILURE_MESSAGES\fP
+environment variable.
+.TP
+.BR \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background. You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.TP
+.BR \-\-ignoremonitoring
+Make no attempt to interact with dmeventd unless
+.BR \-\-monitor
+is specified.
+Do not use this if dmeventd is already monitoring a device.
+.TP
+.BR \-l ", " \-\-logicalvolume " " \fIMaxLogicalVolumes\fR
+Changes the maximum logical volume number of an existing inactive
+volume group.
+.TP
+.BR \-p ", " \-\-maxphysicalvolumes " " \fIMaxPhysicalVolumes\fR
+Changes the maximum number of physical volumes that can belong
+to this volume group.
+For volume groups with metadata in lvm1 format, the limit is 255.
+If the metadata uses lvm2 format, the value 0 removes this restriction:
+there is then no limit. If you have a large number of physical volumes in
+a volume group with metadata in lvm2 format, for tool performance reasons,
+you should consider some use of \fB--pvmetadatacopies 0\fP as described in
+\fBpvcreate(8)\fP, and/or use \fB--vgmetadatacopies\fP.
+.TP
+.BR \-\-[vg]metadatacopies " " \fINumberOfCopies|unmanaged|all\fP
+Sets the desired number of metadata copies in the volume group. If set to
+a non-zero value, LVM will automatically manage the 'metadataignore'
+flags on the physical volumes (see \fBpvchange\fP or \fBpvcreate --metadataignore\fP) in order
+to achieve \fINumberOfCopies\fP copies of metadata. If set to \fIunmanaged\fP,
+LVM will not automatically manage the 'metadataignore' flags. If set to
+\fIall\fP, LVM will first clear all of the 'metadataignore' flags on all
+metadata areas in the volume group, then set the value to \fIunmanaged\fP.
+The \fBvgmetadatacopies\fP option is useful for volume groups containing
+large numbers of physical volumes with metadata as it may be used to
+minimize metadata read and write overhead.
+.TP
+.BR \-s ", " \-\-physicalextentsize " " \fIPhysicalExtentSize\fR[\fBbBsSkKmMgGtTpPeE\fR]
+Changes the physical extent size on physical volumes of this volume group.
+A size suffix (k for kilobytes up to t for terabytes) is optional, megabytes
+is the default if no suffix is present.
+The default is 4 MB and it must be at least 1 KB and a power of 2.
+
+Before increasing the physical extent size, you might need to use lvresize,
+pvresize and/or pvmove so that everything fits. For example, every
+contiguous range of extents used in a logical volume must start and
+end on an extent boundary.
+
+If the volume group metadata uses lvm1 format, extents can vary in size from
+8KB to 16GB and there is a limit of 65534 extents in each logical volume. The
+default of 4 MB leads to a maximum logical volume size of around 256GB.
+
+If the volume group metadata uses lvm2 format those restrictions do not apply,
+but having a large number of extents will slow down the tools but have no
+impact on I/O performance to the logical volume. The smallest PE is 1KB.
+
+The 2.4 kernel has a limitation of 2TB per block device.
+.TP
+.BR \-\-refresh
+If any logical volume in the volume group is active, reload its metadata.
+This is not necessary in normal operation, but may be useful
+if something has gone wrong or if you're doing clustering
+manually without a clustered lock manager.
+.TP
+.BR \-x ", " \-\-resizeable " " { y | n }
+Enables or disables the extension/reduction of this volume group
+with/by physical volumes.
+.SH EXAMPLES
+To activate all known volume groups in the system:
+.nf
+
+\ vgchange -a y
+
+.fi
+To change the maximum number of logical volumes of inactive volume group
+.B vg00
+to 128.
+.nf
+
+\ vgchange -l 128 /dev/vg00
+
+.fi
+.SH SEE ALSO
+.BR lvchange (8),
+.BR lvm (8),
+.BR vgcreate (8)
--- /dev/null
+.TH VGCK 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgck \- check volume group metadata
+.SH SYNOPSIS
+.B vgck
+[\-d|\-\-debug] [\-h|\-?|\-\-help] [\-v|\-\-verbose] [VolumeGroupName...]
+.SH DESCRIPTION
+vgck checks LVM metadata for each named volume group for consistency.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8),
+.BR vgchange (8),
+.BR vgscan (8)
--- /dev/null
+.TH VGCONVERT 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgconvert \- convert volume group metadata format
+.SH SYNOPSIS
+.B vgconvert
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+.RB [ \-\-labelsector ]
+.RB [ \-M | \-\-metadatatype type ]
+.RB [ \-\-pvmetadatacopies #copies ]
+.RB [ \-\-metadatasize size ]
+.RB [ \-\-version ]
+.IR VolumeGroupName " [" VolumeGroupName ...]
+.SH DESCRIPTION
+.B vgconvert
+converts
+.I VolumeGroupName
+metadata from one format to another provided that the metadata
+fits into the same space.
+.SH OPTIONS
+See \fBlvm\fP(8) and \fBpvcreate\fP(8) for options.
+.SH EXAMPLE
+Convert volume group vg1 from LVM1 metadata format to the new LVM2
+metadata format.
+.sp
+.B vgconvert -M2 vg1
+.SH RECOVERY
+Use \fBpvscan\fP(8) to see which PVs lost their metadata.
+Run \fBpvcreate\fP(8) with the --uuid and --restorefile options on each
+such PV to reformat it as it was, using the archive file that
+\fBvgconvert\fP(8) created at the start of the procedure.
+Finally run \fBvgcfgrestore\fP(8) with that archive file to restore
+the original metadata.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvcreate (8),
+.BR vgcfgrestore (8)
--- /dev/null
+.TH VGCREATE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgcreate \- create a volume group
+.SH SYNOPSIS
+.B vgcreate
+.RB [ \-\-addtag
+.IR Tag ]
+.RB [ \-\-alloc
+.IR AllocationPolicy ]
+.RB [ \-A | \-\-autobackup " {" y | n }]
+.RB [ \-c | \-\-clustered " {" y | n }]
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-l | \-\-maxlogicalvolumes
+.IR MaxLogicalVolumes ]
+.RB [ -M | \-\-metadatatype type]
+.RB [ -p | \-\-maxphysicalvolumes
+.IR MaxPhysicalVolumes ]
+.RB [ \-\-[vg]metadatacopies ]
+.IR NumberOfCopies|unmanaged|all ]
+.RB [ \-s | \-\-physicalextentsize
+.IR PhysicalExtentSize [ \fBbBsSkKmMgGtTpPeE\fR ]]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+.RB [ \-\-version ]
+[ \fIPHYSICAL DEVICE OPTIONS\fP ]
+.I VolumeGroupName PhysicalDevicePath
+.RI [ PhysicalDevicePath ...]
+.SH DESCRIPTION
+.B vgcreate
+creates a new volume group called
+.I VolumeGroupName
+using the block special device \fIPhysicalDevicePath\fP.
+.sp
+If \fIPhysicalDevicePath\fP was not previously configured for LVM with
+\fBpvcreate (8)\fP, the device will be initialized with the same
+default values used with \fBpvcreate\fP. If non-default
+\fPpvcreate\fP values are desired, they may be given on the
+commandline with the same options as \fPpvcreate\fP. See
+\fBPHYSICAL DEVICE OPTIONS\fP for available options. Note
+that the restore-related options such as --restorefile, --uuid,
+and --physicalvolumesize are not available. If a restore operation
+is needed, use \fBpvcreate (8)\fP and \fBvgcfgrestore (8)\fP.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-c ", " \-\-clustered " " { y | n }
+If clustered locking is enabled, this defaults to \fBy\fP indicating that
+this Volume Group is shared with other nodes in the cluster.
+
+If the new Volume Group contains only local disks that are not visible
+on the other nodes, you must specify \fB\-\-clustered\ n\fP.
+If the cluster infrastructure is unavailable on a particular node at a
+particular time, you may still be able to use such Volume Groups.
+.TP
+.BR \-l ", " \-\-maxlogicalvolumes " " \fIMaxLogicalVolumes\fR
+Sets the maximum number of logical volumes allowed in this
+volume group.
+The setting can be changed with \fBvgchange\fP.
+For volume groups with metadata in lvm1 format, the limit
+and default value is 255.
+If the metadata uses lvm2 format, the default value is 0
+which removes this restriction: there is then no limit.
+.TP
+.BR \-p ", " \-\-maxphysicalvolumes " " \fIMaxPhysicalVolumes\fR
+Sets the maximum number of physical volumes that can belong
+to this volume group.
+The setting can be changed with \fBvgchange\fP.
+For volume groups with metadata in lvm1 format, the limit
+and default value is 255.
+If the metadata uses lvm2 format, the value 0 removes this restriction:
+there is then no limit. If you have a large number of physical volumes in
+a volume group with metadata in lvm2 format, for tool performance reasons,
+you should consider some use of \fB--pvmetadatacopies 0\fP as described in
+\fBpvcreate(8)\fP, and/or use \fB--vgmetadatacopies\fP.
+.TP
+.BR \-\-vgmetadatacopies " " \fINumberOfCopies|unmanaged|all\fP
+Sets the desired number of metadata copies in the volume group. If set to
+a non-zero value, LVM will automatically manage the 'metadataignore'
+flags on the physical volumes (see \fBpvcreate\fP or \fBpvchange\fP --metadataignore\fP) in order
+to achieve \fINumberOfCopies\fP copies of metadata. If set to \fIunmanaged\fP,
+LVM will not automatically manage the 'metadataignore' flags. If set to
+\fIall\fP, LVM will first clear all of the 'metadataignore' flags on all
+metadata areas in the volume group, then set the value to \fIunmanaged\fP.
+The \fBvgmetadatacopies\fP option is useful for volume groups containing
+large numbers of physical volumes with metadata as it may be used to
+minimize metadata read and write overhead.
+The default value is \fIunmanaged\fP.
+.TP
+.BR \-s ", " \-\-physicalextentsize " " \fIPhysicalExtentSize\fR[\fBbBsSkKmMgGtTpPeE\fR]
+Sets the physical extent size on physical volumes of this volume group.
+A size suffix (k for kilobytes up to t for terabytes) is optional, megabytes
+is the default if no suffix is present.
+The default is 4 MB and it must be at least 1 KB and a power of 2.
+
+Once this value has been set, it is difficult to change it without recreating
+the volume group which would involve backing up and restoring data on any
+logical volumes. However, if no extents need moving for the new
+value to apply, it can be altered using vgchange \-s.
+
+If the volume group metadata uses lvm1 format, extents can vary in size from
+8KB to 16GB and there is a limit of 65534 extents in each logical volume. The
+default of 4 MB leads to a maximum logical volume size of around 256GB.
+
+If the volume group metadata uses lvm2 format those restrictions do not apply,
+but having a large number of extents will slow down the tools but have no
+impact on I/O performance to the logical volume. The smallest PE is 1KB.
+
+The 2.4 kernel has a limitation of 2TB per block device.
+.SH PHYSICAL DEVICE OPTIONS
+The following options are available for initializing physical devices in the
+volume group. These options are further described in the pvcreate man page.
+.TP
+.BR \-f ", " \-\-force
+.TP
+.BR \-y ", " \-\-yes
+.TP
+.BR \-Z ", " \-\-zero " y|n"
+.TP
+.BR \-\-labelsector " sector"
+.TP
+.BR \-\-metadatasize " size"
+.TP
+.BR \-\-pvmetadatacopies " copies"
+.TP
+.BR \-\-dataalignment " alignment"
+.TP
+.BR \-\-dataalignmentoffset " alignment_offset"
+.SH EXAMPLES
+To create a volume group named
+.B test_vg
+using physical volumes
+.BR /dev/sdk1 ", and " /dev/sdl1
+with default physical extent size of 4MB:
+.nf
+
+\ vgcreate test_vg /dev/sdk1 /dev/sdl1
+
+.fi
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvdisplay (8),
+.BR pvcreate (8),
+.BR vgdisplay (8),
+.BR vgextend (8),
+.BR vgreduce (8),
+.BR lvcreate (8),
+.BR lvdisplay (8),
+.BR lvextend (8),
+.BR lvreduce (8)
--- /dev/null
+.TH VGDISPLAY 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgdisplay \- display attributes of volume groups
+.SH SYNOPSIS
+.B vgdisplay
+.RB [ \-A | \-\-activevolumegroups ]
+.RB [ \-c | \-\-colon | \-s | \-\-short | \-v|\-\-verbose ]
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-\-ignorelockingfailure ]
+.RB [ \-\-nosuffix ]
+.RB [ \-P | \-\-partial ]
+.RB [\-\-units hHbBsSkKmMgGtTpPeE]
+.RB [ \-\-version ]
+.RI [VolumeGroupName [VolumeGroupName...]]
+.br
+
+.br
+.B vgdisplay \-\-columns | \-C
+.RB [ \-\-aligned ] [ \-d|\-\-debug ] [ \-h|\-?|\-\-help ]
+.RB [ \-\-ignorelockingfailure ] [ \-\-noheadings ] [ \-\-nosuffix ]
+.RB [ \-o|\-\-options [+]Field[,Field] ]
+.RB [ \-O|\-\-sort [+|-]Key1[,[+|-]Key2[,...]] ]
+.RB [ \-P|\-\-partial ]
+.RB [ \-\-separator Separator ]
+.RB [ \-\-unbuffered ]
+.RB [ \-\-units hHbBsSkKmMgGtTpPeE ]
+.RB [ \-v|\-\-verbose ]
+.RB [ \-\-version ]
+.RI [VolumeGroupName [VolumeGroupName...]]
+.SH DESCRIPTION
+.B vgdisplay
+allows you to see the attributes of
+.I VolumeGroupName
+(or all volume groups if none is given) with it's physical and logical
+volumes and their sizes etc.
+.P
+\fBvgs\fP (8) is an alternative that provides the same information
+in the style of \fBps\fP (1).
+.SH OPTIONS
+See \fBlvm\fP for common options and \fBvgs\fP for options given with
+\fB\-\-columns\fP.
+.TP
+.BR \-A ", " \-\-activevolumegroups
+Only select the active volume groups.
+.TP
+.BR \-c ", " \-\-colon
+Generate colon separated output for easier parsing in scripts or programs.
+N.B. \fBvgs\fP (8) provides considerably more control over the output.
+.nf
+
+The values are:
+
+1 volume group name
+2 volume group access
+3 volume group status
+4 internal volume group number
+5 maximum number of logical volumes
+6 current number of logical volumes
+7 open count of all logical volumes in this volume group
+8 maximum logical volume size
+9 maximum number of physical volumes
+10 current number of physical volumes
+11 actual number of physical volumes
+12 size of volume group in kilobytes
+13 physical extent size
+14 total number of physical extents for this volume group
+15 allocated number of physical extents for this volume group
+16 free number of physical extents for this volume group
+17 uuid of volume group
+
+.fi
+.TP
+.BR \-s ", " \-\-short
+Give a short listing showing the existence of volume groups.
+.TP
+.BR \-v ", " \-\-verbose
+Display verbose information containing long listings of physical
+and logical volumes. If given twice, also display verbose runtime
+information of vgdisplay's activities.
+.TP
+.BR \-\-version
+Display version and exit successfully.
+.TP
+.BR \-\-columns | \-C
+Display output in columns, the equivalent of \fBvgs\fP. Options listed
+are the same as options given in \fPvgs (8)\fP.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgs (8),
+.BR pvcreate (8),
+.BR vgcreate (8),
+.BR lvcreate (8)
--- /dev/null
+.TH VGEXPORT 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgexport \- make volume groups unknown to the system
+.SH SYNOPSIS
+.B vgexport
+[\-a|\-\-all]
+[\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-v|\-\-verbose]
+VolumeGroupName [VolumeGroupName...]
+.SH DESCRIPTION
+vgexport allows you to make the inactive
+.IR VolumeGroupName (s)
+unknown to the system.
+You can then move all the Physical Volumes in that Volume Group to
+a different system for later
+.BR vgimport (8).
+Most LVM2 tools ignore exported Volume Groups.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-a, \-\-all
+Export all inactive Volume Groups.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvscan (8),
+.BR vgimport (8),
+.BR vgscan (8)
--- /dev/null
+.TH VGEXTEND 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgextend \- add physical volumes to a volume group
+.SH SYNOPSIS
+.B vgextend
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-restoremissing]
+[\-f|\-\-force]
+[\-t|\-\-test]
+[\-v|\-\-verbose]
+[ \fIPHYSICAL DEVICE OPTIONS\fP ]
+VolumeGroupName PhysicalDevicePath [PhysicalDevicePath...]
+.SH DESCRIPTION
+vgextend allows you to add one or more initialized physical volumes ( see
+.B pvcreate(8)
+) to an existing volume group to extend it in size. Moreover, it allows you to
+re-add a physical volume that has gone missing previously, due to a transient
+device failure, without re-initialising it. Use vgextend \-\-restoremissing to
+that effect.
+.sp
+If \fIPhysicalDevicePath\fP was not previously configured for LVM with
+\fBpvcreate (8)\fP, the device will be initialized with the same
+default values used with \fBpvcreate\fP. If non-default
+\fPpvcreate\fP values are are desired, they may be given on the
+commandline with the same options as \fPpvcreate\fP. See
+\fBPHYSICAL DEVICE OPTIONS\fP for available options. Note
+that the restore-related options such as --restorefile, --uuid,
+and --physicalvolumesize are not available. If a restore operation
+is needed, use \fBpvcreate (8)\fP and \fBvgcfgrestore (8)\fP.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.SH PHYSICAL DEVICE OPTIONS
+The following options are available for initializing physical devices in the
+volume group. These options are further described in the pvcreate man page.
+.TP
+.BR \-f ", " \-\-force
+.TP
+.BR \-y ", " \-\-yes
+.TP
+.BR \-Z ", " \-\-zero " y|n"
+.TP
+.BR \-\-labelsector " sector"
+.TP
+.BR \-\-metadatasize " size"
+.TP
+.RB [ \-\-metadataignore y|n ]
+.TP
+.BR \-\-pvmetadatacopies " copies"
+.TP
+.BR \-\-dataalignment " alignment"
+.TP
+.BR \-\-dataalignmentoffset " alignment_offset"
+.SH Examples
+"vgextend vg00 /dev/sda4 /dev/sdn1" tries to extend the existing volume
+group "vg00" by the new physical volumes (see
+.B pvcreate(8)
+) "/dev/sdn1" and /dev/sda4".
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8),
+.BR vgreduce (8),
+.BR pvcreate (8)
--- /dev/null
+.TH VGIMPORT 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgimport \- make exported volume groups known to the system
+.SH SYNOPSIS
+.B vgimport
+[\-a|\-\-all]
+[\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-v|\-\-verbose]
+VolumeGroupName [VolumeGroupName...]
+.SH DESCRIPTION
+.B vgimport
+allows you to make a Volume Group that was previously exported using
+.BR vgexport (8)
+known to the system again, perhaps after moving its Physical Volumes
+from a different machine.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-a, \-\-all
+Import all exported Volume Groups.
+.SH SEE ALSO
+.BR lvm (8),
+.BR pvscan (8),
+.BR vgexport (8),
+.BR vgscan (8)
--- /dev/null
+.TH VGIMPORTCLONE 8 "LVM TOOLS #VERSION#" "Red Hat, Inc." \" -*- nroff -*-
+.SH NAME
+vgimportclone \- import and rename duplicated volume group (e.g. a hardware snapshot)
+.SH SYNOPSIS
+.B vgimportclone
+[\-n|\-\-basevgname VolumeGroupName]
+[\-i|\-\-import]
+PhysicalVolume [PhysicalVolume...]
+.SH DESCRIPTION
+.B vgimportclone
+is used to import a duplicated VG (e.g. hardware snapshot). Duplicate VG(s)
+and PV(s) are not able to be used until they are made to coexist with
+the origin VG(s) and PV(s).
+.B vgimportclone
+renames the VG associated with the specified PV(s) and changes the
+associated VG and PV UUIDs.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-n|\-\-basevgname VolumeGroupName
+By default the snapshot VG will be renamed to the original name plus a
+numeric suffix to avoid duplicate naming (e.g. 'test_vg' would be renamed
+to 'test_vg1'). This option will override the base VG name that is
+used for all VG renames. If a VG already exists with the specified name
+a numeric suffix will be added (like the previous example) to make it unique.
+.TP
+.I \-i|\-\-import
+Import exported Volume Groups. Otherwise VGs that have been exported
+will not be changed (nor will their associated PVs).
+.SH ENVIRONMENT VARIABLES
+.TP
+\fBLVM_BINARY\fP
+The LVM2 binary to use.
+Defaults to "lvm".
+.SH EXAMPLES
+The origin VG
+.B vg00
+has origin PVs
+.BR /dev/sda " and " /dev/sdb
+and the respective snapshot PVs are
+.BR /dev/sdc " and " /dev/sdd "."
+To rename the VG
+associated with
+.BR /dev/sdc " and " /dev/sdd
+from
+.B vg00
+to
+.B vg00_snap
+(and to change associated VG and PV UUIDs) do:
+.nf
+
+\ vgimportclone --basevgname vg00_snap /dev/sdc /dev/sdd
+
+.fi
+.SH SEE ALSO
+.BR lvm (8)
+
--- /dev/null
+.TH VGMERGE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgmerge \- merge two volume groups
+.SH SYNOPSIS
+.B vgmerge
+[\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-h|\-?|\-\-help] [\-l|\-\-list]
+[\-t|\-\-test] [\-v|\-\-verbose] DestinationVolumeGroupName
+SourceVolumeGroupName
+.SH DESCRIPTION
+vgmerge merges two existing volume groups. The inactive SourceVolumeGroupName
+will be merged into the DestinationVolumeGroupName if physical extent sizes
+are equal and physical and logical volume summaries of both volume groups
+fit into DestinationVolumeGroupName's limits.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.I \-l, \-\-list
+Display merged DestinationVolumeGroupName like "vgdisplay -v".
+.TP
+.I \-t, \-\-test
+Do a test run WITHOUT making any real changes.
+.SH Examples
+"vgmerge -v databases my_vg" merges the inactive volume group named "my_vg"
+into the active or inactive volume group named "databases" giving verbose
+runtime information.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8),
+.BR vgextend (8),
+.BR vgreduce (8)
--- /dev/null
+.TH VGMKNODES 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgmknodes \- recreate volume group directory and logical volume special files
+.SH SYNOPSIS
+.B vgmknodes
+.RB [ \-d | \-\-debug]
+.RB [ \-h | \-\-help]
+.RB [ \-\-refresh]
+.RB [ \-v | \-\-verbose]
+[[VolumeGroupName | LogicalVolumePath]...]
+.SH DESCRIPTION
+Checks the LVM2 special files in /dev that are needed for active
+logical volumes and creates any missing ones and removes unused ones.
+.SH OPTIONS
+.TP
+.BR \-\-refresh
+If any logical volume in the volume group is active, reload its metadata.
+This is not necessary in normal operation, but may be useful
+if something has gone wrong or if you're doing clustering
+manually without a clustered lock manager.
+.TP
+See \fBlvm\fP for common options.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgscan (8),
+.BR dmsetup (8)
--- /dev/null
+.TH VGREDUCE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgreduce \- reduce a volume group
+.SH SYNOPSIS
+.B vgreduce
+[\-a|\-\-all] [\-A|\-\-autobackup y|n] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-removemissing]
+[\-t|\-\-test]
+[\-v|\-\-verbose] VolumeGroupName
+[PhysicalVolumePath...]
+.SH DESCRIPTION
+vgreduce allows you to remove one or more unused physical volumes
+from a volume group.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-a, \-\-all
+Removes all empty physical volumes if none are given on command line.
+.TP
+.I \-\-removemissing
+Removes all missing physical volumes from the volume group, if there are no
+logical volumes allocated on those. This resumes normal operation of the volume
+group (new logical volumes may again be created, changed and so on).
+
+If this is not possible (there are logical volumes referencing the missing
+physical volumes) and you cannot or do not want to remove them manually, you
+can run this option with --force to have vgreduce remove any partial LVs.
+
+Any logical volumes and dependent snapshots that were partly on the
+missing disks get removed completely. This includes those parts
+that lie on disks that are still present.
+
+If your logical volumes spanned several disks including the ones that are
+lost, you might want to try to salvage data first by activating your
+logical volumes with --partial as described in \fBlvm (8)\fP.
+
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgextend (8)
--- /dev/null
+.TH VGREMOVE 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgremove \- remove a volume group
+.SH SYNOPSIS
+.B vgremove
+[\-d|\-\-debug] [\-f|\-\-force] [\-h|\-?|\-\-help]
+[\-\-noudevsync] [\-t|\-\-test] [\-v|\-\-verbose]
+VolumeGroupName [VolumeGroupName...]
+.SH DESCRIPTION
+vgremove allows you to remove one or more volume groups.
+If one or more physical volumes in the volume group are lost,
+consider \fBvgreduce --removemissing\fP to make the volume group
+metadata consistent again.
+.sp
+If there are logical volumes that exist in the volume group,
+a prompt will be given to confirm removal. You can override
+the prompt with \fB-f\fP.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.BR \-f ", " \-\-force
+Force the removal of any logical volumes on the volume group
+without confirmation.
+.TP
+.BR \-\-noudevsync
+Disable udev synchronisation. The
+process will not wait for notification from udev.
+It will continue irrespective of any possible udev processing
+in the background. You should only use this if udev is not running
+or has rules that ignore the devices LVM2 creates.
+.SH SEE ALSO
+.BR lvm (8),
+.BR lvremove (8),
+.BR vgcreate (8),
+.BR vgreduce (8)
--- /dev/null
+.TH VGRENAME 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgrename \- rename a volume group
+.SH SYNOPSIS
+.B vgrename
+[\-A|\-\-autobackup y|n]
+[\-d|\-\-debug]
+[\-h|\-?|\-\-help]
+[\-t|\-\-test]
+[\-v|\-\-verbose]
+.IR OldVolumeGroup { Path | Name | UUID }
+.IR NewVolumeGroup { Path | Name }
+.SH DESCRIPTION
+vgrename renames an existing (see
+.B vgcreate(8)
+) volume group from
+.IR OldVolumeGroup { Name | Path | UUID }
+to
+.IR NewVolumeGroup { Name | Path }.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.SH Examples
+"vgrename /dev/vg02 /dev/my_volume_group" renames existing
+volume group "vg02" to "my_volume_group".
+.TP
+"vgrename vg02 my_volume_group" does the same.
+.TP
+"vgrename Zvlifi-Ep3t-e0Ng-U42h-o0ye-KHu1-nl7Ns4 VolGroup00_tmp"
+changes the name of the Volume Group with UUID
+Zvlifi-Ep3t-e0Ng-U42h-o0ye-KHu1-nl7Ns4 to
+"VolGroup00_tmp".
+
+All the Volume Groups visible to a system need to have different
+names. Otherwise many LVM2 commands will refuse to run or give
+warning messages.
+
+This situation could arise when disks are moved between machines. If
+a disk is connected and it contains a Volume Group with the same name
+as the Volume Group containing your root filesystem the machine might
+not even boot correctly. However, the two Volume Groups should have
+different UUIDs (unless the disk was cloned) so you can rename
+one of the conflicting Volume Groups with
+\fBvgrename\fP.
+.TP
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgchange (8),
+.BR vgcreate (8),
+.BR lvrename (8)
--- /dev/null
+.TH VGS 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgs \- report information about volume groups
+.SH SYNOPSIS
+.B vgs
+[\-a|\-\-all]
+[\-\-aligned] [\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure] [\-\-nameprefixes] [\-\-noheadings] [\-\-nosuffix]
+[\-o|\-\-options [+]Field[,Field]]
+[\-O|\-\-sort [+|-]Key1[,[+|-]Key2[,...]]]
+[\-P|\-\-partial] [\-\-rows]
+[\-\-separator Separator] [\-\-unbuffered]
+[\-\-units hHbBsSkKmMgGtTpPeE]
+[\-\-unquoted]
+[\-v|\-\-verbose]
+[\-\-version] [VolumeGroupName [VolumeGroupName...]]
+.SH DESCRIPTION
+vgs produces formatted output about volume groups.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-\-all
+List all volume groups. Equivalent to not specifying any volume groups.
+.TP
+.I \-\-aligned
+Use with \-\-separator to align the output columns.
+.TP
+.I \-\-nameprefixes
+Add an "LVM2_" prefix plus the field name to the output. Useful
+with --noheadings to produce a list of field=value pairs that can
+be used to set environment variables (for example, in \fBudev (7)\fP rules).
+.TP
+.I \-\-noheadings
+Suppress the headings line that is normally the first line of output.
+Useful if grepping the output.
+.TP
+.I \-\-nosuffix
+Suppress the suffix on output sizes. Use with \-\-units (except h and H)
+if processing the output.
+.TP
+.I \-o, \-\-options
+Comma-separated ordered list of columns. Precede the list with '+' to append
+to the default selection of columns.
+.IP
+Use \fb-o vg_all\fP to select all volume group columns.
+.IP
+Use \fb-o help\fP to view the full list of columns available.
+.IP
+Column names include: vg_fmt, vg_uuid, vg_name, vg_attr, vg_size, vg_free,
+vg_sysid, vg_extent_size, vg_extent_count, vg_free_count, max_lv, max_pv,
+pv_count, lv_count, snap_count, vg_seqno, vg_tags, vg_mda_count, vg_mda_free,
+and vg_mda_size, vg_mda_used_count.
+.IP
+Any "vg_" prefixes are optional. Columns mentioned in either \fBpvs (8)\fP
+or \fBlvs (8)\fP can also be chosen, but columns cannot be taken from both
+at the same time.
+.IP
+The vg_attr bits are:
+.RS
+.IP 1 3
+Permissions: (w)riteable, (r)ead-only
+.IP 2 3
+Resi(z)eable
+.IP 3 3
+E(x)ported
+.IP 4 3
+(p)artial: one or more physical volumes belonging to the volume group
+are missing from the system
+.IP 5 3
+Allocation policy: (c)ontiguous, c(l)ing, (n)ormal, (a)nywhere, (i)nherited
+.IP 6 3
+(c)lustered
+.RE
+.TP
+.I \-O, \-\-sort
+Comma-separated ordered list of columns to sort by. Replaces the default
+selection. Precede any column with - for a reverse sort on that column.
+.TP
+.I \-\-rows
+Output columns as rows.
+.TP
+.I \-\-separator Separator
+String to use to separate each column. Useful if grepping the output.
+.TP
+.I \-\-unbuffered
+Produce output immediately without sorting or aligning the columns properly.
+.TP
+.I \-\-units hHbBsSkKmMgGtTpPeE
+All sizes are output in these units: (h)uman-readable, (b)ytes, (s)ectors,
+(k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes, (p)etabytes, (e)xabytes.
+Capitalise to use multiples of 1000 (S.I.) instead of 1024. Can also specify
+custom units e.g. \-\-units 3M
+.TP
+.I \-\-unquoted
+When used with --nameprefixes, output values in the field=value pairs are not quoted.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgdisplay (8),
+.BR pvs (8),
+.BR lvs (8)
--- /dev/null
+.TH VGSCAN 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgscan \- scan all disks for volume groups and rebuild caches
+.SH SYNOPSIS
+.B vgscan
+[\-d|\-\-debug] [\-h|\-?|\-\-help]
+[\-\-ignorelockingfailure]
+[\-\-mknodes]
+[\-P|\-\-partial]
+[\-v|\-\-verbose]
+.SH DESCRIPTION
+vgscan scans all SCSI, (E)IDE disks, multiple devices and a bunch
+of other disk devices in the system looking for LVM physical volumes
+and volume groups. Define a filter in \fBlvm.conf\fP(5) to restrict
+the scan to avoid a CD ROM, for example.
+.LP
+In LVM2, vgscans take place automatically; but you might still need to
+run one explicitly after changing hardware.
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.TP
+.I \-\-mknodes
+Also checks the LVM special files in /dev that are needed for active
+logical volumes and creates any missing ones and removes unused ones.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8),
+.BR vgchange (8)
--- /dev/null
+.TH VGSPLIT 8 "LVM TOOLS #VERSION#" "Sistina Software UK" \" -*- nroff -*-
+.SH NAME
+vgsplit \- split a volume group into two
+.SH SYNOPSIS
+.B vgsplit
+.RB [ \-\-alloc
+.IR AllocationPolicy ]
+.RB [ \-A | \-\-autobackup " {" y | n }]
+.RB [ \-c | \-\-clustered " {" y | n }]
+.RB [ \-d | \-\-debug ]
+.RB [ \-h | \-\-help ]
+.RB [ \-l | \-\-maxlogicalvolumes
+.IR MaxLogicalVolumes ]
+.RB [ -M | \-\-metadatatype
+.IR type ]
+.RB [ -p | \-\-maxphysicalvolumes
+.IR MaxPhysicalVolumes ]
+.RB [ \-\-[vg]metadatacopies ]
+.IR NumberOfCopies|unmanaged|all ]
+.RB [ \-n | \-\-name
+.IR LogicalVolumeName ]
+.RB [ \-t | \-\-test ]
+.RB [ \-v | \-\-verbose ]
+SourceVolumeGroupName DestinationVolumeGroupName
+[ PhysicalVolumePath ...]
+.SH DESCRIPTION
+.B vgsplit
+moves one or more physical volumes from
+.I SourceVolumeGroupName
+into
+.I DestinationVolumeGroupName\fP. The physical volumes moved can be
+specified either explicitly via \fIPhysicalVolumePath\fP, or implicitly by
+\fB-n\fP \fILogicalVolumeName\fP, in which case only physical volumes
+underlying the specified logical volume will be moved.
+
+If
+.I DestinationVolumeGroupName
+does not exist, a new volume group will be created. The default attributes
+for the new volume group can be specified with \fB\-\-alloc\fR,
+\fB\-\-clustered\fR, \fB\-\-maxlogicalvolumes\fR, \fB\-\-metadatatype\fR,
+\fB\-\-maxphysicalvolumes\fR and \fB\-\-[vg]metadatacopies\fR,
+(see \fBvgcreate(8)\fR for a description of these options). If any
+of these options are not given, default attribute(s) are taken from
+.I SourceVolumeGroupName\fP. If a non-LVM2 metadata type (e.g. lvm1) is
+being used, you should use the -M option to specify the metadata type
+directly.
+
+If
+.I DestinationVolumeGroupName
+does exist, it will be checked for compatibility with
+.I SourceVolumeGroupName
+before the physical volumes are moved. Specifying any of the above default
+volume group attributes with an existing destination volume group is an error,
+and no split will occur.
+
+Logical volumes cannot be split between volume groups. \fBVgsplit(8)\fP only
+moves complete physical volumes: To move part of a physical volume, use
+\fBpvmove(8)\fP. Each existing logical volume must be entirely on the physical
+volumes forming either the source or the destination volume group. For this
+reason, \fBvgsplit(8)\fP may fail with an error if a split would result in a
+logical volume being split across volume groups.
+
+A \fBvgsplit\fP into an existing volume group retains the existing volume group's
+value of \fPvgmetadatacopies\fP (see \fBvgcreate\fP and \fBlvm.conf\fP for further
+explanation of \fPvgmetadatacopies\fP). To change the value of
+\fBvgmetadatacopies\fP, use \fBvgchange\fP.
+
+.SH OPTIONS
+See \fBlvm\fP for common options.
+.SH SEE ALSO
+.BR lvm (8),
+.BR vgcreate (8),
+.BR vgextend (8),
+.BR vgreduce (8),
+.BR vgmerge (8)
--- /dev/null
+* Wed Jan 05 2011 Paolo Capriotti <paolo.capriotti@collabora.co.uk> - 1.02.60
+- Update to latest upstream version (fixes BMC#12710)
+- Build from LVM2 source.
+- Remove obsolete patch.
+
+* Thu Dec 25 2008 Arjan van de Ven <arjan@linux.intel.com> 1.02.28
+- Fix Source: to be a resolvable URL
+
+* Tue Dec 09 2008 Yi Yang <yi.y.yang@intel.com> 1.02.28
+- Upgrade to 1.02.28
+- Fixed corrupt primary.xml.gz introduced by libdevmapper.pc
+
+* Sun Nov 02 2008 Anas Nashif <anas.nashif@intel.com> 1.02.24
+- fixed file list
--- /dev/null
+%define device_mapper_version 1.02.28
+%define lvm2_version 2.02.79
+
+# Do not reset Release to 1 unless both lvm2 and device-mapper
+# versions are increased together.
+
+Name: device-mapper
+Summary: Device mapper utility
+Version: %{device_mapper_version}
+Release: 1
+License: GPLv2
+Source0: ftp://sources.redhat.com/pub/lvm2/LVM2.%{lvm2_version}.tgz
+Group: System/Base
+URL: http://sources.redhat.com/dm
+Requires: device-mapper-libs = %{device_mapper_version}-%{release}
+
+%description -n device-mapper
+This package contains the supporting userspace utility, dmsetup,
+for the kernel device-mapper.
+
+%package -n device-mapper-devel
+Summary: Development libraries and headers for device-mapper
+Version: %{device_mapper_version}
+Release: %{release}
+License: LGPLv2.1
+Group: Development/Libraries
+Requires: device-mapper = %{device_mapper_version}-%{release}
+Requires: device-mapper-libs = %{device_mapper_version}-%{release}
+
+%description -n device-mapper-devel
+This package contains files needed to develop applications that use
+the device-mapper libraries.
+
+%package -n device-mapper-libs
+Summary: Device-mapper shared library
+Version: %{device_mapper_version}
+Release: %{release}
+License: LGPLv2.1
+Group: System/Libraries
+Obsoletes: device-mapper < 1.02.17-6
+
+%description -n device-mapper-libs
+This package contains the device-mapper shared library, libdevmapper.
+
+%prep
+%setup -q -n LVM2.%{lvm2_version}
+
+%build
+%define _exec_prefix ""
+%configure --with-user= --with-group= --with-device-uid=0 --with-device-gid=6 --with-device-mode=0660 --enable-pkgconfig
+%define _exec_prefix /
+make device-mapper
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install_device-mapper DESTDIR=$RPM_BUILD_ROOT usrlibdir=$RPM_BUILD_ROOT/usr/%{_lib}
+sed -i 's/ (.*)//g' $RPM_BUILD_ROOT%{_libdir}/pkgconfig/*.pc
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post -n device-mapper-libs -p /sbin/ldconfig
+
+%postun -n device-mapper-libs -p /sbin/ldconfig
+
+%files
+%defattr(-,root,root,-)
+%doc COPYING COPYING.LIB INSTALL README VERSION_DM WHATS_NEW_DM
+%attr(755,root,root) %{_sbindir}/dmsetup
+%{_mandir}/man8/dmsetup.8.gz
+
+%files -n device-mapper-devel
+%defattr(-,root,root,-)
+%attr(755,root,root) /%{_libdir}/libdevmapper.so
+%{_includedir}/libdevmapper.h
+%{_libdir}/pkgconfig/*.pc
+
+%files -n device-mapper-libs
+%attr(755,root,root) /%{_libdir}/libdevmapper.so.*
+
+
--- /dev/null
+#
+# Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+LANGS=de
+
+TARGETS=$(LANGS:%=lvm2_%.mo) $(LANGS:%=dm_%.mo)
+
+DM_POSOURCES = $(top_srcdir)/dmsetup/*.pot $(top_srcdir)/libdm/*.pot \
+ $(top_srcdir)/libdm/*/*.pot
+
+LVM_POSOURCES = $(top_srcdir)/tools/*.pot $(top_srcdir)/lib/*/*.pot
+
+include $(top_builddir)/make.tmpl
+
+lvm2.po: Makefile $(LVM_POSOURCES)
+ @echo Compiling string table
+ @xgettext -C -F --keyword=print_log --keyword=log_debug \
+ --keyword=log_info --keyword=_ --keyword=N_ \
+ --keyword=log_notice --keyword=log_warn --keyword=log_err \
+ --keyword=log_fatal --keyword=log_debug --keyword=log_error \
+ --keyword=log_print --keyword=log_verbose \
+ --keyword=log_very_verbose -d - \
+ $(LVM_POSOURCES) > $@
+
+device-mapper.po: Makefile $(DM_POSOURCES)
+ @echo Compiling string table
+ @xgettext -C -F --keyword=dm_log --keyword=log_debug \
+ --keyword=log_info --keyword=_ --keyword=N_ \
+ --keyword=log_notice --keyword=log_warn --keyword=log_err \
+ --keyword=log_fatal --keyword=log_debug --keyword=log_error \
+ --keyword=log_print --keyword=log_verbose \
+ --keyword=log_very_verbose -d - \
+ $(DM_POSOURCES) > $@
+
+pofile: lvm2.po device-mapper.po
+
+# FIXME
+install: $(TARGETS)
+ @echo Installing translation files in $(localedir)
+ @( \
+ for lang in $(LANGS); do \
+ $(INSTALL_DATA) -D $$lang.mo \
+ $(localedir)/$$lang/LC_MESSAGES/lvm2.mo;\
+ done; \
+ )
+ @( \
+ for lang in $(LANGS); do \
+ $(INSTALL_DATA) -D $$lang.mo \
+ $(localedir)/$$lang/LC_MESSAGES/device-mapper.mo;\
+ done; \
+ )
--- /dev/null
+# Dummy test file
+msgid ""
+msgstr ""
+"PO-Revision-Date: 2004-02-13 20:35+0000\n"
+"Last-Translator: Nobody <nobody@nowhere>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-15\n"
+"Content-Transfer-Encoding: 8bit\n"
+
--- /dev/null
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-04-27 21:46+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: activate/activate.c:44
+msgid "LVM1 proc global snprintf failed"
+msgstr ""
+
+#: activate/activate.c:63
+msgid "module string allocation failed"
+msgstr ""
+
+#: activate/activate.c:74 activate/activate.c:91 activate/activate.c:109
+#: activate/activate.c:364 activate/activate.c:417 activate/activate.c:438
+#: activate/activate.c:445 activate/activate.c:492 activate/activate.c:495
+#: activate/activate.c:514 activate/activate.c:520 activate/activate.c:523
+#: activate/activate.c:536 activate/activate.c:548 activate/activate.c:561
+#: activate/activate.c:564 activate/activate.c:576 activate/activate.c:579
+#: activate/activate.c:591 activate/activate.c:594 activate/activate.c:606
+#: activate/activate.c:609 activate/activate.c:764 activate/activate.c:768
+#: activate/activate.c:776 activate/activate.c:785 activate/activate.c:791
+#: activate/activate.c:836 activate/activate.c:848 activate/activate.c:882
+#: activate/activate.c:894 activate/activate.c:953 activate/activate.c:967
+#: activate/activate.c:996 activate/dev_manager.c:104
+#: activate/dev_manager.c:130 activate/dev_manager.c:139
+#: activate/dev_manager.c:142 activate/dev_manager.c:168
+#: activate/dev_manager.c:176 activate/dev_manager.c:250
+#: activate/dev_manager.c:258 activate/dev_manager.c:261
+#: activate/dev_manager.c:339 activate/dev_manager.c:347
+#: activate/dev_manager.c:350 activate/dev_manager.c:379
+#: activate/dev_manager.c:434 activate/dev_manager.c:439
+#: activate/dev_manager.c:452 activate/dev_manager.c:489
+#: activate/dev_manager.c:492 activate/dev_manager.c:500
+#: activate/dev_manager.c:523 activate/dev_manager.c:535
+#: activate/dev_manager.c:611 activate/dev_manager.c:628
+#: activate/dev_manager.c:631 activate/dev_manager.c:654
+#: activate/dev_manager.c:658 activate/dev_manager.c:661
+#: activate/dev_manager.c:664 activate/dev_manager.c:682
+#: activate/dev_manager.c:689 activate/dev_manager.c:698
+#: activate/dev_manager.c:737 activate/dev_manager.c:757
+#: activate/dev_manager.c:760 activate/dev_manager.c:780
+#: activate/dev_manager.c:783 activate/dev_manager.c:788
+#: activate/dev_manager.c:842 activate/dev_manager.c:851
+#: activate/dev_manager.c:854 activate/dev_manager.c:860
+#: activate/dev_manager.c:866 activate/dev_manager.c:869
+#: activate/dev_manager.c:871 activate/dev_manager.c:877
+#: activate/dev_manager.c:891 activate/dev_manager.c:894
+#: activate/dev_manager.c:920 activate/dev_manager.c:929
+#: activate/dev_manager.c:996 activate/dev_manager.c:1010
+#: activate/dev_manager.c:1018 activate/dev_manager.c:1025
+#: activate/dev_manager.c:1030 activate/dev_manager.c:1038
+#: activate/dev_manager.c:1044 activate/dev_manager.c:1048
+#: activate/dev_manager.c:1052 activate/dev_manager.c:1075
+#: activate/dev_manager.c:1138 activate/fs.c:179 activate/fs.c:229
+#: activate/fs.c:236 activate/fs.c:243 activate/fs.c:246 activate/fs.c:320
+#: archiver.c:68 archiver.c:75 archiver.c:87 archiver.c:163 archiver.c:236
+#: archiver.c:286 archiver.c:303 archiver.c:345 archiver.c:350
+#: cache/lvmcache.c:486 cache/lvmcache.c:490 cache/lvmcache.c:704
+#: cache/lvmcache.c:724 cache/lvmcache.c:750 cache/lvmcache.c:810
+#: commands/toolcontext.c:276 commands/toolcontext.c:295
+#: commands/toolcontext.c:302 commands/toolcontext.c:379
+#: commands/toolcontext.c:394 commands/toolcontext.c:418
+#: commands/toolcontext.c:469 commands/toolcontext.c:685
+#: commands/toolcontext.c:781 config/config.c:148 config/config.c:161
+#: config/config.c:176 config/config.c:194 config/config.c:215
+#: config/config.c:235 config/config.c:282 config/config.c:285
+#: config/config.c:467 config/config.c:485 config/config.c:490
+#: config/config.c:500 config/config.c:514 config/config.c:530
+#: config/config.c:586 config/config.c:777 datastruct/btree.c:90
+#: datastruct/str_list.c:24 datastruct/str_list.c:38 datastruct/str_list.c:47
+#: datastruct/str_list.c:77 device/dev-cache.c:240 device/dev-cache.c:253
+#: device/dev-cache.c:298 device/dev-cache.c:302 device/dev-cache.c:373
+#: device/dev-cache.c:404 device/dev-cache.c:443 device/dev-cache.c:511
+#: device/dev-cache.c:547 device/dev-cache.c:552 device/dev-cache.c:567
+#: device/dev-io.c:174 device/dev-io.c:204 device/dev-io.c:358
+#: device/dev-io.c:556 device/dev-io.c:606 device/dev-io.c:624
+#: device/dev-io.c:643 device/dev-io.c:671 device/dev-md.c:41
+#: device/dev-md.c:49 device/dev-md.c:66 device/device.c:61 device/device.c:66
+#: device/device.c:90 display/display.c:243 display/display.c:274
+#: display/display.c:333 display/display.c:379 display/display.c:605
+#: display/display.c:641 error/errseg.c:101 filters/filter-composite.c:54
+#: filters/filter-persistent.c:46 filters/filter-persistent.c:110
+#: filters/filter-persistent.c:114 filters/filter-persistent.c:117
+#: filters/filter-persistent.c:197 filters/filter-persistent.c:299
+#: filters/filter-persistent.c:305 filters/filter-persistent.c:316
+#: filters/filter-regex.c:74 filters/filter-regex.c:101
+#: filters/filter-regex.c:119 filters/filter-regex.c:142
+#: filters/filter-regex.c:196 filters/filter-regex.c:201
+#: filters/filter-regex.c:206 filters/filter-regex.c:209
+#: filters/filter-sysfs.c:288 filters/filter.c:278 format1/disk-rep.c:221
+#: format1/disk-rep.c:233 format1/disk-rep.c:238 format1/disk-rep.c:257
+#: format1/disk-rep.c:260 format1/disk-rep.c:291 format1/disk-rep.c:294
+#: format1/disk-rep.c:313 format1/disk-rep.c:316 format1/disk-rep.c:334
+#: format1/disk-rep.c:351 format1/disk-rep.c:361 format1/disk-rep.c:421
+#: format1/disk-rep.c:428 format1/disk-rep.c:522 format1/disk-rep.c:547
+#: format1/disk-rep.c:563 format1/disk-rep.c:591 format1/disk-rep.c:609
+#: format1/disk-rep.c:646 format1/disk-rep.c:711 format1/disk-rep.c:718
+#: format1/disk-rep.c:734 format1/format1.c:134 format1/format1.c:137
+#: format1/format1.c:149 format1/format1.c:154 format1/format1.c:157
+#: format1/format1.c:160 format1/format1.c:163 format1/format1.c:166
+#: format1/format1.c:171 format1/format1.c:186 format1/format1.c:195
+#: format1/format1.c:198 format1/format1.c:213 format1/format1.c:227
+#: format1/format1.c:245 format1/format1.c:256 format1/format1.c:271
+#: format1/format1.c:297 format1/format1.c:302 format1/format1.c:307
+#: format1/format1.c:312 format1/format1.c:348 format1/format1.c:394
+#: format1/format1.c:410 format1/format1.c:415 format1/format1.c:421
+#: format1/format1.c:431 format1/format1.c:477 format1/format1.c:498
+#: format1/format1.c:507 format1/format1.c:551 format1/import-export.c:63
+#: format1/import-export.c:118 format1/import-export.c:151
+#: format1/import-export.c:168 format1/import-export.c:185
+#: format1/import-export.c:193 format1/import-export.c:228
+#: format1/import-export.c:233 format1/import-export.c:238
+#: format1/import-export.c:316 format1/import-export.c:448
+#: format1/import-export.c:453 format1/import-export.c:474
+#: format1/import-export.c:481 format1/import-export.c:503
+#: format1/import-export.c:524 format1/import-export.c:529
+#: format1/import-export.c:538 format1/import-export.c:548
+#: format1/import-export.c:558 format1/import-export.c:563
+#: format1/import-export.c:666 format1/import-export.c:714
+#: format1/import-extents.c:63 format1/import-extents.c:68
+#: format1/import-extents.c:71 format1/import-extents.c:122
+#: format1/import-extents.c:193 format1/import-extents.c:220
+#: format1/import-extents.c:235 format1/import-extents.c:284
+#: format1/import-extents.c:314 format1/import-extents.c:338
+#: format1/import-extents.c:354 format1/import-extents.c:369
+#: format1/layout.c:126 format1/lvm1-label.c:75 format1/vg_number.c:37
+#: format1/vg_number.c:42 format_pool/disk_rep.c:49 format_pool/disk_rep.c:102
+#: format_pool/disk_rep.c:256 format_pool/disk_rep.c:358
+#: format_pool/disk_rep.c:368 format_pool/disk_rep.c:373
+#: format_pool/format_pool.c:132 format_pool/format_pool.c:137
+#: format_pool/format_pool.c:142 format_pool/format_pool.c:152
+#: format_pool/format_pool.c:161 format_pool/format_pool.c:166
+#: format_pool/format_pool.c:186 format_pool/format_pool.c:195
+#: format_pool/format_pool.c:201 format_pool/format_pool.c:231
+#: format_pool/format_pool.c:236 format_pool/format_pool.c:246
+#: format_pool/format_pool.c:251 format_pool/import_export.c:93
+#: format_pool/import_export.c:180 format_pool/import_export.c:218
+#: format_pool/import_export.c:232 format_pool/import_export.c:256
+#: format_pool/import_export.c:276 format_pool/import_export.c:304
+#: format_pool/import_export.c:309 format_text/archive.c:117
+#: format_text/archive.c:138 format_text/archive.c:165
+#: format_text/archive.c:258 format_text/archive.c:274
+#: format_text/archive.c:350 format_text/archive.c:370
+#: format_text/archiver.c:82 format_text/archiver.c:89
+#: format_text/archiver.c:101 format_text/archiver.c:189
+#: format_text/archiver.c:267 format_text/archiver.c:317
+#: format_text/archiver.c:334 format_text/archiver.c:376
+#: format_text/archiver.c:381 format_text/export.c:138
+#: format_text/export.c:198 format_text/export.c:206 format_text/export.c:293
+#: format_text/export.c:294 format_text/export.c:295 format_text/export.c:296
+#: format_text/export.c:298 format_text/export.c:299 format_text/export.c:300
+#: format_text/export.c:303 format_text/export.c:313 format_text/export.c:317
+#: format_text/export.c:319 format_text/export.c:322 format_text/export.c:325
+#: format_text/export.c:329 format_text/export.c:332 format_text/export.c:336
+#: format_text/export.c:340 format_text/export.c:343 format_text/export.c:344
+#: format_text/export.c:348 format_text/export.c:349 format_text/export.c:373
+#: format_text/export.c:380 format_text/export.c:384 format_text/export.c:385
+#: format_text/export.c:389 format_text/export.c:393 format_text/export.c:395
+#: format_text/export.c:398 format_text/export.c:401 format_text/export.c:404
+#: format_text/export.c:408 format_text/export.c:411 format_text/export.c:415
+#: format_text/export.c:419 format_text/export.c:422 format_text/export.c:427
+#: format_text/export.c:431 format_text/export.c:440 format_text/export.c:443
+#: format_text/export.c:446 format_text/export.c:450 format_text/export.c:451
+#: format_text/export.c:455 format_text/export.c:458 format_text/export.c:463
+#: format_text/export.c:468 format_text/export.c:479 format_text/export.c:481
+#: format_text/export.c:488 format_text/export.c:492 format_text/export.c:497
+#: format_text/export.c:508 format_text/export.c:518 format_text/export.c:519
+#: format_text/export.c:524 format_text/export.c:528 format_text/export.c:531
+#: format_text/export.c:534 format_text/export.c:538 format_text/export.c:541
+#: format_text/export.c:545 format_text/export.c:549 format_text/export.c:551
+#: format_text/export.c:553 format_text/export.c:554 format_text/export.c:555
+#: format_text/export.c:560 format_text/export.c:566 format_text/export.c:581
+#: format_text/export.c:591 format_text/export.c:600 format_text/export.c:606
+#: format_text/export.c:624 format_text/export.c:627 format_text/export.c:634
+#: format_text/export.c:637 format_text/export.c:640 format_text/export.c:652
+#: format_text/export.c:657 format_text/export.c:660 format_text/export.c:665
+#: format_text/export.c:667 format_text/export.c:669 format_text/export.c:671
+#: format_text/export.c:673 format_text/export.c:677 format_text/export.c:680
+#: format_text/export.c:702 format_text/export.c:729 format_text/export.c:747
+#: format_text/flags.c:94 format_text/flags.c:138
+#: format_text/format-text.c:158 format_text/format-text.c:161
+#: format_text/format-text.c:195 format_text/format-text.c:199
+#: format_text/format-text.c:238 format_text/format-text.c:295
+#: format_text/format-text.c:346 format_text/format-text.c:378
+#: format_text/format-text.c:420 format_text/format-text.c:425
+#: format_text/format-text.c:433 format_text/format-text.c:451
+#: format_text/format-text.c:456 format_text/format-text.c:481
+#: format_text/format-text.c:494 format_text/format-text.c:542
+#: format_text/format-text.c:547 format_text/format-text.c:587
+#: format_text/format-text.c:601 format_text/format-text.c:619
+#: format_text/format-text.c:650 format_text/format-text.c:700
+#: format_text/format-text.c:757 format_text/format-text.c:762
+#: format_text/format-text.c:785 format_text/format-text.c:799
+#: format_text/format-text.c:1059 format_text/format-text.c:1064
+#: format_text/format-text.c:1072 format_text/format-text.c:1082
+#: format_text/format-text.c:1103 format_text/format-text.c:1107
+#: format_text/format-text.c:1113 format_text/format-text.c:1125
+#: format_text/format-text.c:1309 format_text/format-text.c:1365
+#: format_text/format-text.c:1370 format_text/format-text.c:1380
+#: format_text/format-text.c:1382 format_text/format-text.c:1390
+#: format_text/format-text.c:1430 format_text/format-text.c:1436
+#: format_text/format-text.c:1621 format_text/format-text.c:1627
+#: format_text/format-text.c:1666 format_text/format-text.c:1711
+#: format_text/format-text.c:1730 format_text/format-text.c:1746
+#: format_text/format-text.c:1751 format_text/format-text.c:1765
+#: format_text/format-text.c:1777 format_text/format-text.c:1783
+#: format_text/format-text.c:1813 format_text/format-text.c:1818
+#: format_text/format-text.c:1823 format_text/format-text.c:1832
+#: format_text/format-text.c:1935 format_text/import.c:47
+#: format_text/import.c:52 format_text/import.c:63 format_text/import.c:98
+#: format_text/import.c:115 format_text/import_vsn1.c:123
+#: format_text/import_vsn1.c:134 format_text/import_vsn1.c:167
+#: format_text/import_vsn1.c:237 format_text/import_vsn1.c:303
+#: format_text/import_vsn1.c:309 format_text/import_vsn1.c:322
+#: format_text/import_vsn1.c:387 format_text/import_vsn1.c:429
+#: format_text/import_vsn1.c:457 format_text/import_vsn1.c:465
+#: format_text/import_vsn1.c:482 format_text/import_vsn1.c:489
+#: format_text/import_vsn1.c:518 format_text/import_vsn1.c:576
+#: format_text/import_vsn1.c:629 format_text/import_vsn1.c:654
+#: format_text/import_vsn1.c:664 format_text/import_vsn1.c:667
+#: format_text/import_vsn1.c:735 format_text/import_vsn1.c:846
+#: format_text/tags.c:28 format_text/tags.c:35 format_text/tags.c:42
+#: format_text/tags.c:48 format_text/tags.c:67 format_text/text_label.c:210
+#: format_text/text_label.c:246 label/label.c:90 label/label.c:207
+#: label/label.c:258 label/label.c:274 label/label.c:284 label/label.c:291
+#: label/label.c:321 label/label.c:329 label/label.c:341 label/label.c:360
+#: label/label.c:364 label/label.c:370 locking/cluster_locking.c:85
+#: locking/cluster_locking.c:420 locking/cluster_locking.c:432
+#: locking/cluster_locking.c:436 locking/external_locking.c:77 lvchange.c:57
+#: lvchange.c:99 lvchange.c:116 lvchange.c:122 lvchange.c:136 lvchange.c:143
+#: lvchange.c:150 lvchange.c:268 lvchange.c:282 lvchange.c:353 lvchange.c:361
+#: lvchange.c:395 lvchange.c:472 lvchange.c:479 lvchange.c:526 lvchange.c:534
+#: lvconvert.c:96 lvconvert.c:147 lvconvert.c:211 lvconvert.c:222
+#: lvconvert.c:273 lvconvert.c:285 lvconvert.c:298 lvconvert.c:332
+#: lvconvert.c:354 lvconvert.c:369 lvconvert.c:378 lvconvert.c:397
+#: lvconvert.c:404 lvconvert.c:470 lvconvert.c:481 lvconvert.c:544
+#: lvconvert.c:585 lvcreate.c:133 lvcreate.c:349 lvcreate.c:373 lvcreate.c:399
+#: lvcreate.c:529 lvcreate.c:661 lvcreate.c:698 lvcreate.c:728 lvcreate.c:755
+#: lvcreate.c:763 lvcreate.c:769 lvcreate.c:776 lvcreate.c:868
+#: lvmcmdline.c:830 lvmcmdline.c:836 lvmcmdline.c:839 lvmcmdline.c:842
+#: lvmcmdline.c:846 lvmcmdline.c:853 lvmcmdline.c:885 lvmcmdline.c:896
+#: lvmcmdline.c:906 lvmcmdline.c:936 lvmcmdline.c:1022 lvremove.c:99
+#: lvrename.c:85 lvrename.c:164 lvrename.c:175 lvrename.c:182 lvrename.c:188
+#: lvresize.c:466 lvresize.c:522 lvresize.c:529 lvresize.c:536 lvresize.c:547
+#: lvresize.c:554 lvresize.c:560 lvresize.c:579 lvresize.c:593 lvresize.c:618
+#: metadata/lv_manip.c:85 metadata/lv_manip.c:91 metadata/lv_manip.c:192
+#: metadata/lv_manip.c:227 metadata/lv_manip.c:258 metadata/lv_manip.c:316
+#: metadata/lv_manip.c:325 metadata/lv_manip.c:340 metadata/lv_manip.c:349
+#: metadata/lv_manip.c:379 metadata/lv_manip.c:580 metadata/lv_manip.c:588
+#: metadata/lv_manip.c:623 metadata/lv_manip.c:735 metadata/lv_manip.c:738
+#: metadata/lv_manip.c:748 metadata/lv_manip.c:846 metadata/lv_manip.c:874
+#: metadata/lv_manip.c:1048 metadata/lv_manip.c:1095 metadata/lv_manip.c:1100
+#: metadata/lv_manip.c:1130 metadata/lv_manip.c:1221 metadata/lv_manip.c:1228
+#: metadata/lv_manip.c:1265 metadata/lv_manip.c:1277 metadata/lv_manip.c:1306
+#: metadata/lv_manip.c:1316 metadata/lv_manip.c:1364 metadata/lv_manip.c:1429
+#: metadata/lv_manip.c:1436 metadata/lv_manip.c:1548 metadata/lv_manip.c:1619
+#: metadata/merge.c:253 metadata/merge.c:292 metadata/merge.c:297
+#: metadata/metadata.c:119 metadata/metadata.c:154 metadata/metadata.c:182
+#: metadata/metadata.c:252 metadata/metadata.c:276 metadata/metadata.c:284
+#: metadata/metadata.c:322 metadata/metadata.c:372 metadata/metadata.c:378
+#: metadata/metadata.c:384 metadata/metadata.c:395 metadata/metadata.c:401
+#: metadata/metadata.c:413 metadata/metadata.c:419 metadata/metadata.c:431
+#: metadata/metadata.c:439 metadata/metadata.c:446 metadata/metadata.c:453
+#: metadata/metadata.c:460 metadata/metadata.c:473 metadata/metadata.c:481
+#: metadata/metadata.c:490 metadata/metadata.c:549 metadata/metadata.c:564
+#: metadata/metadata.c:754 metadata/metadata.c:779 metadata/metadata.c:815
+#: metadata/metadata.c:846 metadata/metadata.c:874 metadata/metadata.c:880
+#: metadata/metadata.c:887 metadata/metadata.c:898 metadata/metadata.c:903
+#: metadata/metadata.c:925 metadata/metadata.c:947 metadata/metadata.c:964
+#: metadata/metadata.c:1063 metadata/metadata.c:1068 metadata/metadata.c:1079
+#: metadata/metadata.c:1137 metadata/metadata.c:1142 metadata/metadata.c:1168
+#: metadata/metadata.c:1183 metadata/metadata.c:1191 metadata/metadata.c:1246
+#: metadata/metadata.c:1250 metadata/metadata.c:1399 metadata/metadata.c:1433
+#: metadata/metadata.c:1490 metadata/metadata.c:1494 metadata/metadata.c:1527
+#: metadata/mirror.c:106 metadata/mirror.c:109 metadata/mirror.c:112
+#: metadata/mirror.c:205 metadata/mirror.c:484 metadata/mirror.c:526
+#: metadata/mirror.c:560 metadata/mirror.c:599 metadata/mirror.c:608
+#: metadata/mirror.c:736 metadata/mirror.c:757 metadata/mirror.c:762
+#: metadata/mirror.c:836 metadata/pv_manip.c:54 metadata/pv_manip.c:73
+#: metadata/pv_manip.c:94 metadata/pv_manip.c:131 metadata/pv_manip.c:156
+#: metadata/pv_manip.c:197 metadata/pv_manip.c:332 metadata/pv_map.c:44
+#: metadata/pv_map.c:92 metadata/pv_map.c:112 metadata/pv_map.c:122
+#: metadata/pv_map.c:149 metadata/pv_map.c:159 metadata/snapshot_manip.c:70
+#: metadata/snapshot_manip.c:77 mirror/mirrored.c:144 mirror/mirrored.c:149
+#: mirror/mirrored.c:151 mirror/mirrored.c:304 mirror/mirrored.c:328
+#: mirror/mirrored.c:331 mirror/mirrored.c:501 mirror/mirrored.c:552
+#: misc/lvm-file.c:291 misc/timestamp.c:44 pvchange.c:191 pvmove.c:102
+#: pvmove.c:107 pvmove.c:192 pvmove.c:220 pvmove.c:227 pvmove.c:292
+#: pvmove.c:299 pvmove.c:308 pvmove.c:337 pvmove.c:349 pvmove.c:356
+#: pvmove.c:363 pvmove.c:371 pvmove.c:383 pvmove.c:524 pvresize.c:165
+#: pvscan.c:55 report/report.c:187 report/report.c:513 report/report.c:543
+#: report/report.c:699 reporter.c:289 snapshot/snapshot.c:74
+#: snapshot/snapshot.c:83 snapshot/snapshot.c:84 snapshot/snapshot.c:85
+#: snapshot/snapshot.c:169 striped/striped.c:89 striped/striped.c:169
+#: striped/striped.c:172 striped/striped.c:216 toollib.c:912 toollib.c:962
+#: toollib.c:1020 toollib.c:1060 toollib.c:1085 toollib.c:1194 toollib.c:1332
+#: toollib.c:1337 toollib.c:1350 toollib.c:1357 uuid/uuid.c:90 uuid/uuid.c:94
+#: vgcfgbackup.c:69 vgcfgbackup.c:78 vgcfgbackup.c:85 vgchange.c:420
+#: vgmerge.c:193 vgreduce.c:29 vgreduce.c:96 vgreduce.c:102 vgreduce.c:124
+#: vgreduce.c:130 vgreduce.c:148 vgreduce.c:196 vgreduce.c:217 vgreduce.c:241
+#: vgreduce.c:307 vgreduce.c:353 zero/zero.c:99
+msgid "<backtrace>"
+msgstr ""
+
+#: activate/activate.c:81
+msgid "snap_seg module string allocation failed"
+msgstr ""
+
+#: activate/activate.c:245
+msgid "Activation enabled. Device-mapper kernel driver will be used."
+msgstr ""
+
+#: activate/activate.c:248
+msgid ""
+"WARNING: Activation disabled. No device-mapper interaction will be attempted."
+msgstr ""
+
+#: activate/activate.c:281
+msgid "Ignoring invalid string in config file activation/volume_list"
+msgstr ""
+
+#: activate/activate.c:287
+msgid "Ignoring empty string in config file activation/volume_list"
+msgstr ""
+
+#: activate/activate.c:296
+msgid "Ignoring empty tag in config file activation/volume_list"
+msgstr ""
+
+#: activate/activate.c:326
+#, c-format
+msgid "dm_snprintf error from %s/%s"
+msgstr ""
+
+#: activate/activate.c:350
+msgid "Getting driver version"
+msgstr ""
+
+#: activate/activate.c:362
+#, c-format
+msgid "Getting target version for %s"
+msgstr ""
+
+#: activate/activate.c:367
+#, c-format
+msgid "Failed to get %s target version"
+msgstr ""
+
+#: activate/activate.c:411
+#, c-format
+msgid "target_present module name too long: %s"
+msgstr ""
+
+#: activate/activate.c:440
+#, c-format
+msgid "Getting device info for %s"
+msgstr ""
+
+#: activate/activate.c:771
+#, c-format
+msgid "Skipping: Suspending '%s'."
+msgstr ""
+
+#: activate/activate.c:831
+#, c-format
+msgid "Skipping: Resuming '%s'."
+msgstr ""
+
+#: activate/activate.c:877
+#, c-format
+msgid "Skipping: Deactivating '%s'."
+msgstr ""
+
+#: activate/activate.c:888
+#, c-format
+msgid "LV %s/%s in use: not deactivating"
+msgstr ""
+
+#: activate/activate.c:917 activate/activate.c:942
+#, c-format
+msgid "Not activating %s/%s due to config file settings"
+msgstr ""
+
+#: activate/activate.c:948
+#, c-format
+msgid "Skipping: Activating '%s'."
+msgstr ""
+
+#: activate/dev_manager.c:75
+#, c-format
+msgid "_build_dlid: pool allocation failed for %zu %s %s."
+msgstr ""
+
+#: activate/dev_manager.c:136 activate/dev_manager.c:255
+#: activate/dev_manager.c:344
+msgid "Failed to disable open_count"
+msgstr ""
+
+#: activate/dev_manager.c:163
+msgid "Failed to allocate dm_task struct to check dev status"
+msgstr ""
+
+#: activate/dev_manager.c:171
+msgid "Failed to get state of mapped device"
+msgstr ""
+
+#: activate/dev_manager.c:229 activate/dev_manager.c:528
+#, c-format
+msgid "dlid build failed for %s"
+msgstr ""
+
+#: activate/dev_manager.c:360 activate/dev_manager.c:384
+#, c-format
+msgid "Number of segments in active LV %s does not match metadata"
+msgstr ""
+
+#: activate/dev_manager.c:394
+#, c-format
+msgid "LV percent: %f"
+msgstr ""
+
+#: activate/dev_manager.c:497
+#, c-format
+msgid "Getting device status percentage for %s"
+msgstr ""
+
+#: activate/dev_manager.c:532
+#, c-format
+msgid "Getting device mirror status percentage for %s"
+msgstr ""
+
+#: activate/dev_manager.c:633
+#, c-format
+msgid "Getting device info for %s [%s]"
+msgstr ""
+
+#: activate/dev_manager.c:635
+#, c-format
+msgid "Failed to get info for %s [%s]."
+msgstr ""
+
+#: activate/dev_manager.c:640
+#, c-format
+msgid "Failed to add device (%u:%u) to dtree"
+msgstr ""
+
+#: activate/dev_manager.c:677
+#, c-format
+msgid "Partial dtree creation failed for %s."
+msgstr ""
+
+#: activate/dev_manager.c:741
+#, c-format
+msgid "Internal error: Unassigned area found in LV %s."
+msgstr ""
+
+#: activate/dev_manager.c:775
+#, c-format
+msgid "Couldn't find snapshot for '%s'."
+msgstr ""
+
+#: activate/dev_manager.c:800
+#, c-format
+msgid "_emit_target: Internal error: Can't handle segment type %s"
+msgstr ""
+
+#: activate/dev_manager.c:828
+#, c-format
+msgid "Checking kernel supports %s segment type for %s%s%s"
+msgstr ""
+
+#: activate/dev_manager.c:834
+#, c-format
+msgid "Can't expand LV %s: %s target support missing from kernel?"
+msgstr ""
+
+#: activate/dev_manager.c:847
+msgid "Clustered snapshots are not yet supported"
+msgstr ""
+
+#: activate/dev_manager.c:902
+#, c-format
+msgid "_add_new_lv_to_dtree: pool alloc failed for %s %s."
+msgstr ""
+
+#: activate/dev_manager.c:961
+#, c-format
+msgid "_create_lv_symlinks: Couldn't split up old device name %s"
+msgstr ""
+
+#: activate/dev_manager.c:987
+#, c-format
+msgid "_clean_tree: Couldn't split up device name %s."
+msgstr ""
+
+#: activate/dev_manager.c:1013 activate/dev_manager.c:1133
+msgid "Lost dependency tree root node"
+msgstr ""
+
+#: activate/dev_manager.c:1055
+#, c-format
+msgid "Failed to create symlinks for %s."
+msgstr ""
+
+#: activate/dev_manager.c:1060
+#, c-format
+msgid "_tree_action: Action %u not supported."
+msgstr ""
+
+#: activate/dev_manager.c:1119
+msgid "partial dtree creation failed"
+msgstr ""
+
+#: activate/dev_manager.c:1124
+#, c-format
+msgid "Failed to add device %s (%u:%u) to dtree"
+msgstr ""
+
+#: activate/fs.c:35 activate/fs.c:58
+msgid "Couldn't construct name of volume group directory."
+msgstr ""
+
+#: activate/fs.c:43
+#, c-format
+msgid "Creating directory %s"
+msgstr ""
+
+#: activate/fs.c:45 activate/fs.c:80 activate/fs.c:100 activate/fs.c:153
+#: activate/fs.c:166 activate/fs.c:173 activate/fs.c:208
+#: commands/toolcontext.c:342 commands/toolcontext.c:820 config/config.c:209
+#: config/config.c:247 config/config.c:262 config/config.c:328
+#: config/config.c:428 config/config.c:452 device/dev-cache.c:208
+#: device/dev-cache.c:212 device/dev-cache.c:394 device/dev-cache.c:417
+#: device/dev-cache.c:424 device/dev-cache.c:681 device/dev-cache.c:683
+#: device/dev-io.c:131 device/dev-io.c:231 device/dev-io.c:249
+#: device/dev-io.c:254 device/dev-io.c:256 device/dev-io.c:262
+#: device/dev-io.c:396 device/dev-io.c:398 device/dev-io.c:481
+#: filters/filter-persistent.c:203 filters/filter-persistent.c:207
+#: filters/filter-persistent.c:230 filters/filter-persistent.c:243
+#: filters/filter-sysfs.c:42 filters/filter-sysfs.c:58
+#: filters/filter-sysfs.c:156 filters/filter-sysfs.c:163
+#: filters/filter-sysfs.c:182 filters/filter-sysfs.c:225 filters/filter.c:164
+#: filters/filter.c:221 filters/filter.c:232 filters/filter.c:240
+#: filters/filter.c:253 format_text/archive.c:214 format_text/archive.c:223
+#: format_text/archive.c:253 format_text/archive.c:260
+#: format_text/archive.c:265 format_text/format-text.c:873
+#: format_text/format-text.c:875 format_text/format-text.c:884
+#: format_text/format-text.c:889 format_text/format-text.c:891
+#: format_text/format-text.c:896 format_text/format-text.c:921
+#: format_text/format-text.c:983 format_text/format-text.c:988
+#: format_text/format-text.c:1013 format_text/format-text.c:1040
+#: locking/file_locking.c:61 locking/file_locking.c:69
+#: locking/file_locking.c:72 locking/file_locking.c:105
+#: locking/file_locking.c:167 locking/file_locking.c:171
+#: locking/file_locking.c:187 locking/file_locking.c:296
+#: locking/file_locking.c:301 locking/locking.c:45 locking/locking.c:50
+#: locking/locking.c:66 locking/locking.c:221 log/log.c:69 lvmcmdline.c:1092
+#: lvmcmdline.c:1130 misc/lvm-exec.c:42 misc/lvm-file.c:47 misc/lvm-file.c:70
+#: misc/lvm-file.c:97 misc/lvm-file.c:107 misc/lvm-file.c:157
+#: misc/lvm-file.c:170 misc/lvm-file.c:199 misc/lvm-file.c:208
+#: misc/lvm-file.c:236 misc/lvm-file.c:241 misc/lvm-file.c:244
+#: misc/lvm-file.c:289 misc/lvm-file.c:297 misc/timestamp.c:47 mm/memlock.c:97
+#: mm/memlock.c:105 mm/memlock.c:116 uuid/uuid.c:83 uuid/uuid.c:88
+#, c-format
+msgid "%s: %s failed: %s"
+msgstr ""
+
+#: activate/fs.c:64
+#, c-format
+msgid "Removing directory %s"
+msgstr ""
+
+#: activate/fs.c:91
+#, c-format
+msgid "Couldn't create path for %s"
+msgstr ""
+
+#: activate/fs.c:98 activate/fs.c:151 activate/fs.c:164
+#, c-format
+msgid "Removing %s"
+msgstr ""
+
+#: activate/fs.c:114
+#, c-format
+msgid "Couldn't create path for volume group dir %s"
+msgstr ""
+
+#: activate/fs.c:121
+#, c-format
+msgid "Couldn't create source pathname for logical volume link %s"
+msgstr ""
+
+#: activate/fs.c:128
+#, c-format
+msgid "Couldn't create destination pathname for logical volume link for %s"
+msgstr ""
+
+#: activate/fs.c:135
+#, c-format
+msgid "Couldn't create pathname for LVM1 group file for %s"
+msgstr ""
+
+#: activate/fs.c:146
+#, c-format
+msgid "Non-LVM1 character device found at %s"
+msgstr ""
+
+#: activate/fs.c:159
+#, c-format
+msgid "Symbolic link %s not created: file exists"
+msgstr ""
+
+#: activate/fs.c:171
+#, c-format
+msgid "Linking %s -> %s"
+msgstr ""
+
+#: activate/fs.c:195
+msgid "Couldn't determine link pathname."
+msgstr ""
+
+#: activate/fs.c:202
+#, c-format
+msgid "%s not symbolic link - not removing"
+msgstr ""
+
+#: activate/fs.c:206
+#, c-format
+msgid "Removing link %s"
+msgstr ""
+
+#: activate/fs.c:282
+msgid "No space to stack fs operation"
+msgstr ""
+
+#: archiver.c:40 format_text/archiver.c:53
+msgid "Couldn't copy archive directory name."
+msgstr ""
+
+#: archiver.c:102 format_text/archiver.c:116
+msgid "Test mode: Skipping archiving of volume group."
+msgstr ""
+
+#: archiver.c:109
+#, c-format
+msgid "Archiving volume group \"%s\" metadata."
+msgstr ""
+
+#: archiver.c:111 format_text/archiver.c:131
+#, c-format
+msgid "Volume group \"%s\" metadata archive failed."
+msgstr ""
+
+#: archiver.c:138 format_text/archiver.c:164
+msgid "Couldn't copy backup directory name."
+msgstr ""
+
+#: archiver.c:169 format_text/archiver.c:195
+msgid "Failed to generate volume group metadata backup filename."
+msgstr ""
+
+#: archiver.c:180 format_text/archiver.c:206
+msgid "WARNING: This metadata update is NOT backed up"
+msgstr ""
+
+#: archiver.c:185 format_text/archiver.c:211
+msgid "Test mode: Skipping volume group backup."
+msgstr ""
+
+#: archiver.c:193 format_text/archiver.c:224
+#, c-format
+msgid "Backup of volume group %s metadata failed."
+msgstr ""
+
+#: archiver.c:207 format_text/archiver.c:238
+msgid "Failed to generate backup filename (for removal)."
+msgstr ""
+
+#: archiver.c:230 format_text/archiver.c:261
+msgid "Couldn't create text format object."
+msgstr ""
+
+#: archiver.c:259 format_text/archiver.c:290
+msgid "Failed to allocate format instance"
+msgstr ""
+
+#: archiver.c:267 format_text/archiver.c:298
+#, c-format
+msgid "PV %s missing from cache"
+msgstr ""
+
+#: archiver.c:272
+#, c-format
+msgid "PV %s is a different format (%s)"
+msgstr ""
+
+#: archiver.c:279 format_text/archiver.c:310
+#, c-format
+msgid "Format-specific setup for %s failed"
+msgstr ""
+
+#: archiver.c:316 format_text/archiver.c:347
+msgid "Failed to generate backup filename (for restore)."
+msgstr ""
+
+#: archiver.c:333
+#, c-format
+msgid "Creating volume group backup \"%s\""
+msgstr ""
+
+#: archiver.c:338 format_text/archiver.c:369
+msgid "Couldn't create backup object."
+msgstr ""
+
+#: cache/lvmcache.c:56 cache/lvmcache.c:235 cache/lvmcache.c:740
+msgid "Internal cache initialisation failed"
+msgstr ""
+
+#: cache/lvmcache.c:61
+#, c-format
+msgid "Cache locking failure for %s"
+msgstr ""
+
+#: cache/lvmcache.c:127
+msgid "device_list element allocation failed"
+msgstr ""
+
+#: cache/lvmcache.c:245 toollib.c:638
+msgid "dev_iter creation failed"
+msgstr ""
+
+#: cache/lvmcache.c:278
+msgid "vgids list allocation failed"
+msgstr ""
+
+#: cache/lvmcache.c:285 cache/lvmcache.c:308 cache/lvmcache.c:334
+#: toollib.c:271 toollib.c:306 toollib.c:314 toollib.c:326 toollib.c:405
+#: toollib.c:547 toollib.c:561 toollib.c:698
+msgid "strlist allocation failed"
+msgstr ""
+
+#: cache/lvmcache.c:301
+msgid "vgnames list allocation failed"
+msgstr ""
+
+#: cache/lvmcache.c:324
+msgid "pvids list allocation failed"
+msgstr ""
+
+#: cache/lvmcache.c:395
+#, c-format
+msgid "vg hash re-insertion failed: %s"
+msgstr ""
+
+#: cache/lvmcache.c:440
+#, c-format
+msgid "_lvmcache_update: pvid insertion failed: %s"
+msgstr ""
+
+#: cache/lvmcache.c:456
+#, c-format
+msgid "lvmcache: %s: clearing VGID"
+msgstr ""
+
+#: cache/lvmcache.c:463
+#, c-format
+msgid "_lvmcache_update: vgid hash insertion failed: %s"
+msgstr ""
+
+#: cache/lvmcache.c:468
+#, c-format
+msgid "lvmcache: %s: setting %s VGID to %s"
+msgstr ""
+
+#: cache/lvmcache.c:502
+#, c-format
+msgid ""
+"WARNING: Duplicate VG name %s: Existing %s takes precedence over exported %s"
+msgstr ""
+
+#: cache/lvmcache.c:508
+#, c-format
+msgid "WARNING: Duplicate VG name %s: %s takes precedence over exported %s"
+msgstr ""
+
+#: cache/lvmcache.c:516
+#, c-format
+msgid ""
+"WARNING: Duplicate VG name %s: Existing %s (created here) takes precedence "
+"over %s"
+msgstr ""
+
+#: cache/lvmcache.c:521
+#, c-format
+msgid ""
+"WARNING: Duplicate VG name %s: %s (with creation_host) takes precedence over "
+"%s"
+msgstr ""
+
+#: cache/lvmcache.c:529
+#, c-format
+msgid ""
+"WARNING: Duplicate VG name %s: %s (created here) takes precedence over %s"
+msgstr ""
+
+#: cache/lvmcache.c:547
+#, c-format
+msgid "cache_update: vg hash insertion failed: %s"
+msgstr ""
+
+#: cache/lvmcache.c:619
+msgid "lvmcache_update_vgname: list alloc failed"
+msgstr ""
+
+#: cache/lvmcache.c:625
+#, c-format
+msgid "cache vgname alloc failed for %s"
+msgstr ""
+
+#: cache/lvmcache.c:652
+#, c-format
+msgid "lvmcache: %s: now %s%s%s%s%s"
+msgstr ""
+
+#: cache/lvmcache.c:668
+#, c-format
+msgid "lvmcache: %s: VG %s %s exported"
+msgstr ""
+
+#: cache/lvmcache.c:685
+#, c-format
+msgid "cache creation host alloc failed for %s"
+msgstr ""
+
+#: cache/lvmcache.c:690
+#, c-format
+msgid "lvmcache: %s: VG %s: Set creation host to %s."
+msgstr ""
+
+#: cache/lvmcache.c:754
+msgid "lvmcache_info allocation failed"
+msgstr ""
+
+#: cache/lvmcache.c:769
+#, c-format
+msgid "Ignoring duplicate PV %s on %s - using md %s"
+msgstr ""
+
+#: cache/lvmcache.c:776
+#, c-format
+msgid "Ignoring duplicate PV %s on %s - using dm %s"
+msgstr ""
+
+#: cache/lvmcache.c:783
+#, c-format
+msgid "Duplicate PV %s on %s - using md %s"
+msgstr ""
+
+#: cache/lvmcache.c:789
+#, c-format
+msgid "Duplicate PV %s on %s - using dm %s"
+msgstr ""
+
+#: cache/lvmcache.c:798
+#, c-format
+msgid "Found duplicate PV %s: using %s not %s"
+msgstr ""
+
+#: cache/lvmcache.c:872
+msgid "Wiping internal VG cache"
+msgstr ""
+
+#: commands/toolcontext.c:70
+msgid "LVM_SYSTEM_DIR environment variable is too long."
+msgstr ""
+
+#: commands/toolcontext.c:146
+#, c-format
+msgid "Logging initialised at %s"
+msgstr ""
+
+#: commands/toolcontext.c:165
+#, c-format
+msgid "Set umask to %04o"
+msgstr ""
+
+#: commands/toolcontext.c:171 commands/toolcontext.c:182
+msgid "Device directory given in config file too long"
+msgstr ""
+
+#: commands/toolcontext.c:187
+#, c-format
+msgid "Warning: proc dir %s not found - some checks will be bypassed"
+msgstr ""
+
+#: commands/toolcontext.c:207 lvmcmdline.c:723
+msgid "Invalid units specification"
+msgstr ""
+
+#: commands/toolcontext.c:216
+#, c-format
+msgid "Setting host tag: %s"
+msgstr ""
+
+#: commands/toolcontext.c:219
+#, c-format
+msgid "_set_tag: str_list_add %s failed"
+msgstr ""
+
+#: commands/toolcontext.c:243
+#, c-format
+msgid "Invalid hostname string for tag %s"
+msgstr ""
+
+#: commands/toolcontext.c:254
+msgid "host_filter not supported yet"
+msgstr ""
+
+#: commands/toolcontext.c:289
+#, c-format
+msgid "Invalid tag in config file: %s"
+msgstr ""
+
+#: commands/toolcontext.c:322
+msgid "LVM_SYSTEM_DIR or tag was too long"
+msgstr ""
+
+#: commands/toolcontext.c:327
+msgid "config_tree_list allocation failed"
+msgstr ""
+
+#: commands/toolcontext.c:332
+msgid "config_tree allocation failed"
+msgstr ""
+
+#: commands/toolcontext.c:347
+#, c-format
+msgid "Loading config file: %s"
+msgstr ""
+
+#: commands/toolcontext.c:349
+#, c-format
+msgid "Failed to load config file %s"
+msgstr ""
+
+#: commands/toolcontext.c:372 commands/toolcontext.c:410
+msgid "Failed to create config tree"
+msgstr ""
+
+#: commands/toolcontext.c:473
+msgid "Failed to add /dev to internal device cache"
+msgstr ""
+
+#: commands/toolcontext.c:477
+msgid "device/scan not in config file: Defaulting to /dev"
+msgstr ""
+
+#: commands/toolcontext.c:484
+msgid "Invalid string in config file: devices/scan"
+msgstr ""
+
+#: commands/toolcontext.c:490 format_text/format-text.c:1980
+#, c-format
+msgid "Failed to add %s to internal device cache"
+msgstr ""
+
+#: commands/toolcontext.c:501
+msgid "Invalid string in config file: devices/loopfiles"
+msgstr ""
+
+#: commands/toolcontext.c:507
+#, c-format
+msgid "Failed to add loopfile %s to internal device cache"
+msgstr ""
+
+#: commands/toolcontext.c:546
+msgid "devices/filter not found in config file: no regex filter installed"
+msgstr ""
+
+#: commands/toolcontext.c:550
+msgid "Failed to create regex device filter"
+msgstr ""
+
+#: commands/toolcontext.c:557
+msgid "Failed to create lvm type filter"
+msgstr ""
+
+#: commands/toolcontext.c:602 commands/toolcontext.c:610
+msgid "Persistent cache filename too long."
+msgstr ""
+
+#: commands/toolcontext.c:615
+msgid "Failed to create persistent device filter"
+msgstr ""
+
+#: commands/toolcontext.c:634
+#, c-format
+msgid "Failed to load existing device cache from %s"
+msgstr ""
+
+#: commands/toolcontext.c:679
+msgid "Invalid string in config file: global/format_libraries"
+msgstr ""
+
+#: commands/toolcontext.c:690
+#, c-format
+msgid "Shared library %s does not contain format functions"
+msgstr ""
+
+#: commands/toolcontext.c:722
+#, c-format
+msgid "_init_formats: Default format (%s) not found"
+msgstr ""
+
+#: commands/toolcontext.c:775
+msgid "Invalid string in config file: global/segment_libraries"
+msgstr ""
+
+#: commands/toolcontext.c:786
+#, c-format
+msgid "Shared library %s does not contain segment type functions"
+msgstr ""
+
+#: commands/toolcontext.c:801
+#, c-format
+msgid "Duplicate segment type %s: unloading shared library %s"
+msgstr ""
+
+#: commands/toolcontext.c:825
+msgid "_init_hostname: dm_pool_strdup failed"
+msgstr ""
+
+#: commands/toolcontext.c:830
+msgid "_init_hostname: dm_pool_strdup kernel_vsn failed"
+msgstr ""
+
+#: commands/toolcontext.c:844
+msgid "WARNING: Metadata changes will NOT be backed up"
+msgstr ""
+
+#: commands/toolcontext.c:864
+#, c-format
+msgid "Couldn't create default archive path '%s/%s'."
+msgstr ""
+
+#: commands/toolcontext.c:873 commands/toolcontext.c:893
+msgid "backup_init failed."
+msgstr ""
+
+#: commands/toolcontext.c:885
+#, c-format
+msgid "Couldn't create default backup path '%s/%s'."
+msgstr ""
+
+#: commands/toolcontext.c:911
+msgid "setlocale failed"
+msgstr ""
+
+#: commands/toolcontext.c:920
+msgid "Failed to allocate command context"
+msgstr ""
+
+#: commands/toolcontext.c:940
+msgid ""
+"Failed to create LVM2 system dir for metadata backups, config files and "
+"internal cache."
+msgstr ""
+
+#: commands/toolcontext.c:942
+msgid ""
+"Set environment variable LVM_SYSTEM_DIR to alternative location or empty "
+"string."
+msgstr ""
+
+#: commands/toolcontext.c:948
+msgid "Library memory pool creation failed"
+msgstr ""
+
+#: commands/toolcontext.c:979
+msgid "Command memory pool creation failed"
+msgstr ""
+
+#: commands/toolcontext.c:1042
+msgid "Reloading config files"
+msgstr ""
+
+#: config/config.c:111
+msgid "Failed to allocate config pool."
+msgstr ""
+
+#: config/config.c:116
+msgid "Failed to allocate config tree."
+msgstr ""
+
+#: config/config.c:165
+msgid "Failed to allocate config tree parser."
+msgstr ""
+
+#: config/config.c:228
+#, c-format
+msgid "%s: Checksum error"
+msgstr ""
+
+#: config/config.c:268
+#, c-format
+msgid "%s is not a regular file"
+msgstr ""
+
+#: config/config.c:276
+#, c-format
+msgid "%s is empty"
+msgstr ""
+
+#: config/config.c:324
+#, c-format
+msgid "Config file %s has disappeared!"
+msgstr ""
+
+#: config/config.c:329
+msgid "Failed to reload configuration files"
+msgstr ""
+
+#: config/config.c:334
+#, c-format
+msgid "Configuration file %s is not a regular file"
+msgstr ""
+
+#: config/config.c:344
+#, c-format
+msgid "Detected config file change to %s"
+msgstr ""
+
+#: config/config.c:368
+#, c-format
+msgid "_write_value: Unknown value type: %d"
+msgstr ""
+
+#: config/config.c:432
+#, c-format
+msgid "Dumping configuration to %s"
+msgstr ""
+
+#: config/config.c:435 config/config.c:441
+#, c-format
+msgid "Failure while writing to %s"
+msgstr ""
+
+#: config/config.c:445
+#, c-format
+msgid "Configuration node %s not found"
+msgstr ""
+
+#: config/config.c:494 config/config.c:497 config/config.c:510
+#: config/config.c:512 config/config.c:527 config/config.c:541
+#: config/config.c:543 config/config.c:572 config/config.c:578
+#: config/config.c:590
+#, c-format
+msgid "Parse error at byte %td (line %d): unexpected token"
+msgstr ""
+
+#: config/config.c:594
+#, c-format
+msgid "Parse error at byte %td (line %d): expected a value"
+msgstr ""
+
+#: config/config.c:810
+#, c-format
+msgid "WARNING: Ignoring duplicate config node: %s (seeking %s)"
+msgstr ""
+
+#: config/config.c:858
+#, c-format
+msgid "Setting %s to %s"
+msgstr ""
+
+#: config/config.c:863
+#, c-format
+msgid "%s not found in config: defaulting to %s"
+msgstr ""
+
+#: config/config.c:881
+#, c-format
+msgid "Setting %s to %ld"
+msgstr ""
+
+#: config/config.c:885
+#, c-format
+msgid "%s not found in config: defaulting to %ld"
+msgstr ""
+
+#: config/config.c:903
+#, c-format
+msgid "Setting %s to %f"
+msgstr ""
+
+#: config/config.c:907
+#, c-format
+msgid "%s not found in config: defaulting to %f"
+msgstr ""
+
+#: device/dev-cache.c:64 device/dev-cache.c:81 device/dev-cache.c:118
+msgid "struct device allocation failed"
+msgstr ""
+
+#: device/dev-cache.c:68 device/dev-cache.c:85
+msgid "struct str_list allocation failed"
+msgstr ""
+
+#: device/dev-cache.c:73 device/dev-cache.c:90 device/dev-cache.c:95
+msgid "filename strdup failed"
+msgstr ""
+
+#: device/dev-cache.c:142
+#, c-format
+msgid "%s: New preferred name"
+msgstr ""
+
+#: device/dev-cache.c:247
+#, c-format
+msgid "%s: Already in device cache"
+msgstr ""
+
+#: device/dev-cache.c:260
+#, c-format
+msgid "%s: Aliased to %s in device cache%s"
+msgstr ""
+
+#: device/dev-cache.c:264
+#, c-format
+msgid "%s: Added to device cache"
+msgstr ""
+
+#: device/dev-cache.c:307
+msgid "Couldn't insert device into binary tree."
+msgstr ""
+
+#: device/dev-cache.c:314
+msgid "Couldn't add alias to dev cache."
+msgstr ""
+
+#: device/dev-cache.c:319
+msgid "Couldn't add name to hash in dev cache."
+msgstr ""
+
+#: device/dev-cache.c:399
+#, c-format
+msgid "%s: Not a regular file"
+msgstr ""
+
+#: device/dev-cache.c:429
+#, c-format
+msgid "%s: Symbolic link to directory"
+msgstr ""
+
+#: device/dev-cache.c:438
+#, c-format
+msgid "%s: Not a block device"
+msgstr ""
+
+#: device/dev-cache.c:496
+msgid ""
+"devices/preferred_names not found in config file: using built-in preferences"
+msgstr ""
+
+#: device/dev-cache.c:503
+msgid "preferred_names patterns must be enclosed in quotes"
+msgstr ""
+
+#: device/dev-cache.c:514
+msgid "Failed to allocate preferred device name pattern list."
+msgstr ""
+
+#: device/dev-cache.c:521
+msgid "Failed to allocate a preferred device name pattern."
+msgstr ""
+
+#: device/dev-cache.c:529
+msgid "Preferred device name pattern matcher creation failed."
+msgstr ""
+
+#: device/dev-cache.c:559
+msgid "Couldn't create binary tree for dev-cache."
+msgstr ""
+
+#: device/dev-cache.c:579
+#, c-format
+msgid "Device '%s' has been left open."
+msgstr ""
+
+#: device/dev-cache.c:617 device/dev-cache.c:643
+#, c-format
+msgid "Ignoring %s: %s"
+msgstr ""
+
+#: device/dev-cache.c:623
+#, c-format
+msgid "Ignoring %s: Not a directory"
+msgstr ""
+
+#: device/dev-cache.c:628
+msgid "dir_list allocation failed"
+msgstr ""
+
+#: device/dev-cache.c:649
+#, c-format
+msgid "Ignoring %s: Not a regular file"
+msgstr ""
+
+#: device/dev-cache.c:654
+msgid "dir_list allocation failed for file"
+msgstr ""
+
+#: device/dev-cache.c:686 device/dev-cache.c:690
+#, c-format
+msgid "Path %s no longer valid for device(%d,%d)"
+msgstr ""
+
+#: device/dev-cache.c:707
+#, c-format
+msgid "Aborting - please provide new pathname for what used to be %s"
+msgstr ""
+
+#: device/dev-cache.c:747
+msgid "dev_iter allocation failed"
+msgstr ""
+
+#: device/dev-io.c:67
+#, c-format
+msgid "Attempt to read an unopened device (%s)."
+msgstr ""
+
+#: device/dev-io.c:79
+#, c-format
+msgid "Read size too large: %lu"
+msgstr ""
+
+#: device/dev-io.c:84
+#, c-format
+msgid "%s: lseek %lu failed: %s"
+msgstr ""
+
+#: device/dev-io.c:98
+#, c-format
+msgid "%s: %s failed after %lu of %lu at %lu: %s"
+msgstr ""
+
+#: device/dev-io.c:134
+#, c-format
+msgid "%s: block size is %u bytes"
+msgstr ""
+
+#: device/dev-io.c:191
+msgid "Bounce buffer alloca failed"
+msgstr ""
+
+#: device/dev-io.c:238 device/dev-io.c:264
+#, c-format
+msgid "%s: size is %lu sectors"
+msgstr ""
+
+#: device/dev-io.c:343
+#, c-format
+msgid "WARNING: %s already opened read-only"
+msgstr ""
+
+#: device/dev-io.c:352
+#, c-format
+msgid "WARNING: dev_open(%s) called while suspended"
+msgstr ""
+
+#: device/dev-io.c:364
+#, c-format
+msgid "%s: stat failed: Has device name changed?"
+msgstr ""
+
+#: device/dev-io.c:390
+#, c-format
+msgid "%s: Not using O_DIRECT"
+msgstr ""
+
+#: device/dev-io.c:422
+#, c-format
+msgid "%s: fstat failed: Has device name changed?"
+msgstr ""
+
+#: device/dev-io.c:437
+#, c-format
+msgid "Opened %s %s%s%s"
+msgstr ""
+
+#: device/dev-io.c:486
+#, c-format
+msgid "Closed %s"
+msgstr ""
+
+#: device/dev-io.c:501
+#, c-format
+msgid "Attempt to close device '%s' which is not open."
+msgstr ""
+
+#: device/dev-io.c:515
+#, c-format
+msgid "%s: Immediate close attempt while still referenced"
+msgstr ""
+
+#: device/dev-io.c:576
+#, c-format
+msgid "Read from %s failed"
+msgstr ""
+
+#: device/dev-io.c:588
+#, c-format
+msgid "Circular read from %s failed"
+msgstr ""
+
+#: device/dev-io.c:648
+#, c-format
+msgid "Wiping %s at %lu length %zu"
+msgstr ""
+
+#: device/dev-io.c:651
+#, c-format
+msgid "Wiping %s at sector %lu length %zu sectors"
+msgstr ""
+
+#: display/display.c:145
+#, c-format
+msgid "Unrecognised allocation policy %s"
+msgstr ""
+
+#: display/display.c:172
+msgid "no memory for size display buffer"
+msgstr ""
+
+#: display/display.c:247
+#, c-format
+msgid "%s:%s:%lu:-1:%u:%u:-1:%u:%u:%u:%u:%s"
+msgstr ""
+
+#: display/display.c:278
+#, c-format
+msgid "--- %sPhysical volume ---"
+msgstr ""
+
+#: display/display.c:279
+#, c-format
+msgid "PV Name %s"
+msgstr ""
+
+#: display/display.c:280
+#, c-format
+msgid "VG Name %s%s"
+msgstr ""
+
+#: display/display.c:290
+#, c-format
+msgid "PV Size %s / not usable %s"
+msgstr ""
+
+#: display/display.c:296
+#, c-format
+msgid "PV Size %s"
+msgstr ""
+
+#: display/display.c:304
+#, c-format
+msgid "Allocatable yes %s"
+msgstr ""
+
+#: display/display.c:307
+msgid "Allocatable NO"
+msgstr ""
+
+#: display/display.c:312
+#, c-format
+msgid "PE Size (KByte) %u"
+msgstr ""
+
+#: display/display.c:313 display/display.c:592
+#, c-format
+msgid "Total PE %u"
+msgstr ""
+
+#: display/display.c:314
+#, c-format
+msgid "Free PE %u"
+msgstr ""
+
+#: display/display.c:315
+#, c-format
+msgid "Allocated PE %u"
+msgstr ""
+
+#: display/display.c:316 display/display.c:339
+#, c-format
+msgid "PV UUID %s"
+msgstr ""
+
+#: display/display.c:317 display/display.c:345 display/display.c:476
+#: display/display.c:527 display/display.c:610 format_text/archive.c:315
+#: lvmcmdline.c:769 mirror/mirrored.c:73 striped/striped.c:49
+msgid " "
+msgstr ""
+
+#: display/display.c:337
+#, c-format
+msgid "PV Name %s "
+msgstr ""
+
+#: display/display.c:340
+#, c-format
+msgid "PV Status %sallocatable"
+msgstr ""
+
+#: display/display.c:342
+#, c-format
+msgid "Total PE / Free PE %u / %u"
+msgstr ""
+
+#: display/display.c:355
+#, c-format
+msgid "%s%s/%s:%s:%d:%d:-1:%d:%lu:%d:-1:%d:%d:%d:%d"
+msgstr ""
+
+#: display/display.c:385
+msgid "--- Logical volume ---"
+msgstr ""
+
+#: display/display.c:387
+#, c-format
+msgid "LV Name %s%s/%s"
+msgstr ""
+
+#: display/display.c:389
+#, c-format
+msgid "VG Name %s"
+msgstr ""
+
+#: display/display.c:391
+#, c-format
+msgid "LV UUID %s"
+msgstr ""
+
+#: display/display.c:393
+#, c-format
+msgid "LV Write Access %s"
+msgstr ""
+
+#: display/display.c:397
+msgid "LV snapshot status source of"
+msgstr ""
+
+#: display/display.c:406
+#, c-format
+msgid " %s%s/%s [%s]"
+msgstr ""
+
+#: display/display.c:419
+#, c-format
+msgid "LV snapshot status %s destination for %s%s/%s"
+msgstr ""
+
+#: display/display.c:426
+msgid "LV Status suspended"
+msgstr ""
+
+#: display/display.c:428
+#, c-format
+msgid "LV Status %savailable"
+msgstr ""
+
+#: display/display.c:436
+#, c-format
+msgid "# open %u"
+msgstr ""
+
+#: display/display.c:438
+#, c-format
+msgid "LV Size %s"
+msgstr ""
+
+#: display/display.c:442
+#, c-format
+msgid "Current LE %u"
+msgstr ""
+
+#: display/display.c:446
+#, c-format
+msgid "COW-table size %s"
+msgstr ""
+
+#: display/display.c:448
+#, c-format
+msgid "COW-table LE %u"
+msgstr ""
+
+#: display/display.c:451
+#, c-format
+msgid "Allocated to snapshot %.2f%% "
+msgstr ""
+
+#: display/display.c:453
+#, c-format
+msgid "Snapshot chunk size %s"
+msgstr ""
+
+#: display/display.c:457
+#, c-format
+msgid "Segments %u"
+msgstr ""
+
+#: display/display.c:463
+#, c-format
+msgid "Allocation %s"
+msgstr ""
+
+#: display/display.c:464
+#, c-format
+msgid "Read ahead sectors %u"
+msgstr ""
+
+#: display/display.c:468
+#, c-format
+msgid "Persistent major %d"
+msgstr ""
+
+#: display/display.c:469
+#, c-format
+msgid "Persistent minor %d"
+msgstr ""
+
+#: display/display.c:473
+#, c-format
+msgid "Block device %d:%d"
+msgstr ""
+
+#: display/display.c:486
+#, c-format
+msgid "%sPhysical volume\t%s"
+msgstr ""
+
+#: display/display.c:492
+#, c-format
+msgid "%sPhysical extents\t%d to %d"
+msgstr ""
+
+#: display/display.c:497
+#, c-format
+msgid "%sLogical volume\t%s"
+msgstr ""
+
+#: display/display.c:502
+#, c-format
+msgid "%sLogical extents\t%d to %d"
+msgstr ""
+
+#: display/display.c:507
+#, c-format
+msgid "%sUnassigned area"
+msgstr ""
+
+#: display/display.c:515
+msgid "--- Segments ---"
+msgstr ""
+
+#: display/display.c:518
+#, c-format
+msgid "Logical extent %u to %u:"
+msgstr ""
+
+#: display/display.c:521
+#, c-format
+msgid " Type\t\t%s"
+msgstr ""
+
+#: display/display.c:547
+msgid "--- Volume group ---"
+msgstr ""
+
+#: display/display.c:548
+#, c-format
+msgid "VG Name %s"
+msgstr ""
+
+#: display/display.c:549
+#, c-format
+msgid "System ID %s"
+msgstr ""
+
+#: display/display.c:550
+#, c-format
+msgid "Format %s"
+msgstr ""
+
+#: display/display.c:552
+#, c-format
+msgid "Metadata Areas %d"
+msgstr ""
+
+#: display/display.c:554
+#, c-format
+msgid "Metadata Sequence No %d"
+msgstr ""
+
+#: display/display.c:557
+#, c-format
+msgid "VG Access %s%s%s%s"
+msgstr ""
+
+#: display/display.c:562
+#, c-format
+msgid "VG Status %s%sresizable"
+msgstr ""
+
+#: display/display.c:569
+msgid "Clustered yes"
+msgstr ""
+
+#: display/display.c:570
+#, c-format
+msgid "Shared %s"
+msgstr ""
+
+#: display/display.c:573
+#, c-format
+msgid "MAX LV %u"
+msgstr ""
+
+#: display/display.c:574
+#, c-format
+msgid "Cur LV %u"
+msgstr ""
+
+#: display/display.c:575
+#, c-format
+msgid "Open LV %u"
+msgstr ""
+
+#: display/display.c:581
+#, c-format
+msgid "Max PV %u"
+msgstr ""
+
+#: display/display.c:582
+#, c-format
+msgid "Cur PV %u"
+msgstr ""
+
+#: display/display.c:583
+#, c-format
+msgid "Act PV %u"
+msgstr ""
+
+#: display/display.c:585
+#, c-format
+msgid "VG Size %s"
+msgstr ""
+
+#: display/display.c:589
+#, c-format
+msgid "PE Size %s"
+msgstr ""
+
+#: display/display.c:594
+#, c-format
+msgid "Alloc PE / Size %u / %s"
+msgstr ""
+
+#: display/display.c:600
+#, c-format
+msgid "Free PE / Size %u / %s"
+msgstr ""
+
+#: display/display.c:609
+#, c-format
+msgid "VG UUID %s"
+msgstr ""
+
+#: display/display.c:645
+#, c-format
+msgid "%s:%s:%d:-1:%u:%u:%u:-1:%u:%u:%u:%lu:%u:%u:%u:%u:%s"
+msgstr ""
+
+#: display/display.c:669
+#, c-format
+msgid "\"%s\" %-9s [%-9s used / %s free]"
+msgstr ""
+
+#: display/display.c:686 display/display.c:695 pvscan.c:34
+#, c-format
+msgid "%s"
+msgstr ""
+
+#: error/errseg.c:73
+msgid "error module string list allocation failed"
+msgstr ""
+
+#: error/errseg.c:109 mirror/mirrored.c:562 snapshot/snapshot.c:179
+#: striped/striped.c:227 zero/zero.c:109
+#, c-format
+msgid "Initialised segtype: %s"
+msgstr ""
+
+#: filters/filter-composite.c:31
+#, c-format
+msgid "Using %s"
+msgstr ""
+
+#: filters/filter-composite.c:59
+msgid "composite filters allocation failed"
+msgstr ""
+
+#: filters/filter-composite.c:67
+msgid "compsoite filters allocation failed"
+msgstr ""
+
+#: filters/filter-md.c:31
+#, c-format
+msgid "%s: Skipping md component device"
+msgstr ""
+
+#: filters/filter-md.c:36
+#, c-format
+msgid "%s: Skipping: error in md component detection"
+msgstr ""
+
+#: filters/filter-md.c:54
+msgid "md filter allocation failed"
+msgstr ""
+
+#: filters/filter-persistent.c:57
+msgid "Wiping cache of LVM-capable devices"
+msgstr ""
+
+#: filters/filter-persistent.c:73
+#, c-format
+msgid "Couldn't find %s array in '%s'"
+msgstr ""
+
+#: filters/filter-persistent.c:84
+msgid "Devices array contains a value which is not a string ... ignoring"
+msgstr ""
+
+#: filters/filter-persistent.c:90
+#, c-format
+msgid "Couldn't add '%s' to filter ... ignoring"
+msgstr ""
+
+#: filters/filter-persistent.c:108
+#, c-format
+msgid "%s: stat failed: %s"
+msgstr ""
+
+#: filters/filter-persistent.c:132
+#, c-format
+msgid "Loaded persistent filter cache from %s"
+msgstr ""
+
+#: filters/filter-persistent.c:183
+#, c-format
+msgid "Internal persistent device cache empty - not writing to %s"
+msgstr ""
+
+#: filters/filter-persistent.c:188
+#, c-format
+msgid "Device cache incomplete - not writing to %s"
+msgstr ""
+
+#: filters/filter-persistent.c:193
+#, c-format
+msgid "Dumping persistent device cache to %s"
+msgstr ""
+
+#: filters/filter-persistent.c:248 format_text/format-text.c:902
+#: format_text/format-text.c:928 format_text/format-text.c:965
+#: misc/lvm-file.c:91
+#, c-format
+msgid "%s: rename to %s failed: %s"
+msgstr ""
+
+#: filters/filter-persistent.c:276
+#, c-format
+msgid "%s: Skipping (cached)"
+msgstr ""
+
+#: filters/filter-persistent.c:311
+msgid "Couldn't create hash table for persistent filter."
+msgstr ""
+
+#: filters/filter-regex.c:44
+msgid "pattern must begin with 'a' or 'r'"
+msgstr ""
+
+#: filters/filter-regex.c:83
+msgid "invalid separator at end of regex"
+msgstr ""
+
+#: filters/filter-regex.c:108
+msgid "filter patterns must be enclosed in quotes"
+msgstr ""
+
+#: filters/filter-regex.c:133
+msgid "invalid filter pattern"
+msgstr ""
+
+#: filters/filter-regex.c:174
+#, c-format
+msgid "%s: Skipping (regex)"
+msgstr ""
+
+#: filters/filter-sysfs.c:31
+msgid "No proc filesystem found: skipping sysfs filter"
+msgstr ""
+
+#: filters/filter-sysfs.c:37
+msgid "Failed to create /proc/mounts string"
+msgstr ""
+
+#: filters/filter-sysfs.c:137
+#, c-format
+msgid "Empty sysfs device file: %s"
+msgstr ""
+
+#: filters/filter-sysfs.c:142
+msgid "sysfs device file not correct format"
+msgstr ""
+
+#: filters/filter-sysfs.c:192
+#, c-format
+msgid "sysfs path name too long: %s in %s"
+msgstr ""
+
+#: filters/filter-sysfs.c:255
+#, c-format
+msgid "%s: Skipping (sysfs)"
+msgstr ""
+
+#: filters/filter-sysfs.c:278
+msgid "sysfs pool creation failed"
+msgstr ""
+
+#: filters/filter-sysfs.c:283
+msgid "sysfs dev_set creation failed"
+msgstr ""
+
+#: filters/filter.c:90
+#, c-format
+msgid "%s: Skipping: Unrecognised LVM device type %lu"
+msgstr ""
+
+#: filters/filter.c:98
+#, c-format
+msgid "%s: Skipping: Suspended dm device"
+msgstr ""
+
+#: filters/filter.c:104
+#, c-format
+msgid "%s: Skipping: open failed"
+msgstr ""
+
+#: filters/filter.c:110
+#, c-format
+msgid "%s: Skipping: dev_get_size failed"
+msgstr ""
+
+#: filters/filter.c:115
+#, c-format
+msgid "%s: Skipping: Too small to hold a PV"
+msgstr ""
+
+#: filters/filter.c:120
+#, c-format
+msgid "%s: Skipping: Partition table signature found"
+msgstr ""
+
+#: filters/filter.c:147
+msgid "No proc filesystem found: using all block device types"
+msgstr ""
+
+#: filters/filter.c:159
+msgid "Failed to create /proc/devices string"
+msgstr ""
+
+#: filters/filter.c:218
+msgid "Expecting string in devices/types in config file"
+msgstr ""
+
+#: filters/filter.c:228
+#, c-format
+msgid "Max partition count missing for %s in devices/types in config file"
+msgstr ""
+
+#: filters/filter.c:236
+#, c-format
+msgid "Zero partition count invalid for %s in devices/types in config file"
+msgstr ""
+
+#: filters/filter.c:269
+msgid "LVM type filter allocation failed"
+msgstr ""
+
+#: format1/disk-rep.c:190
+#, c-format
+msgid "%s does not have a valid LVM1 PV identifier"
+msgstr ""
+
+#: format1/disk-rep.c:196
+#, c-format
+msgid "format1: Unknown metadata version %d found on %s"
+msgstr ""
+
+#: format1/disk-rep.c:210 format_pool/disk_rep.c:43
+#, c-format
+msgid "Failed to read PV data from %s"
+msgstr ""
+
+#: format1/disk-rep.c:367
+#, c-format
+msgid "%s is not a member of any format1 VG"
+msgstr ""
+
+#: format1/disk-rep.c:374
+#, c-format
+msgid "Failed to read VG data from PV (%s)"
+msgstr ""
+
+#: format1/disk-rep.c:380
+#, c-format
+msgid "%s is not a member of the VG %s"
+msgstr ""
+
+#: format1/disk-rep.c:390
+#, c-format
+msgid "Failed to read PV uuid list from %s"
+msgstr ""
+
+#: format1/disk-rep.c:395
+#, c-format
+msgid "Failed to read LV's from %s"
+msgstr ""
+
+#: format1/disk-rep.c:400
+#, c-format
+msgid "Failed to read extents from %s"
+msgstr ""
+
+#: format1/disk-rep.c:404
+#, c-format
+msgid "Found %s in %sVG %s"
+msgstr ""
+
+#: format1/disk-rep.c:443 format_pool/disk_rep.c:67
+#, c-format
+msgid "Ignoring duplicate PV %s on %s"
+msgstr ""
+
+#: format1/disk-rep.c:448 format_pool/disk_rep.c:72
+#, c-format
+msgid "Duplicate PV %s - using md %s"
+msgstr ""
+
+#: format1/disk-rep.c:494
+msgid "read_pvs_in_vg: dev_iter_create failed"
+msgstr ""
+
+#: format1/disk-rep.c:517
+#, c-format
+msgid "Writing %s VG metadata to %s at %lu len %zu"
+msgstr ""
+
+#: format1/disk-rep.c:537
+#, c-format
+msgid "Too many uuids to fit on %s"
+msgstr ""
+
+#: format1/disk-rep.c:542
+#, c-format
+msgid "Writing %s uuidlist to %s at %lu len %d"
+msgstr ""
+
+#: format1/disk-rep.c:557
+#, c-format
+msgid "Writing %s LV %s metadata to %s at %lu len %zu"
+msgstr ""
+
+#: format1/disk-rep.c:578
+#, c-format
+msgid "Couldn't zero lv area on device '%s'"
+msgstr ""
+
+#: format1/disk-rep.c:586
+#, c-format
+msgid "lv_number %d too large"
+msgstr ""
+
+#: format1/disk-rep.c:603
+#, c-format
+msgid "Writing %s extents metadata to %s at %lu len %zu"
+msgstr ""
+
+#: format1/disk-rep.c:623
+msgid "Invalid PV structure size."
+msgstr ""
+
+#: format1/disk-rep.c:632
+msgid "Couldn't allocate temporary PV buffer."
+msgstr ""
+
+#: format1/disk-rep.c:639
+#, c-format
+msgid "Writing %s PV metadata to %s at %lu len %zu"
+msgstr ""
+
+#: format1/disk-rep.c:662
+#, c-format
+msgid "Failed to write PV structure onto %s"
+msgstr ""
+
+#: format1/disk-rep.c:681
+#, c-format
+msgid "Failed to write VG data to %s"
+msgstr ""
+
+#: format1/disk-rep.c:686
+#, c-format
+msgid "Failed to write PV uuid list to %s"
+msgstr ""
+
+#: format1/disk-rep.c:691
+#, c-format
+msgid "Failed to write LV's to %s"
+msgstr ""
+
+#: format1/disk-rep.c:696
+#, c-format
+msgid "Failed to write extents to %s"
+msgstr ""
+
+#: format1/disk-rep.c:736
+#, c-format
+msgid "Successfully wrote data to %s"
+msgstr ""
+
+#: format1/format1.c:72
+#, c-format
+msgid "VG data differs between PVs %s and %s"
+msgstr ""
+
+#: format1/format1.c:74 format1/format1.c:89
+#, c-format
+msgid "VG data on %s: %s %s %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u"
+msgstr ""
+
+#: format1/format1.c:115
+#, c-format
+msgid "%d PV(s) found for VG %s: expected %d"
+msgstr ""
+
+#: format1/format1.c:294 format_pool/format_pool.c:228
+#, c-format
+msgid "Reading physical volume data %s from disk"
+msgstr ""
+
+#: format1/format1.c:335
+#, c-format
+msgid "Physical volumes cannot be bigger than %s"
+msgstr ""
+
+#: format1/format1.c:355
+msgid "Metadata would overwrite physical extents"
+msgstr ""
+
+#: format1/format1.c:370
+#, c-format
+msgid "logical volumes cannot contain more than %d extents."
+msgstr ""
+
+#: format1/format1.c:375
+#, c-format
+msgid "logical volumes cannot be larger than %s"
+msgstr ""
+
+#: format1/format1.c:451
+#, c-format
+msgid "Extent size must be between %s and %s"
+msgstr ""
+
+#: format1/format1.c:459
+#, c-format
+msgid "Extent size must be multiple of %s"
+msgstr ""
+
+#: format1/format1.c:466 format_text/format-text.c:79
+msgid "Extent size must be power of 2"
+msgstr ""
+
+#: format1/format1.c:563
+msgid "Couldn't create lvm1 label handler."
+msgstr ""
+
+#: format1/format1.c:568
+msgid "Couldn't register lvm1 label handler."
+msgstr ""
+
+#: format1/format1.c:572 format_pool/format_pool.c:354
+#: format_text/format-text.c:1994
+#, c-format
+msgid "Initialised format: %s"
+msgstr ""
+
+#: format1/import-export.c:75
+#, c-format
+msgid "System ID %s on %s differs from %s for volume group"
+msgstr ""
+
+#: format1/import-export.c:98 format_text/import_vsn1.c:220
+#: metadata/metadata.c:569 metadata/metadata.c:1542 pvresize.c:121
+#: vgreduce.c:395 vgremove.c:62
+#, c-format
+msgid "%s: Couldn't get size."
+msgstr ""
+
+#: format1/import-export.c:101 format_text/import_vsn1.c:223
+#, c-format
+msgid "Fixing up missing format1 size (%s) for PV %s"
+msgstr ""
+
+#: format1/import-export.c:108 format_text/import_vsn1.c:230
+#, c-format
+msgid "WARNING: Physical Volume %s is too large for underlying device"
+msgstr ""
+
+#: format1/import-export.c:130
+msgid "Generated system_id too long"
+msgstr ""
+
+#: format1/import-export.c:174
+#, c-format
+msgid "Volume group name %s too long to export"
+msgstr ""
+
+#: format1/import-export.c:412
+#, c-format
+msgid "Segment type %s in LV %s: unsupported by format1"
+msgstr ""
+
+#: format1/import-export.c:418
+#, c-format
+msgid "Non-PV stripe found in LV %s: unsupported by format1"
+msgstr ""
+
+#: format1/import-export.c:610
+msgid "Logical volume number out of bounds."
+msgstr ""
+
+#: format1/import-export.c:617
+#, c-format
+msgid "Couldn't find logical volume '%s'."
+msgstr ""
+
+#: format1/import-export.c:637
+#, c-format
+msgid "Couldn't find origin logical volume for snapshot '%s'."
+msgstr ""
+
+#: format1/import-export.c:650
+msgid "Couldn't add snapshot."
+msgstr ""
+
+#: format1/import-extents.c:53
+msgid "Unable to create hash table for holding extent maps."
+msgstr ""
+
+#: format1/import-extents.c:92
+#, c-format
+msgid "Physical volume (%s) contains an unknown logical volume (%s)."
+msgstr ""
+
+#: format1/import-extents.c:137
+#, c-format
+msgid "Invalid LV in extent map (PV %s, PE %u, LV %u, LE %u)"
+msgstr ""
+
+#: format1/import-extents.c:149
+msgid "logical extent number out of bounds"
+msgstr ""
+
+#: format1/import-extents.c:155
+#, c-format
+msgid "logical extent (%u) already mapped."
+msgstr ""
+
+#: format1/import-extents.c:175
+#, c-format
+msgid "Logical volume (%s) contains an incomplete mapping table."
+msgstr ""
+
+#: format1/import-extents.c:229
+msgid "Failed to allocate linear segment."
+msgstr ""
+
+#: format1/import-extents.c:276
+#, c-format
+msgid ""
+"Number of stripes (%u) incompatible with logical extent count (%u) for %s"
+msgstr ""
+
+#: format1/import-extents.c:303
+msgid "Failed to allocate striped segment."
+msgstr ""
+
+#: format1/import-extents.c:359
+msgid "Couldn't allocate logical volume maps."
+msgstr ""
+
+#: format1/import-extents.c:364
+msgid "Couldn't fill logical volume maps."
+msgstr ""
+
+#: format1/import-extents.c:374
+msgid "Couldn't build extent segments."
+msgstr ""
+
+#: format1/layout.c:79
+#, c-format
+msgid "MaxLogicalVolumes of %d exceeds format limit of %d for VG '%s'"
+msgstr ""
+
+#: format1/layout.c:86
+#, c-format
+msgid "MaxPhysicalVolumes of %d exceeds format limit of %d for VG '%s'"
+msgstr ""
+
+#: format1/layout.c:105
+msgid "Insufficient space for metadata and PE's."
+msgstr ""
+
+#: format1/layout.c:141
+#, c-format
+msgid "Too few extents on %s. Try smaller extent size."
+msgstr ""
+
+#: format1/layout.c:162
+#, c-format
+msgid "Metadata extent limit (%u) exceeded for %s - %u required"
+msgstr ""
+
+#: format1/lvm1-label.c:29
+#, c-format
+msgid "The '%s' operation is not supported for the lvm1 labeller."
+msgstr ""
+
+#: format1/lvm1-label.c:120 format_pool/pool_label.c:99
+#: format_text/text_label.c:285
+msgid "Couldn't allocate labeller object."
+msgstr ""
+
+#: format_pool/disk_rep.c:94 format_pool/disk_rep.c:98
+#, c-format
+msgid "Calculated uuid %s for %s"
+msgstr ""
+
+#: format_pool/disk_rep.c:274
+#, c-format
+msgid "Unable to allocate %d 32-bit uints"
+msgstr ""
+
+#: format_pool/disk_rep.c:341
+#, c-format
+msgid "No devices for vg %s found in cache"
+msgstr ""
+
+#: format_pool/disk_rep.c:363
+msgid "Unable to allocate pool list structure"
+msgstr ""
+
+#: format_pool/format_pool.c:44
+#, c-format
+msgid "Unable to allocate %d subpool structures"
+msgstr ""
+
+#: format_pool/format_pool.c:64
+#, c-format
+msgid "Unable to allocate %d pool_device structures"
+msgstr ""
+
+#: format_pool/format_pool.c:87
+#, c-format
+msgid "Missing subpool %d in pool %s"
+msgstr ""
+
+#: format_pool/format_pool.c:92
+#, c-format
+msgid "Missing device %u for subpool %d in pool %s"
+msgstr ""
+
+#: format_pool/format_pool.c:113
+msgid "Unable to allocate volume group structure"
+msgstr ""
+
+#: format_pool/format_pool.c:279
+msgid "Unable to allocate format instance structure for pool format"
+msgstr ""
+
+#: format_pool/format_pool.c:289
+msgid "Unable to allocate metadata area structure for pool format"
+msgstr ""
+
+#: format_pool/format_pool.c:332
+msgid "Unable to allocate format type structure for pool format"
+msgstr ""
+
+#: format_pool/format_pool.c:345
+msgid "Couldn't create pool label handler."
+msgstr ""
+
+#: format_pool/format_pool.c:350
+msgid "Couldn't register pool label handler."
+msgstr ""
+
+#: format_pool/import_export.c:64
+msgid "Unable to allocate lv list structure"
+msgstr ""
+
+#: format_pool/import_export.c:69
+msgid "Unable to allocate logical volume structure"
+msgstr ""
+
+#: format_pool/import_export.c:98
+#, c-format
+msgid "Calculated lv uuid for lv %s: %s"
+msgstr ""
+
+#: format_pool/import_export.c:133
+msgid "Unable to allocate pv list structure"
+msgstr ""
+
+#: format_pool/import_export.c:137
+msgid "Unable to allocate pv structure"
+msgstr ""
+
+#: format_pool/import_export.c:165
+msgid "Unable to duplicate vg_name string"
+msgstr ""
+
+#: format_pool/import_export.c:195
+#, c-format
+msgid "Found sptype %X and converted it to %s"
+msgstr ""
+
+#: format_pool/import_export.c:210
+msgid "Stripe size must be a power of 2"
+msgstr ""
+
+#: format_pool/import_export.c:226
+msgid "Unable to allocate striped lv_segment structure"
+msgstr ""
+
+#: format_pool/import_export.c:267
+msgid "Unable to allocate linear lv_segment structure"
+msgstr ""
+
+#: format_pool/pool_label.c:28
+#, c-format
+msgid "The '%s' operation is not supported for the pool labeller."
+msgstr ""
+
+#: format_text/archive.c:146
+#, c-format
+msgid "Couldn't scan the archive directory (%s)."
+msgstr ""
+
+#: format_text/archive.c:173
+msgid "Couldn't create new archive file."
+msgstr ""
+
+#: format_text/archive.c:221
+#, c-format
+msgid "Expiring archive %s"
+msgstr ""
+
+#: format_text/archive.c:246
+msgid "Couldn't create temporary archive name."
+msgstr ""
+
+#: format_text/archive.c:251
+msgid "Couldn't create FILE object for archive."
+msgstr ""
+
+#: format_text/archive.c:288
+msgid "Archive file name too long."
+msgstr ""
+
+#: format_text/archive.c:299
+#, c-format
+msgid "Archive rename failed for %s"
+msgstr ""
+
+#: format_text/archive.c:316
+#, c-format
+msgid "File:\t\t%s"
+msgstr ""
+
+#: format_text/archive.c:321
+msgid "Couldn't create text instance object."
+msgstr ""
+
+#: format_text/archive.c:331
+msgid "Unable to read archive file."
+msgstr ""
+
+#: format_text/archive.c:336
+#, c-format
+msgid "VG name: \t%s"
+msgstr ""
+
+#: format_text/archive.c:337
+#, c-format
+msgid "Description:\t%s"
+msgstr ""
+
+#: format_text/archive.c:338
+#, c-format
+msgid "Backup Time:\t%s"
+msgstr ""
+
+#: format_text/archive.c:355
+#, c-format
+msgid "No archives found in %s."
+msgstr ""
+
+#: format_text/archiver.c:43 format_text/archiver.c:155
+msgid "archive_params alloc failed"
+msgstr ""
+
+#: format_text/archiver.c:128
+#, c-format
+msgid "Archiving volume group \"%s\" metadata (seqno %u)."
+msgstr ""
+
+#: format_text/archiver.c:303
+#, c-format
+msgid "PV %s is a different format (seqno %s)"
+msgstr ""
+
+#: format_text/archiver.c:364
+#, c-format
+msgid "Creating volume group backup \"%s\" (seqno %u)."
+msgstr ""
+
+#: format_text/archiver.c:402
+msgid "Failed to generate backup filename."
+msgstr ""
+
+#: format_text/export.c:80
+#, c-format
+msgid "uname failed: %s"
+msgstr ""
+
+#: format_text/export.c:101
+msgid "Internal error tracking indentation"
+msgstr ""
+
+#: format_text/export.c:120
+#, c-format
+msgid "Doubling metadata output buffer to %u"
+msgstr ""
+
+#: format_text/export.c:124
+msgid "Buffer reallocation failed."
+msgstr ""
+
+#: format_text/export.c:737
+msgid "text_export buffer allocation failed"
+msgstr ""
+
+#: format_text/flags.c:79
+msgid "Unknown flag set requested."
+msgstr ""
+
+#: format_text/flags.c:125
+msgid "Metadata inconsistency: Not all flags successfully exported."
+msgstr ""
+
+#: format_text/flags.c:147
+msgid "Status value is not a string."
+msgstr ""
+
+#: format_text/flags.c:158
+#, c-format
+msgid "Unknown status flag '%s'."
+msgstr ""
+
+#: format_text/format-text.c:152
+#, c-format
+msgid "Found text metadata area, offset=%lu, size=%lu"
+msgstr ""
+
+#: format_text/format-text.c:207
+#, c-format
+msgid ""
+"Found LVM2 metadata record at offset=%lu, size=%lu, offset2=%lu size2=%lu"
+msgstr ""
+
+#: format_text/format-text.c:259
+#, c-format
+msgid "Random lvid creation failed for %s/%s."
+msgstr ""
+
+#: format_text/format-text.c:290
+msgid "struct mda_header allocation failed"
+msgstr ""
+
+#: format_text/format-text.c:302
+msgid "Incorrect metadata area header checksum"
+msgstr ""
+
+#: format_text/format-text.c:309
+msgid "Wrong magic number in metadata area header"
+msgstr ""
+
+#: format_text/format-text.c:314
+#, c-format
+msgid "Incompatible metadata area header version: %d"
+msgstr ""
+
+#: format_text/format-text.c:320
+#, c-format
+msgid "Incorrect start sector in metadata area header: %lu"
+msgstr ""
+
+#: format_text/format-text.c:461
+#, c-format
+msgid "VG %s not found on %s"
+msgstr ""
+
+#: format_text/format-text.c:469 format_text/format-text.c:574
+#, c-format
+msgid "VG %s metadata too large for circular buffer"
+msgstr ""
+
+#: format_text/format-text.c:484
+#, c-format
+msgid "Read %s %smetadata (%u) from %s at %lu size %lu"
+msgstr ""
+
+#: format_text/format-text.c:557
+#, c-format
+msgid "VG %s metadata writing failed"
+msgstr ""
+
+#: format_text/format-text.c:579
+#, c-format
+msgid "Writing %s metadata to %s at %lu len %lu"
+msgstr ""
+
+#: format_text/format-text.c:592
+#, c-format
+msgid "Writing metadata to %s at %lu len %u"
+msgstr ""
+
+#: format_text/format-text.c:681
+#, c-format
+msgid "%sCommitting %s metadata (%u) to %s header at %lu"
+msgstr ""
+
+#: format_text/format-text.c:685
+#, c-format
+msgid "Wiping pre-committed %s metadata from %s header at %lu"
+msgstr ""
+
+#: format_text/format-text.c:691 format_text/format-text.c:777
+msgid "Failed to write metadata area header"
+msgstr ""
+
+#: format_text/format-text.c:810
+#, c-format
+msgid "'%s' does not contain volume group '%s'."
+msgstr ""
+
+#: format_text/format-text.c:814
+#, c-format
+msgid "Read volume group %s from %s"
+msgstr ""
+
+#: format_text/format-text.c:863
+msgid "Text format failed to determine directory."
+msgstr ""
+
+#: format_text/format-text.c:868
+msgid "Couldn't create temporary text file name."
+msgstr ""
+
+#: format_text/format-text.c:879
+#, c-format
+msgid "Writing %s metadata to %s"
+msgstr ""
+
+#: format_text/format-text.c:882
+#, c-format
+msgid "Failed to write metadata to %s."
+msgstr ""
+
+#: format_text/format-text.c:901 format_text/format-text.c:926
+#: format_text/format-text.c:960
+#, c-format
+msgid "Renaming %s to %s"
+msgstr ""
+
+#: format_text/format-text.c:917
+#, c-format
+msgid "Test mode: Skipping committing %s metadata (%u)"
+msgstr ""
+
+#: format_text/format-text.c:920
+#, c-format
+msgid "Unlinking %s"
+msgstr ""
+
+#: format_text/format-text.c:925
+#, c-format
+msgid "Committing %s metadata (%u)"
+msgstr ""
+
+#: format_text/format-text.c:962
+msgid "Test mode: Skipping rename"
+msgstr ""
+
+#: format_text/format-text.c:1025 format_text/format-text.c:1723
+#, c-format
+msgid "Name too long %s/%s"
+msgstr ""
+
+#: format_text/format-text.c:1089
+#, c-format
+msgid "%s: metadata too large for circular buffer"
+msgstr ""
+
+#: format_text/format-text.c:1118
+#, c-format
+msgid "%s: Found metadata at %lu size %lu for %s (%s)"
+msgstr ""
+
+#: format_text/format-text.c:1186
+#, c-format
+msgid "Physical extents end beyond end of device %s!"
+msgstr ""
+
+#: format_text/format-text.c:1207
+#, c-format
+msgid "Warning: metadata area fills disk leaving no space for data on %s."
+msgstr ""
+
+#: format_text/format-text.c:1238 format_text/format-text.c:1283
+msgid "Failed to wipe new metadata area"
+msgstr ""
+
+#: format_text/format-text.c:1329
+#, c-format
+msgid "Creating metadata area on %s at sector %lu size %lu sectors"
+msgstr ""
+
+#: format_text/format-text.c:1410
+msgid "_add_raw allocation failed"
+msgstr ""
+
+#: format_text/format-text.c:1470
+#, c-format
+msgid "Must be exactly one data area (found %d) on PV %s"
+msgstr ""
+
+#: format_text/format-text.c:1485 format_text/format-text.c:1489
+msgid "metadata_area allocation failed"
+msgstr ""
+
+#: format_text/format-text.c:1650
+#, c-format
+msgid "PV %s too large for extent size %s."
+msgstr ""
+
+#: format_text/format-text.c:1693
+msgid "Couldn't allocate format instance object."
+msgstr ""
+
+#: format_text/format-text.c:1699
+msgid "Couldn't allocate text_fid_context."
+msgstr ""
+
+#: format_text/format-text.c:1807
+#, c-format
+msgid "%s: Volume group filename may not end in .tmp"
+msgstr ""
+
+#: format_text/format-text.c:1841
+msgid "Couldn't allocate text format context object."
+msgstr ""
+
+#: format_text/format-text.c:1863
+msgid "_add_dir allocation failed"
+msgstr ""
+
+#: format_text/format-text.c:1866
+#, c-format
+msgid "Adding text format metadata dir: %s"
+msgstr ""
+
+#: format_text/format-text.c:1883
+msgid "Empty metadata disk_area section of config file"
+msgstr ""
+
+#: format_text/format-text.c:1888
+msgid "Missing start_sector in metadata disk_area section of config file"
+msgstr ""
+
+#: format_text/format-text.c:1895
+msgid "Missing size in metadata disk_area section of config file"
+msgstr ""
+
+#: format_text/format-text.c:1902
+msgid "Missing uuid in metadata disk_area section of config file"
+msgstr ""
+
+#: format_text/format-text.c:1908
+#, c-format
+msgid "Invalid uuid in metadata disk_area section of config file: %s"
+msgstr ""
+
+#: format_text/format-text.c:1917 format_text/import_vsn1.c:155
+msgid "Couldn't find device."
+msgstr ""
+
+#: format_text/format-text.c:1919 format_text/import_vsn1.c:157
+#, c-format
+msgid "Couldn't find device with uuid '%s'."
+msgstr ""
+
+#: format_text/format-text.c:1948
+msgid "Failed to allocate dir_list"
+msgstr ""
+
+#: format_text/format-text.c:1960
+msgid "Couldn't create text label handler."
+msgstr ""
+
+#: format_text/format-text.c:1966
+msgid "Couldn't register text label handler."
+msgstr ""
+
+#: format_text/format-text.c:1974
+msgid "Invalid string in config file: metadata/dirs"
+msgstr ""
+
+#: format_text/import.c:103
+msgid "Couldn't read volume group metadata."
+msgstr ""
+
+#: format_text/import_vsn1.c:46
+#, c-format
+msgid "Can't process text format file - %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:94
+msgid "Couldn't find uuid."
+msgstr ""
+
+#: format_text/import_vsn1.c:100
+msgid "uuid must be a string."
+msgstr ""
+
+#: format_text/import_vsn1.c:105
+msgid "Invalid uuid."
+msgstr ""
+
+#: format_text/import_vsn1.c:139
+msgid "Empty pv section."
+msgstr ""
+
+#: format_text/import_vsn1.c:144
+msgid "Couldn't read uuid for volume group."
+msgstr ""
+
+#: format_text/import_vsn1.c:174
+msgid "Couldn't find status flags for physical volume."
+msgstr ""
+
+#: format_text/import_vsn1.c:179
+msgid "Couldn't read status flags for physical volume."
+msgstr ""
+
+#: format_text/import_vsn1.c:187
+msgid "Couldn't read extent size for volume group."
+msgstr ""
+
+#: format_text/import_vsn1.c:192
+msgid "Couldn't find extent count (pe_count) for physical volume."
+msgstr ""
+
+#: format_text/import_vsn1.c:203
+#, c-format
+msgid "Couldn't read tags for physical volume %s in %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:275
+msgid "Empty segment section."
+msgstr ""
+
+#: format_text/import_vsn1.c:280
+#, c-format
+msgid "Couldn't read 'start_extent' for segment '%s'."
+msgstr ""
+
+#: format_text/import_vsn1.c:286
+#, c-format
+msgid "Couldn't read 'extent_count' for segment '%s'."
+msgstr ""
+
+#: format_text/import_vsn1.c:296
+msgid "Segment type must be a string."
+msgstr ""
+
+#: format_text/import_vsn1.c:316
+msgid "Segment allocation failed"
+msgstr ""
+
+#: format_text/import_vsn1.c:329
+#, c-format
+msgid "Couldn't read tags for a segment of %s/%s."
+msgstr ""
+
+#: format_text/import_vsn1.c:358
+#, c-format
+msgid "Zero areas not allowed for segment '%s'"
+msgstr ""
+
+#: format_text/import_vsn1.c:394
+#, c-format
+msgid "Couldn't find volume '%s' for segment '%s'."
+msgstr ""
+
+#: format_text/import_vsn1.c:407
+#, c-format
+msgid "Incorrect number of areas in area array for segment '%s'."
+msgstr ""
+
+#: format_text/import_vsn1.c:437
+msgid "Only one segment permitted for snapshot"
+msgstr ""
+
+#: format_text/import_vsn1.c:443
+msgid "Couldn't read segment count for logical volume."
+msgstr ""
+
+#: format_text/import_vsn1.c:448
+msgid "segment_count and actual number of segments disagree."
+msgstr ""
+
+#: format_text/import_vsn1.c:494 format_text/import_vsn1.c:562
+msgid "Empty logical volume section."
+msgstr ""
+
+#: format_text/import_vsn1.c:499
+msgid "Couldn't find status flags for logical volume."
+msgstr ""
+
+#: format_text/import_vsn1.c:504
+msgid "Couldn't read status flags for logical volume."
+msgstr ""
+
+#: format_text/import_vsn1.c:512 format_text/import_vsn1.c:729
+msgid "allocation_policy must be a string."
+msgstr ""
+
+#: format_text/import_vsn1.c:535
+#, c-format
+msgid "Couldn't read tags for logical volume %s/%s."
+msgstr ""
+
+#: format_text/import_vsn1.c:555
+#, c-format
+msgid "Lost logical volume reference %s"
+msgstr ""
+
+#: format_text/import_vsn1.c:568
+#, c-format
+msgid "Couldn't read uuid for logical volume %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:595
+#, c-format
+msgid "Couldn't read minor number for logical volume %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:603
+#, c-format
+msgid "Couldn't read major number for logical volume %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:620
+#, c-format
+msgid "Couldn't find section '%s'."
+msgstr ""
+
+#: format_text/import_vsn1.c:649 format_text/import_vsn1.c:841
+msgid "Couldn't find volume group in file."
+msgstr ""
+
+#: format_text/import_vsn1.c:673
+msgid "system_id must be a string"
+msgstr ""
+
+#: format_text/import_vsn1.c:680 format_text/import_vsn1.c:851
+#, c-format
+msgid "Couldn't read uuid for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:685
+#, c-format
+msgid "Couldn't read 'seqno' for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:691 format_text/import_vsn1.c:856
+#, c-format
+msgid "Couldn't find status flags for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:697 format_text/import_vsn1.c:862
+#, c-format
+msgid "Couldn't read status flags for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:703
+#, c-format
+msgid "Couldn't read extent size for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:714
+#, c-format
+msgid "Couldn't read 'max_lv' for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:720
+#, c-format
+msgid "Couldn't read 'max_pv' for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:745
+msgid "Couldn't create hash table."
+msgstr ""
+
+#: format_text/import_vsn1.c:752
+#, c-format
+msgid "Couldn't find all physical volumes for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:763
+#, c-format
+msgid "Couldn't read tags for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:769
+#, c-format
+msgid "Couldn't read all logical volume names for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:776
+#, c-format
+msgid "Couldn't read all logical volumes for volume group %s."
+msgstr ""
+
+#: format_text/import_vsn1.c:782
+#, c-format
+msgid "Failed to fixup mirror pointers after import for volume group %s."
+msgstr ""
+
+#: format_text/tags.c:62
+msgid "Found a tag that is not a string"
+msgstr ""
+
+#: format_text/text_label.c:98 format_text/text_label.c:103
+msgid "struct data_area_list allocation failed"
+msgstr ""
+
+#: format_text/text_label.c:138 format_text/text_label.c:149
+msgid "struct mda_list allocation failed"
+msgstr ""
+
+#: format_text/text_label.c:143 format_text/text_label.c:154
+msgid "struct mda_context allocation failed"
+msgstr ""
+
+#: label/label.c:49
+msgid "Couldn't allocate memory for labeller list object."
+msgstr ""
+
+#: label/label.c:123 label/label.c:218
+#, c-format
+msgid "%s: Failed to read label area"
+msgstr ""
+
+#: label/label.c:135 label/label.c:164
+#, c-format
+msgid "Ignoring additional label on %s at sector %lu"
+msgstr ""
+
+#: label/label.c:140
+#, c-format
+msgid "%s: Label for sector %lu found at sector %lu - ignoring"
+msgstr ""
+
+#: label/label.c:150
+#, c-format
+msgid "Label checksum incorrect on %s - ignoring"
+msgstr ""
+
+#: label/label.c:161
+#, c-format
+msgid "%s: %s label detected"
+msgstr ""
+
+#: label/label.c:185
+#, c-format
+msgid "%s: No label detected"
+msgstr ""
+
+#: label/label.c:204
+#, c-format
+msgid "Scanning for labels to wipe from %s"
+msgstr ""
+
+#: label/label.c:244
+#, c-format
+msgid "%s: Wiping label at sector %lu"
+msgstr ""
+
+#: label/label.c:248
+#, c-format
+msgid "Failed to remove label from %s at sector %lu"
+msgstr ""
+
+#: label/label.c:304
+msgid "Label handler does not support label writes"
+msgstr ""
+
+#: label/label.c:309
+#, c-format
+msgid "Label sector %lu beyond range (%ld)"
+msgstr ""
+
+#: label/label.c:333
+#, c-format
+msgid "%s: Writing label to sector %lu"
+msgstr ""
+
+#: label/label.c:336
+#, c-format
+msgid "Failed to write label to %s"
+msgstr ""
+
+#: label/label.c:386
+msgid "label allocaction failed"
+msgstr ""
+
+#: locking/cluster_locking.c:69
+#, c-format
+msgid "Local socket creation failed: %s"
+msgstr ""
+
+#: locking/cluster_locking.c:82
+#, c-format
+msgid "connect() failed on local socket: %s"
+msgstr ""
+
+#: locking/cluster_locking.c:109
+#, c-format
+msgid "Error writing data to clvmd: %s"
+msgstr ""
+
+#: locking/cluster_locking.c:118
+#, c-format
+msgid "Error reading data from clvmd: %s"
+msgstr ""
+
+#: locking/cluster_locking.c:123
+msgid "EOF reading CLVMD"
+msgstr ""
+
+#: locking/cluster_locking.c:156
+#, c-format
+msgid "cluster request failed: %s"
+msgstr ""
+
+#: locking/cluster_locking.c:346
+#, c-format
+msgid "clvmd not running on node %s"
+msgstr ""
+
+#: locking/cluster_locking.c:351
+#, c-format
+msgid "Error locking on node %s: %s"
+msgstr ""
+
+#: locking/cluster_locking.c:402 locking/file_locking.c:266
+#: locking/locking.c:265 locking/no_locking.c:71
+#, c-format
+msgid "Unrecognised lock scope: %d"
+msgstr ""
+
+#: locking/cluster_locking.c:408
+#, c-format
+msgid "Locking %s at 0x%x"
+msgstr ""
+
+#: locking/external_locking.c:64
+msgid "External locking already initialised"
+msgstr ""
+
+#: locking/external_locking.c:86
+#, c-format
+msgid "Shared library %s does not contain locking functions"
+msgstr ""
+
+#: locking/external_locking.c:93
+#, c-format
+msgid "Loaded external locking library %s"
+msgstr ""
+
+#: locking/file_locking.c:59
+#, c-format
+msgid "Unlocking %s"
+msgstr ""
+
+#: locking/file_locking.c:111
+msgid "CTRL-c detected: giving up waiting for lock"
+msgstr ""
+
+#: locking/file_locking.c:149
+#, c-format
+msgid "Unrecognised lock type: %d"
+msgstr ""
+
+#: locking/file_locking.c:163
+#, c-format
+msgid "Locking %s %c%c"
+msgstr ""
+
+#: locking/file_locking.c:237
+#, c-format
+msgid "Unlocking LV %s"
+msgstr ""
+
+#: locking/file_locking.c:242
+#, c-format
+msgid "Locking LV %s (NL)"
+msgstr ""
+
+#: locking/file_locking.c:247
+#, c-format
+msgid "Locking LV %s (R)"
+msgstr ""
+
+#: locking/file_locking.c:252
+#, c-format
+msgid "Locking LV %s (W)"
+msgstr ""
+
+#: locking/file_locking.c:257
+#, c-format
+msgid "Locking LV %s (EX)"
+msgstr ""
+
+#: locking/locking.c:133
+msgid ""
+"WARNING: Locking disabled. Be careful! This could corrupt your metadata."
+msgstr ""
+
+#: locking/locking.c:138
+msgid "File-based locking selected."
+msgstr ""
+
+#: locking/locking.c:146
+msgid "External locking selected."
+msgstr ""
+
+#: locking/locking.c:156
+msgid "Falling back to internal clustered locking."
+msgstr ""
+
+#: locking/locking.c:160
+msgid "Cluster locking selected."
+msgstr ""
+
+#: locking/locking.c:167
+msgid "Unknown locking type requested."
+msgstr ""
+
+#: locking/locking.c:174
+msgid "WARNING: Falling back to local file-based locking."
+msgstr ""
+
+#: locking/locking.c:175
+msgid "Volume Groups with the clustered attribute will be inaccessible."
+msgstr ""
+
+#: locking/locking.c:185
+msgid "Locking disabled - only read operations permitted."
+msgstr ""
+
+#: locking/locking.c:212
+#, c-format
+msgid "LVM1 proc VG pathname too long for %s"
+msgstr ""
+
+#: locking/locking.c:217
+#, c-format
+msgid "%s exists: Is the original LVM driver using this volume group?"
+msgstr ""
+
+#: locking/locking.c:302 lvresize.c:573
+#, c-format
+msgid "Failed to suspend %s"
+msgstr ""
+
+#: locking/locking.c:323
+#, c-format
+msgid "Failed to activate %s"
+msgstr ""
+
+#: log/log.c:145
+msgid "Test mode: Metadata will NOT be updated."
+msgstr ""
+
+#: lvchange.c:27
+#, c-format
+msgid "Logical volume \"%s\" is already writable"
+msgstr ""
+
+#: lvchange.c:33
+#, c-format
+msgid "Logical volume \"%s\" is already read only"
+msgstr ""
+
+#: lvchange.c:40
+#, c-format
+msgid "Cannot change permissions of mirror \"%s\" while active."
+msgstr ""
+
+#: lvchange.c:47
+#, c-format
+msgid "Setting logical volume \"%s\" read/write"
+msgstr ""
+
+#: lvchange.c:51
+#, c-format
+msgid "Setting logical volume \"%s\" read-only"
+msgstr ""
+
+#: lvchange.c:55 lvchange.c:314 lvchange.c:350 lvchange.c:393 lvchange.c:470
+#: lvchange.c:524 lvconvert.c:401
+#, c-format
+msgid "Updating logical volume \"%s\" on disk(s)"
+msgstr ""
+
+#: lvchange.c:64 lvchange.c:402 lvconvert.c:409 metadata/mirror.c:227
+#, c-format
+msgid "Failed to lock %s"
+msgstr ""
+
+#: lvchange.c:74 lvchange.c:412
+#, c-format
+msgid "Updating permissions for \"%s\" in kernel"
+msgstr ""
+
+#: lvchange.c:76 lvchange.c:414 lvconvert.c:422 lvresize.c:585
+#: metadata/mirror.c:240
+#, c-format
+msgid "Problem reactivating %s"
+msgstr ""
+
+#: lvchange.c:89
+#, c-format
+msgid "Logical volume, %s, is not active"
+msgstr ""
+
+#: lvchange.c:113
+#, c-format
+msgid "Deactivating logical volume \"%s\" locally"
+msgstr ""
+
+#: lvchange.c:120
+#, c-format
+msgid "Deactivating logical volume \"%s\""
+msgstr ""
+
+#: lvchange.c:127
+#, c-format
+msgid "Locking failed: ignoring clustered logical volume %s"
+msgstr ""
+
+#: lvchange.c:133
+#, c-format
+msgid "Activating logical volume \"%s\" exclusively"
+msgstr ""
+
+#: lvchange.c:140
+#, c-format
+msgid "Activating logical volume \"%s\" locally"
+msgstr ""
+
+#: lvchange.c:147
+#, c-format
+msgid "Activating logical volume \"%s\""
+msgstr ""
+
+#: lvchange.c:157
+#, c-format
+msgid "Spawning background pvmove process for %s"
+msgstr ""
+
+#: lvchange.c:168
+#, c-format
+msgid "Refreshing logical volume \"%s\" (if active)"
+msgstr ""
+
+#: lvchange.c:183
+#, c-format
+msgid "Unable to resync %s because it is not mirrored."
+msgstr ""
+
+#: lvchange.c:189
+#, c-format
+msgid "Unable to resync pvmove volume %s"
+msgstr ""
+
+#: lvchange.c:194
+#, c-format
+msgid "Unable to resync locked volume %s"
+msgstr ""
+
+#: lvchange.c:200
+#, c-format
+msgid "Can't resync open logical volume \"%s\""
+msgstr ""
+
+#: lvchange.c:210
+#, c-format
+msgid "Logical volume \"%s\" not resynced"
+msgstr ""
+
+#: lvchange.c:220
+#, c-format
+msgid "Can't get exclusive access to clustered volume %s"
+msgstr ""
+
+#: lvchange.c:226
+#, c-format
+msgid "Unable to deactivate %s for resync"
+msgstr ""
+
+#: lvchange.c:232
+#, c-format
+msgid "Starting resync of %s%s%s mirror \"%s\""
+msgstr ""
+
+#: lvchange.c:246
+#, c-format
+msgid "Failed to reactivate %s to resynchronize mirror"
+msgstr ""
+
+#: lvchange.c:262
+msgid "Failed to write intermediate VG metadata."
+msgstr ""
+
+#: lvchange.c:276
+msgid "Failed to commit intermediate VG metadata."
+msgstr ""
+
+#: lvchange.c:288
+#, c-format
+msgid "Unable to activate %s for mirror log resync"
+msgstr ""
+
+#: lvchange.c:293
+#, c-format
+msgid "Clearing log device %s"
+msgstr ""
+
+#: lvchange.c:295
+#, c-format
+msgid "Unable to reset sync status for %s"
+msgstr ""
+
+#: lvchange.c:297
+msgid "Failed to deactivate log LV after wiping failed"
+msgstr ""
+
+#: lvchange.c:303
+#, c-format
+msgid "Unable to deactivate log LV %s after wiping for resync"
+msgstr ""
+
+#: lvchange.c:316
+msgid "Failed to update metadata on disk."
+msgstr ""
+
+#: lvchange.c:321
+#, c-format
+msgid "Failed to reactivate %s after resync"
+msgstr ""
+
+#: lvchange.c:338
+#, c-format
+msgid "Allocation policy of logical volume \"%s\" is already %s"
+msgstr ""
+
+#: lvchange.c:347
+#, c-format
+msgid "Setting contiguous allocation policy for \"%s\" to %s"
+msgstr ""
+
+#: lvchange.c:383
+#, c-format
+msgid "Read ahead is already %u for \"%s\""
+msgstr ""
+
+#: lvchange.c:390
+#, c-format
+msgid "Setting read ahead to %u for \"%s\""
+msgstr ""
+
+#: lvchange.c:429
+#, c-format
+msgid "Minor number is already not persistent for \"%s\""
+msgstr ""
+
+#: lvchange.c:436
+#, c-format
+msgid "Disabling persistent device number for \"%s\""
+msgstr ""
+
+#: lvchange.c:440
+msgid "Minor number must be specified with -My"
+msgstr ""
+
+#: lvchange.c:444
+msgid "Major number must be specified with -My"
+msgstr ""
+
+#: lvchange.c:453
+#, c-format
+msgid "%s device number not changed."
+msgstr ""
+
+#: lvchange.c:457
+#, c-format
+msgid "Ensuring %s is inactive."
+msgstr ""
+
+#: lvchange.c:459
+#, c-format
+msgid "%s: deactivation failed"
+msgstr ""
+
+#: lvchange.c:465
+#, c-format
+msgid "Setting persistent device number to (%d, %d) for \"%s\""
+msgstr ""
+
+#: lvchange.c:484
+#, c-format
+msgid "Re-activating logical volume \"%s\""
+msgstr ""
+
+#: lvchange.c:486
+#, c-format
+msgid "%s: reactivation failed"
+msgstr ""
+
+#: lvchange.c:500 lvcreate.c:680 pvchange.c:49 vgchange.c:440 vgcreate.c:107
+msgid "Failed to get tag"
+msgstr ""
+
+#: lvchange.c:505
+#, c-format
+msgid "Logical volume %s/%s does not support tags"
+msgstr ""
+
+#: lvchange.c:512 lvcreate.c:746
+#, c-format
+msgid "Failed to add tag %s to %s/%s"
+msgstr ""
+
+#: lvchange.c:518
+#, c-format
+msgid "Failed to remove tag %s from %s/%s"
+msgstr ""
+
+#: lvchange.c:551
+#, c-format
+msgid "Only -a permitted with read-only volume group \"%s\""
+msgstr ""
+
+#: lvchange.c:560
+#, c-format
+msgid "Can't change logical volume \"%s\" under snapshot"
+msgstr ""
+
+#: lvchange.c:566
+#, c-format
+msgid "Can't change snapshot logical volume \"%s\""
+msgstr ""
+
+#: lvchange.c:572
+#, c-format
+msgid "Unable to change pvmove LV %s"
+msgstr ""
+
+#: lvchange.c:574
+msgid "Use 'pvmove --abort' to abandon a pvmove"
+msgstr ""
+
+#: lvchange.c:579
+#, c-format
+msgid "Unable to change mirror log LV %s directly"
+msgstr ""
+
+#: lvchange.c:584
+#, c-format
+msgid "Unable to change mirror image LV %s directly"
+msgstr ""
+
+#: lvchange.c:590
+#, c-format
+msgid "Unable to change internal LV %s directly"
+msgstr ""
+
+#: lvchange.c:648
+#, c-format
+msgid "Logical volume \"%s\" changed"
+msgstr ""
+
+#: lvchange.c:683
+msgid ""
+"Need 1 or more of -a, -C, -j, -m, -M, -p, -r, --resync, --refresh, --alloc, "
+"--addtag, --deltag or --monitor"
+msgstr ""
+
+#: lvchange.c:694
+msgid "Only -a permitted with --ignorelockingfailure"
+msgstr ""
+
+#: lvchange.c:699
+msgid "Please give logical volume path(s)"
+msgstr ""
+
+#: lvchange.c:705
+msgid "--major and --minor require -My"
+msgstr ""
+
+#: lvchange.c:710
+msgid "Only give one logical volume when specifying minor"
+msgstr ""
+
+#: lvchange.c:715
+msgid "Only one of --alloc and --contiguous permitted"
+msgstr ""
+
+#: lvconvert.c:50 lvcreate.c:69
+msgid "Please specify a logical volume to act as the snapshot origin."
+msgstr ""
+
+#: lvconvert.c:58 lvcreate.c:77
+msgid "The origin name should include the volume group."
+msgstr ""
+
+#: lvconvert.c:69
+msgid "Please provide logical volume path"
+msgstr ""
+
+#: lvconvert.c:79 lvrename.c:38
+#, c-format
+msgid "Please use a single volume group name (\"%s\" or \"%s\")"
+msgstr ""
+
+#: lvconvert.c:88 lvrename.c:52
+msgid "Please provide a valid volume group name"
+msgstr ""
+
+#: lvconvert.c:110
+msgid "Exactly one of --mirrors or --snapshot arguments required."
+msgstr ""
+
+#: lvconvert.c:129
+msgid "--regionsize is only available with mirrors"
+msgstr ""
+
+#: lvconvert.c:134 lvcreate.c:336
+msgid "Negative chunk size is invalid"
+msgstr ""
+
+#: lvconvert.c:140 lvcreate.c:342
+msgid "Chunk size must be a power of 2 in the range 4K to 512K"
+msgstr ""
+
+#: lvconvert.c:144 lvcreate.c:346
+#, c-format
+msgid "Setting chunksize to %d sectors."
+msgstr ""
+
+#: lvconvert.c:156
+msgid "--chunksize is only available with snapshots"
+msgstr ""
+
+#: lvconvert.c:162
+msgid "--zero is only available with snapshots"
+msgstr ""
+
+#: lvconvert.c:174 lvcreate.c:253
+msgid "Negative regionsize is invalid"
+msgstr ""
+
+#: lvconvert.c:184 lvcreate.c:262
+msgid "Negative regionsize in configuration file is invalid"
+msgstr ""
+
+#: lvconvert.c:192 lvcreate.c:276
+#, c-format
+msgid "Region size (%u) must be a multiple of machine memory page size (%d)"
+msgstr ""
+
+#: lvconvert.c:200 lvcreate.c:270
+#, c-format
+msgid "Region size (%u) must be a power of 2"
+msgstr ""
+
+#: lvconvert.c:206 lvcreate.c:283
+msgid "Non-zero region size must be supplied."
+msgstr ""
+
+#: lvconvert.c:216 lvcreate.c:390 metadata/mirror.c:566
+#, c-format
+msgid "%s: Required device-mapper target(s) not detected in your kernel"
+msgstr ""
+
+#: lvconvert.c:249
+#, c-format
+msgid "Logical volume %s only has %u mirrors."
+msgstr ""
+
+#: lvconvert.c:259
+msgid "Mirror log region size cannot be changed on an existing mirror."
+msgstr ""
+
+#: lvconvert.c:266
+#, c-format
+msgid "Logical volume %s is already not mirrored."
+msgstr ""
+
+#: lvconvert.c:277
+#, c-format
+msgid "Logical volume %s has multiple mirror segments."
+msgstr ""
+
+#: lvconvert.c:287 lvconvert.c:320
+msgid "Unable to determine mirror sync status."
+msgstr ""
+
+#: lvconvert.c:311 lvconvert.c:389 lvcreate.c:721
+msgid "Failed to create mirror log."
+msgstr ""
+
+#: lvconvert.c:335
+#, c-format
+msgid "Logical volume %s already has %u mirror(s)."
+msgstr ""
+
+#: lvconvert.c:346
+msgid "Adding mirror images is not supported yet."
+msgstr ""
+
+#: lvconvert.c:363
+msgid "Mirrors of striped volumes are not yet supported."
+msgstr ""
+
+#: lvconvert.c:419 metadata/mirror.c:237
+#, c-format
+msgid "Updating \"%s\" in kernel"
+msgstr ""
+
+#: lvconvert.c:426
+#, c-format
+msgid "Logical volume %s converted."
+msgstr ""
+
+#: lvconvert.c:438 lvcreate.c:608
+#, c-format
+msgid "Couldn't find origin volume '%s'."
+msgstr ""
+
+#: lvconvert.c:443
+#, c-format
+msgid "Unable to create a snapshot of a %s LV."
+msgstr ""
+
+#: lvconvert.c:450 lvcreate.c:799
+#, c-format
+msgid "WARNING: \"%s\" not zeroed"
+msgstr ""
+
+#: lvconvert.c:452
+msgid "Aborting. Failed to wipe snapshot exception store."
+msgstr ""
+
+#: lvconvert.c:458
+#, c-format
+msgid "Couldn't deactivate LV %s."
+msgstr ""
+
+#: lvconvert.c:464 lvcreate.c:812
+msgid "Couldn't create snapshot."
+msgstr ""
+
+#: lvconvert.c:475 lvcreate.c:821
+#, c-format
+msgid "Failed to suspend origin %s"
+msgstr ""
+
+#: lvconvert.c:484 lvcreate.c:830
+#, c-format
+msgid "Problem reactivating origin %s"
+msgstr ""
+
+#: lvconvert.c:488
+#, c-format
+msgid "Logical volume %s converted to snapshot."
+msgstr ""
+
+#: lvconvert.c:499
+#, c-format
+msgid "Cannot convert locked LV %s"
+msgstr ""
+
+#: lvconvert.c:504
+#, c-format
+msgid "Can't convert logical volume \"%s\" under snapshot"
+msgstr ""
+
+#: lvconvert.c:510
+#, c-format
+msgid "Can't convert snapshot logical volume \"%s\""
+msgstr ""
+
+#: lvconvert.c:516
+#, c-format
+msgid "Unable to convert pvmove LV %s"
+msgstr ""
+
+#: lvconvert.c:548 lvrename.c:100 vgrename.c:62
+#, c-format
+msgid "Checking for existing volume group \"%s\""
+msgstr ""
+
+#: lvconvert.c:551 lvcreate.c:863 lvrename.c:103 lvresize.c:613 pvchange.c:59
+#: pvmove.c:59 pvresize.c:69 vgcreate.c:140 vgextend.c:53 vgmerge.c:34
+#: vgmerge.c:65 vgreduce.c:476 vgrename.c:94 vgrename.c:133 vgsplit.c:240
+#: vgsplit.c:277
+#, c-format
+msgid "Can't get lock for %s"
+msgstr ""
+
+#: lvconvert.c:556 lvcreate.c:492 lvrename.c:108 pvmove.c:64 vgdisplay.c:24
+#: vgmerge.c:39 vgmerge.c:72 vgreduce.c:482 vgsplit.c:245
+#, c-format
+msgid "Volume group \"%s\" doesn't exist"
+msgstr ""
+
+#: lvconvert.c:562 lvcreate.c:498 lvrename.c:114 lvresize.c:146 pvchange.c:72
+#: pvdisplay.c:41 pvmove.c:71 pvresize.c:83 reporter.c:76 reporter.c:124
+#: toollib.c:363 toollib.c:383 toollib.c:490 toollib.c:741 vgextend.c:64
+#: vgmerge.c:46 vgmerge.c:78 vgreduce.c:489 vgreduce.c:511 vgrename.c:107
+#: vgsplit.c:252
+#, c-format
+msgid "Skipping clustered volume group %s"
+msgstr ""
+
+#: lvconvert.c:567 lvcreate.c:503 lvrename.c:119 metadata/metadata.c:1377
+#: polldaemon.c:195 pvchange.c:78 pvmove.c:76 pvresize.c:89 toollib.c:163
+#: vgchange.c:534 vgck.c:34 vgconvert.c:54 vgextend.c:69 vgmerge.c:52
+#: vgmerge.c:83 vgreduce.c:541 vgremove.c:35 vgrename.c:113 vgsplit.c:258
+#, c-format
+msgid "Volume group \"%s\" is exported"
+msgstr ""
+
+#: lvconvert.c:572 lvcreate.c:508 lvremove.c:28 lvrename.c:124 pvchange.c:84
+#: pvmove.c:82 pvresize.c:95 vgchange.c:529 vgconvert.c:49 vgexport.c:42
+#: vgextend.c:74 vgmerge.c:58 vgmerge.c:88 vgreduce.c:547 vgrename.c:117
+#: vgsplit.c:270
+#, c-format
+msgid "Volume group \"%s\" is read-only"
+msgstr ""
+
+#: lvconvert.c:577
+#, c-format
+msgid "Logical volume \"%s\" not found in volume group \"%s\""
+msgstr ""
+
+#: lvcreate.c:93 lvresize.c:105
+msgid "Please provide a volume group name"
+msgstr ""
+
+#: lvcreate.c:100
+msgid "Volume group name expected (no slash)"
+msgstr ""
+
+#: lvcreate.c:115
+#, c-format
+msgid "Inconsistent volume group names given: \"%s\" and \"%s\""
+msgstr ""
+
+#: lvcreate.c:138
+#, c-format
+msgid "Logical volume name \"%s\" is invalid"
+msgstr ""
+
+#: lvcreate.c:151 lvresize.c:65
+msgid "Please specify either size or extents (not both)"
+msgstr ""
+
+#: lvcreate.c:157
+msgid "Negative number of extents is invalid"
+msgstr ""
+
+#: lvcreate.c:167
+msgid "Negative size is invalid"
+msgstr ""
+
+#: lvcreate.c:189
+msgid "Negative stripesize is invalid"
+msgstr ""
+
+#: lvcreate.c:194 lvresize.c:192
+#, c-format
+msgid "Stripe size cannot be larger than %s"
+msgstr ""
+
+#: lvcreate.c:202
+msgid "Ignoring stripesize argument with single stripe"
+msgstr ""
+
+#: lvcreate.c:210 lvresize.c:330
+#, c-format
+msgid "Using default stripesize %s"
+msgstr ""
+
+#: lvcreate.c:215
+#, c-format
+msgid "Too few physical volumes on command line for %d-way striping"
+msgstr ""
+
+#: lvcreate.c:221
+#, c-format
+msgid "Number of stripes (%d) must be between %d and %d"
+msgstr ""
+
+#: lvcreate.c:229 lvresize.c:407
+#, c-format
+msgid "Invalid stripe size %s"
+msgstr ""
+
+#: lvcreate.c:246
+#, c-format
+msgid "Too few physical volumes on command line for %d-way mirroring"
+msgstr ""
+
+#: lvcreate.c:309
+msgid "Redundant stripes argument: default is 1"
+msgstr ""
+
+#: lvcreate.c:323
+msgid "Redundant mirrors argument: default is 0"
+msgstr ""
+
+#: lvcreate.c:325 lvresize.c:180
+msgid "Mirrors argument may not be negative"
+msgstr ""
+
+#: lvcreate.c:332
+msgid "-Z is incompatible with snapshots"
+msgstr ""
+
+#: lvcreate.c:354
+msgid "-c is only available with snapshots"
+msgstr ""
+
+#: lvcreate.c:361
+msgid "mirrors and snapshots are currently incompatible"
+msgstr ""
+
+#: lvcreate.c:367
+msgid "mirrors and stripes are currently incompatible"
+msgstr ""
+
+#: lvcreate.c:378
+msgid "--corelog is only available with mirrors"
+msgstr ""
+
+#: lvcreate.c:383
+msgid "--nosync is only available with mirrors"
+msgstr ""
+
+#: lvcreate.c:419
+msgid "Conflicting contiguous and alloc arguments"
+msgstr ""
+
+#: lvcreate.c:448
+msgid "Please specify minor number with --minor when using -My"
+msgstr ""
+
+#: lvcreate.c:453
+msgid "Please specify major number with --major when using -My"
+msgstr ""
+
+#: lvcreate.c:459
+msgid "--major and --minor incompatible with -Mn"
+msgstr ""
+
+#: lvcreate.c:489 pvmove.c:305 toollib.c:481 vgreduce.c:474
+#, c-format
+msgid "Finding volume group \"%s\""
+msgstr ""
+
+#: lvcreate.c:513 lvrename.c:129
+#, c-format
+msgid "Logical volume \"%s\" already exists in volume group \"%s\""
+msgstr ""
+
+#: lvcreate.c:519
+msgid "Metadata does not support mirroring."
+msgstr ""
+
+#: lvcreate.c:536
+#, c-format
+msgid "Reducing requested stripe size %s to maximum, physical extent size %s"
+msgstr ""
+
+#: lvcreate.c:547
+#, c-format
+msgid "Stripe size may not exceed %s"
+msgstr ""
+
+#: lvcreate.c:559 lvresize.c:237
+#, c-format
+msgid "Rounding up size to full physical extent %s"
+msgstr ""
+
+#: lvcreate.c:564
+#, c-format
+msgid "Volume too large (%s) for extent size %s. Upper limit is %s."
+msgstr ""
+
+#: lvcreate.c:583
+#, c-format
+msgid "Please express size as %%VG or %%FREE."
+msgstr ""
+
+#: lvcreate.c:590
+#, c-format
+msgid "Rounding size (%d extents) up to stripe boundary size (%d extents)"
+msgstr ""
+
+#: lvcreate.c:598
+msgid "Can't create snapshot without using device-mapper kernel driver"
+msgstr ""
+
+#: lvcreate.c:604
+msgid "Clustered snapshots are not yet supported."
+msgstr ""
+
+#: lvcreate.c:613
+msgid "Snapshots of snapshots are not supported yet."
+msgstr ""
+
+#: lvcreate.c:618
+msgid "Snapshots of locked devices are not supported yet"
+msgstr ""
+
+#: lvcreate.c:625
+msgid "Snapshots and mirrors may not yet be mixed."
+msgstr ""
+
+#: lvcreate.c:634
+msgid "Unable to create new logical volume with no extents"
+msgstr ""
+
+#: lvcreate.c:640
+#, c-format
+msgid "Insufficient free extents (%u) in volume group %s: %u required"
+msgstr ""
+
+#: lvcreate.c:646
+#, c-format
+msgid "Number of stripes (%u) must not exceed number of physical volumes (%d)"
+msgstr ""
+
+#: lvcreate.c:653
+msgid "Can't create mirror without using device-mapper kernel driver."
+msgstr ""
+
+#: lvcreate.c:672
+msgid "Failed to generate LV name."
+msgstr ""
+
+#: lvcreate.c:685 vgchange.c:445
+#, c-format
+msgid "Volume group %s does not support tags"
+msgstr ""
+
+#: lvcreate.c:709
+msgid ""
+"WARNING: New mirror won't be synchronised. Don't read what you didn't write!"
+msgstr ""
+
+#: lvcreate.c:733
+msgid "Setting read ahead sectors"
+msgstr ""
+
+#: lvcreate.c:741
+#, c-format
+msgid "Setting device number to (%d, %d)"
+msgstr ""
+
+#: lvcreate.c:782
+msgid ""
+"Aborting. Failed to activate snapshot exception store. Remove new LV and "
+"retry."
+msgstr ""
+
+#: lvcreate.c:787
+msgid "Failed to activate new LV."
+msgstr ""
+
+#: lvcreate.c:794
+msgid ""
+"Aborting. Failed to wipe snapshot exception store. Remove new LV and retry."
+msgstr ""
+
+#: lvcreate.c:837
+#, c-format
+msgid "Logical volume \"%s\" created"
+msgstr ""
+
+#: lvdisplay.c:39 lvdisplay.c:48 pvdisplay.c:89 pvdisplay.c:99 vgdisplay.c:67
+#: vgdisplay.c:76
+msgid "Incompatible options selected"
+msgstr ""
+
+#: lvdisplay.c:53
+msgid "Options -v and -c are incompatible"
+msgstr ""
+
+#: lvmchange.c:21
+msgid "With LVM2 and the device mapper, this program is obsolete."
+msgstr ""
+
+#: lvmcmdline.c:289
+msgid "Minor number outside range 0-255"
+msgstr ""
+
+#: lvmcmdline.c:304
+msgid "Major number outside range 0-255"
+msgstr ""
+
+#: lvmcmdline.c:402
+msgid "Couldn't allocate memory."
+msgstr ""
+
+#: lvmcmdline.c:451
+msgid "Out of memory."
+msgstr ""
+
+#: lvmcmdline.c:504
+#, c-format
+msgid ""
+"%s: %s\n"
+"\n"
+"%s"
+msgstr ""
+
+#: lvmcmdline.c:598
+msgid "Unrecognised option."
+msgstr ""
+
+#: lvmcmdline.c:604
+#, c-format
+msgid "Option%s%c%s%s may not be repeated"
+msgstr ""
+
+#: lvmcmdline.c:613
+msgid "Option requires argument."
+msgstr ""
+
+#: lvmcmdline.c:620
+#, c-format
+msgid "Invalid argument %s"
+msgstr ""
+
+#: lvmcmdline.c:639
+#, c-format
+msgid "%s and %s are synonyms. Please only supply one."
+msgstr ""
+
+#: lvmcmdline.c:667
+#, c-format
+msgid "LVM version: %s"
+msgstr ""
+
+#: lvmcmdline.c:669
+#, c-format
+msgid "Library version: %s"
+msgstr ""
+
+#: lvmcmdline.c:671
+#, c-format
+msgid "Driver version: %s"
+msgstr ""
+
+#: lvmcmdline.c:706
+msgid "Partial mode. Incomplete volume groups will be activated read-only."
+msgstr ""
+
+#: lvmcmdline.c:729
+msgid "--trustcache is incompatible with --all"
+msgstr ""
+
+#: lvmcmdline.c:733
+msgid ""
+"WARNING: Cache file of PVs will be trusted. New devices holding PVs may get "
+"ignored."
+msgstr ""
+
+#: lvmcmdline.c:767
+msgid "Available lvm commands:"
+msgstr ""
+
+#: lvmcmdline.c:768
+msgid "Use 'lvm help <command>' for more information"
+msgstr ""
+
+#: lvmcmdline.c:774
+#, c-format
+msgid "%-16.16s%s"
+msgstr ""
+
+#: lvmcmdline.c:794
+msgid "Failed to set overridden configuration entries."
+msgstr ""
+
+#: lvmcmdline.c:858
+msgid "Couldn't copy command line."
+msgstr ""
+
+#: lvmcmdline.c:871
+#, c-format
+msgid "Parsing: %s"
+msgstr ""
+
+#: lvmcmdline.c:877
+msgid "Error during parsing of command line."
+msgstr ""
+
+#: lvmcmdline.c:890
+msgid "Updated config file invalid. Aborting."
+msgstr ""
+
+#: lvmcmdline.c:899
+#, c-format
+msgid "Processing: %s"
+msgstr ""
+
+#: lvmcmdline.c:902
+msgid "O_DIRECT will be used"
+msgstr ""
+
+#: lvmcmdline.c:915
+#, c-format
+msgid "Locking type %d initialisation failed."
+msgstr ""
+
+#: lvmcmdline.c:927
+msgid "Test mode: Wiping internal cache"
+msgstr ""
+
+#: lvmcmdline.c:951
+#, c-format
+msgid "Completed: %s"
+msgstr ""
+
+#: lvmcmdline.c:1073
+#, c-format
+msgid "Line too long (max 255) beginning: %s"
+msgstr ""
+
+#: lvmcmdline.c:1080
+#, c-format
+msgid "Too many arguments: %s"
+msgstr ""
+
+#: lvmcmdline.c:1125
+msgid "Failed to create LVM1 tool pathname"
+msgstr ""
+
+#: lvmcmdline.c:1173
+msgid "Falling back to LVM1 tools, but no command specified."
+msgstr ""
+
+#: lvmcmdline.c:1189
+msgid "Please supply an LVM command."
+msgstr ""
+
+#: lvmcmdline.c:1203
+msgid "No such command. Try 'help'."
+msgstr ""
+
+#: lvmdiskscan.c:38 lvmdiskscan.c:108
+msgid "dev_iter_create failed"
+msgstr ""
+
+#: lvmdiskscan.c:66
+#, c-format
+msgid "%-*s [%15s] %s"
+msgstr ""
+
+#: lvmdiskscan.c:83 lvmdiskscan.c:117
+#, c-format
+msgid "Couldn't get size of \"%s\""
+msgstr ""
+
+#: lvmdiskscan.c:88
+#, c-format
+msgid "dev_close on \"%s\" failed"
+msgstr ""
+
+#: lvmdiskscan.c:103
+msgid "WARNING: only considering LVM devices"
+msgstr ""
+
+#: lvmdiskscan.c:137
+#, c-format
+msgid "%d disk%s"
+msgstr ""
+
+#: lvmdiskscan.c:139
+#, c-format
+msgid "%d partition%s"
+msgstr ""
+
+#: lvmdiskscan.c:142
+#, c-format
+msgid "%d LVM physical volume whole disk%s"
+msgstr ""
+
+#: lvmdiskscan.c:144
+#, c-format
+msgid "%d LVM physical volume%s"
+msgstr ""
+
+#: lvremove.c:33
+#, c-format
+msgid "Can't remove logical volume \"%s\" under snapshot"
+msgstr ""
+
+#: lvremove.c:39
+#, c-format
+msgid "Can't remove logical volume %s used by a mirror"
+msgstr ""
+
+#: lvremove.c:45
+#, c-format
+msgid "Can't remove logical volume %s used as mirror log"
+msgstr ""
+
+#: lvremove.c:51
+#, c-format
+msgid "Can't remove locked LV %s"
+msgstr ""
+
+#: lvremove.c:59
+#, c-format
+msgid "Can't remove open logical volume \"%s\""
+msgstr ""
+
+#: lvremove.c:68
+#, c-format
+msgid "Logical volume \"%s\" not removed"
+msgstr ""
+
+#: lvremove.c:82
+#, c-format
+msgid "Can't get exclusive access to volume \"%s\""
+msgstr ""
+
+#: lvremove.c:90
+#, c-format
+msgid "Unable to deactivate logical volume \"%s\""
+msgstr ""
+
+#: lvremove.c:97
+#, c-format
+msgid "Removing snapshot %s"
+msgstr ""
+
+#: lvremove.c:104
+#, c-format
+msgid "Releasing logical volume \"%s\""
+msgstr ""
+
+#: lvremove.c:106
+#, c-format
+msgid "Error releasing logical volume \"%s\""
+msgstr ""
+
+#: lvremove.c:122
+#, c-format
+msgid "Failed to refresh %s without snapshot."
+msgstr ""
+
+#: lvremove.c:124
+#, c-format
+msgid "Failed to resume %s."
+msgstr ""
+
+#: lvremove.c:127
+#, c-format
+msgid "Logical volume \"%s\" successfully removed"
+msgstr ""
+
+#: lvremove.c:134
+msgid "Please enter one or more logical volume paths"
+msgstr ""
+
+#: lvrename.c:47
+msgid "Old and new logical volume names required"
+msgstr ""
+
+#: lvrename.c:59
+#, c-format
+msgid "Logical volume names must have the same volume group (\"%s\" or \"%s\")"
+msgstr ""
+
+#: lvrename.c:74
+#, c-format
+msgid "New logical volume path exceeds maximum length of %zu!"
+msgstr ""
+
+#: lvrename.c:80
+msgid "New logical volume name may not be blank"
+msgstr ""
+
+#: lvrename.c:90
+#, c-format
+msgid "New logical volume name \"%s\" is invalid"
+msgstr ""
+
+#: lvrename.c:96
+msgid "Old and new logical volume names must differ"
+msgstr ""
+
+#: lvrename.c:135
+#, c-format
+msgid "Existing logical volume \"%s\" not found in volume group \"%s\""
+msgstr ""
+
+#: lvrename.c:143
+#, c-format
+msgid "Cannot rename locked LV %s"
+msgstr ""
+
+#: lvrename.c:150 lvrename.c:158
+#, c-format
+msgid "Mirrored LV, \"%s\" cannot be renamed: %s"
+msgstr ""
+
+#: lvrename.c:169
+msgid "Failed to allocate space for new name"
+msgstr ""
+
+#: lvrename.c:173 vgmerge.c:223 vgrename.c:165
+msgid "Writing out updated volume group"
+msgstr ""
+
+#: lvrename.c:197
+#, c-format
+msgid "Renamed \"%s\" to \"%s\" in volume group \"%s\""
+msgstr ""
+
+#: lvresize.c:83
+msgid "Negative argument not permitted - use lvreduce"
+msgstr ""
+
+#: lvresize.c:88
+msgid "Positive sign not permitted - use lvextend"
+msgstr ""
+
+#: lvresize.c:96
+msgid "Please provide the logical volume name"
+msgstr ""
+
+#: lvresize.c:140
+#, c-format
+msgid "Volume group %s doesn't exist"
+msgstr ""
+
+#: lvresize.c:151
+#, c-format
+msgid "Volume group %s is exported"
+msgstr ""
+
+#: lvresize.c:156
+#, c-format
+msgid "Volume group %s is read-only"
+msgstr ""
+
+#: lvresize.c:162
+#, c-format
+msgid "Logical volume %s not found in volume group %s"
+msgstr ""
+
+#: lvresize.c:171
+msgid "Varied striping not supported. Ignoring."
+msgstr ""
+
+#: lvresize.c:178
+msgid "Mirrors not supported. Ignoring."
+msgstr ""
+
+#: lvresize.c:187
+msgid "Stripesize may not be negative."
+msgstr ""
+
+#: lvresize.c:198
+msgid "Varied stripesize not supported. Ignoring."
+msgstr ""
+
+#: lvresize.c:200
+#, c-format
+msgid "Reducing stripe size %s to maximum, physical extent size %s"
+msgstr ""
+
+#: lvresize.c:211
+msgid "Mirrors and striping cannot be combined yet."
+msgstr ""
+
+#: lvresize.c:215
+msgid "Stripe size must be power of 2"
+msgstr ""
+
+#: lvresize.c:223
+#, c-format
+msgid "Can't resize locked LV %s"
+msgstr ""
+
+#: lvresize.c:263
+#, c-format
+msgid "Unable to reduce %s below 1 extent"
+msgstr ""
+
+#: lvresize.c:272
+msgid "New size of 0 not permitted"
+msgstr ""
+
+#: lvresize.c:277 lvresize.c:414
+#, c-format
+msgid "New size (%d extents) matches existing size (%d extents)"
+msgstr ""
+
+#: lvresize.c:291
+#, c-format
+msgid "VolumeType does not match (%s)"
+msgstr ""
+
+#: lvresize.c:308
+msgid "Please specify number of stripes (-i) and stripesize (-I)"
+msgstr ""
+
+#: lvresize.c:322
+#, c-format
+msgid "Using stripesize of last segment %s"
+msgstr ""
+
+#: lvresize.c:346
+#, c-format
+msgid "Extending %u mirror images."
+msgstr ""
+
+#: lvresize.c:352
+msgid "Cannot vary number of mirrors in LV yet."
+msgstr ""
+
+#: lvresize.c:362
+msgid "Ignoring stripes, stripesize and mirrors arguments when reducing"
+msgstr ""
+
+#: lvresize.c:391
+msgid "Stripesize for striped segment should not be 0!"
+msgstr ""
+
+#: lvresize.c:400
+#, c-format
+msgid ""
+"Rounding size (%d extents) down to stripe boundary size for segment (%d "
+"extents)"
+msgstr ""
+
+#: lvresize.c:421
+#, c-format
+msgid "New size given (%d extents) not larger than existing size (%d extents)"
+msgstr ""
+
+#: lvresize.c:431
+#, c-format
+msgid "New size given (%d extents) not less than existing size (%d extents)"
+msgstr ""
+
+#: lvresize.c:441
+msgid "Mirrors cannot be resized while active yet."
+msgstr ""
+
+#: lvresize.c:447
+msgid "Snapshot origin volumes cannot be reduced in size yet."
+msgstr ""
+
+#: lvresize.c:455
+msgid ""
+"Snapshot origin volumes can be resized only while inactive: try lvchange -an"
+msgstr ""
+
+#: lvresize.c:463
+msgid "Ignoring PVs on command line when reducing"
+msgstr ""
+
+#: lvresize.c:474
+msgid "lv_info failed: aborting"
+msgstr ""
+
+#: lvresize.c:479
+#, c-format
+msgid "Logical volume %s must be activated before resizing filesystem"
+msgstr ""
+
+#: lvresize.c:485
+#, c-format
+msgid "WARNING: Reducing active%s logical volume to %s"
+msgstr ""
+
+#: lvresize.c:490
+msgid "THIS MAY DESTROY YOUR DATA (filesystem etc.)"
+msgstr ""
+
+#: lvresize.c:497
+#, c-format
+msgid "Logical volume %s NOT reduced"
+msgstr ""
+
+#: lvresize.c:508
+#, c-format
+msgid "Couldn't create LV path for %s"
+msgstr ""
+
+#: lvresize.c:516
+msgid "Couldn't generate new LV size string"
+msgstr ""
+
+#: lvresize.c:540
+#, c-format
+msgid "%sing logical volume %s to %s"
+msgstr ""
+
+#: lvresize.c:589
+#, c-format
+msgid "Logical volume %s successfully resized"
+msgstr ""
+
+#: lvresize.c:611
+#, c-format
+msgid "Finding volume group %s"
+msgstr ""
+
+#: lvscan.c:64
+#, c-format
+msgid "%s%s '%s%s/%s' [%s] %s"
+msgstr ""
+
+#: lvscan.c:79
+msgid "No additional command line arguments allowed"
+msgstr ""
+
+#: metadata/lv_manip.c:96
+msgid "alloc_lv_segment: Missing segtype."
+msgstr ""
+
+#: metadata/lv_manip.c:131
+msgid "Failed to find snapshot segtype"
+msgstr ""
+
+#: metadata/lv_manip.c:139
+msgid "Couldn't allocate new snapshot segment."
+msgstr ""
+
+#: metadata/lv_manip.c:280
+#, c-format
+msgid "Segment extent reduction %unot divisible by #stripes %u"
+msgstr ""
+
+#: metadata/lv_manip.c:445
+msgid "Striped mirrors are not supported yet"
+msgstr ""
+
+#: metadata/lv_manip.c:450
+msgid "Can't mix striping or mirroring with creation of a mirrored PV yet"
+msgstr ""
+
+#: metadata/lv_manip.c:456
+msgid "Can't mix striping or pvmove with a mirror log yet."
+msgstr ""
+
+#: metadata/lv_manip.c:471
+msgid "allocation handle allocation failed"
+msgstr ""
+
+#: metadata/lv_manip.c:481
+msgid "allocation pool creation failed"
+msgstr ""
+
+#: metadata/lv_manip.c:516 report/report.c:92 report/report.c:152
+msgid "dm_pool_begin_object failed"
+msgstr ""
+
+#: metadata/lv_manip.c:523 metadata/lv_manip.c:528 metadata/lv_manip.c:535
+#: report/report.c:112 report/report.c:123 report/report.c:129
+#: report/report.c:135 report/report.c:159 report/report.c:165
+msgid "dm_pool_grow_object failed"
+msgstr ""
+
+#: metadata/lv_manip.c:541
+#, c-format
+msgid "Parallel PVs at LE %u length %u: %s"
+msgstr ""
+
+#: metadata/lv_manip.c:574
+msgid "Couldn't allocate new LV segment."
+msgstr ""
+
+#: metadata/lv_manip.c:654
+msgid "alloced_area allocation failed"
+msgstr ""
+
+#: metadata/lv_manip.c:705
+#, c-format
+msgid "Failed to find segment for %s extent %u"
+msgstr ""
+
+#: metadata/lv_manip.c:907
+#, c-format
+msgid "Insufficient free space: %u extents needed, but only %u available"
+msgstr ""
+
+#: metadata/lv_manip.c:1081
+msgid "_allocate called with no work to do!"
+msgstr ""
+
+#: metadata/lv_manip.c:1105
+msgid "Not enough PVs with free space available for parallel allocation."
+msgstr ""
+
+#: metadata/lv_manip.c:1107
+msgid "Consider --alloc anywhere if desperate."
+msgstr ""
+
+#: metadata/lv_manip.c:1120
+msgid "Couldn't allocate areas array."
+msgstr ""
+
+#: metadata/lv_manip.c:1137
+#, c-format
+msgid ""
+"Insufficient suitable %sallocatable extents for logical volume %s: %u more "
+"required"
+msgstr ""
+
+#: metadata/lv_manip.c:1147
+#, c-format
+msgid "Insufficient extents for log allocation for logical volume %s."
+msgstr ""
+
+#: metadata/lv_manip.c:1168
+msgid "Couldn't allocate new zero segment."
+msgstr ""
+
+#: metadata/lv_manip.c:1201
+msgid "allocate_extents does not handle virtual segments"
+msgstr ""
+
+#: metadata/lv_manip.c:1207
+#, c-format
+msgid "Metadata format (%s) does not support required LV segment type (%s)."
+msgstr ""
+
+#: metadata/lv_manip.c:1210
+msgid "Consider changing the metadata format by running vgconvert."
+msgstr ""
+
+#: metadata/lv_manip.c:1251
+msgid "Missing segtype in lv_add_segment()."
+msgstr ""
+
+#: metadata/lv_manip.c:1256
+msgid "lv_add_segment cannot handle virtual segments"
+msgstr ""
+
+#: metadata/lv_manip.c:1270
+msgid "Couldn't merge segments after extending logical volume."
+msgstr ""
+
+#: metadata/lv_manip.c:1292
+msgid "Log segments can only be added to an empty LV"
+msgstr ""
+
+#: metadata/lv_manip.c:1301
+msgid "Couldn't allocate new mirror log segment."
+msgstr ""
+
+#: metadata/lv_manip.c:1339
+#, c-format
+msgid "Log LV %s is empty."
+msgstr ""
+
+#: metadata/lv_manip.c:1349
+msgid "Couldn't allocate new mirror segment."
+msgstr ""
+
+#: metadata/lv_manip.c:1384
+msgid "Mirrored LV must only have one segment."
+msgstr ""
+
+#: metadata/lv_manip.c:1394
+#, c-format
+msgid "Failed to allocate widened LV segment for %s."
+msgstr ""
+
+#: metadata/lv_manip.c:1446
+#, c-format
+msgid "Aborting. Failed to extend %s."
+msgstr ""
+
+#: metadata/lv_manip.c:1499
+#, c-format
+msgid "Maximum number of logical volumes (%u) reached in volume group %s"
+msgstr ""
+
+#: metadata/lv_manip.c:1506
+msgid "Failed to generate unique name for the new logical volume"
+msgstr ""
+
+#: metadata/lv_manip.c:1512
+#, c-format
+msgid "Creating logical volume %s"
+msgstr ""
+
+#: metadata/lv_manip.c:1516
+msgid "lv_list allocation failed"
+msgstr ""
+
+#: metadata/lv_manip.c:1526
+msgid "lv name strdup failed"
+msgstr ""
+
+#: metadata/lv_manip.c:1574 metadata/metadata.c:986
+msgid "pv_list allocation failed"
+msgstr ""
+
+#: metadata/lv_manip.c:1596
+msgid "parallel_areas allocation failed"
+msgstr ""
+
+#: metadata/lv_manip.c:1604
+msgid "allocation failed"
+msgstr ""
+
+#: metadata/merge.c:72
+#, c-format
+msgid "LV %s invalid: segment %u should begin at LE %u (found %u)."
+msgstr ""
+
+#: metadata/merge.c:82
+#, c-format
+msgid "LV %s: segment %u has inconsistent area_len %u"
+msgstr ""
+
+#: metadata/merge.c:90
+#, c-format
+msgid "LV %s: segment %u has log LV but is not mirrored"
+msgstr ""
+
+#: metadata/merge.c:97
+#, c-format
+msgid "LV %s: segment %u log LV %s is not a mirror log"
+msgstr ""
+
+#: metadata/merge.c:105
+#, c-format
+msgid "LV %s: segment %u log LV does not point back to mirror segment"
+msgstr ""
+
+#: metadata/merge.c:115
+#, c-format
+msgid "LV %s: segment %u mirror image is not mirrored"
+msgstr ""
+
+#: metadata/merge.c:124
+#, c-format
+msgid "LV %s: segment %u has unassigned area %u."
+msgstr ""
+
+#: metadata/merge.c:132
+#, c-format
+msgid "LV %s: segment %u has inconsistent PV area %u"
+msgstr ""
+
+#: metadata/merge.c:141
+#, c-format
+msgid "LV %s: segment %u has inconsistent LV area %u"
+msgstr ""
+
+#: metadata/merge.c:152
+#, c-format
+msgid "LV %s: segment %u mirror image %u missing mirror ptr"
+msgstr ""
+
+#: metadata/merge.c:174
+#, c-format
+msgid "LV %s: inconsistent LE count %u != %u"
+msgstr ""
+
+#: metadata/merge.c:195
+#, c-format
+msgid "Unable to split the %s segment at LE %u in LV %s"
+msgstr ""
+
+#: metadata/merge.c:208
+msgid "Couldn't allocate cloned LV segment."
+msgstr ""
+
+#: metadata/merge.c:213
+msgid "LV segment tags duplication failed"
+msgstr ""
+
+#: metadata/merge.c:240
+#, c-format
+msgid "Split %s:%u[%u] at %u: %s LE %u"
+msgstr ""
+
+#: metadata/merge.c:256
+#, c-format
+msgid "Split %s:%u[%u] at %u: %s PE %u"
+msgstr ""
+
+#: metadata/merge.c:263 metadata/metadata.c:495
+#, c-format
+msgid "Unassigned area %u found in segment"
+msgstr ""
+
+#: metadata/merge.c:282
+#, c-format
+msgid "Segment with extent %u in LV %s not found"
+msgstr ""
+
+#: metadata/metadata.c:43
+#, c-format
+msgid "Adding physical volume '%s' to volume group '%s'"
+msgstr ""
+
+#: metadata/metadata.c:47 metadata/metadata.c:1008
+#, c-format
+msgid "pv_list allocation for '%s' failed"
+msgstr ""
+
+#: metadata/metadata.c:53
+#, c-format
+msgid "%s not identified as an existing physical volume"
+msgstr ""
+
+#: metadata/metadata.c:59
+#, c-format
+msgid "Physical volume '%s' is already in volume group '%s'"
+msgstr ""
+
+#: metadata/metadata.c:65
+#, c-format
+msgid "Physical volume %s is of different format type (%s)"
+msgstr ""
+
+#: metadata/metadata.c:72
+#, c-format
+msgid "Physical volume %s might be constructed from same volume group %s"
+msgstr ""
+
+#: metadata/metadata.c:78 metadata/metadata.c:199
+#, c-format
+msgid "vg->name allocation failed for '%s'"
+msgstr ""
+
+#: metadata/metadata.c:100
+#, c-format
+msgid "Format-specific setup of physical volume '%s' failed."
+msgstr ""
+
+#: metadata/metadata.c:106
+#, c-format
+msgid "Physical volume '%s' listed more than once."
+msgstr ""
+
+#: metadata/metadata.c:112
+#, c-format
+msgid "No space for '%s' - volume group '%s' holds max %d physical volume(s)."
+msgstr ""
+
+#: metadata/metadata.c:127
+#, c-format
+msgid "Unable to add %s to %s: new extent count (%lu) exceeds limit (%u)."
+msgstr ""
+
+#: metadata/metadata.c:148
+msgid "PV tags duplication failed"
+msgstr ""
+
+#: metadata/metadata.c:170
+#, c-format
+msgid "get_pv_from_vg_by_id: vg_read failed to read VG %s"
+msgstr ""
+
+#: metadata/metadata.c:176
+#, c-format
+msgid "Warning: Volume group %s is not consistent"
+msgstr ""
+
+#: metadata/metadata.c:205
+#, c-format
+msgid "pv->vg_name allocation failed for '%s'"
+msgstr ""
+
+#: metadata/metadata.c:222
+#, c-format
+msgid "Unable to add physical volume '%s' to volume group '%s'."
+msgstr ""
+
+#: metadata/metadata.c:260
+#, c-format
+msgid "A volume group called '%s' already exists."
+msgstr ""
+
+#: metadata/metadata.c:266
+#, c-format
+msgid "Couldn't create uuid for volume group '%s'."
+msgstr ""
+
+#: metadata/metadata.c:309 metadata/metadata.c:1085 metadata/metadata.c:1151
+msgid "Failed to create format instance"
+msgstr ""
+
+#: metadata/metadata.c:315
+#, c-format
+msgid "Format specific setup of volume group '%s' failed."
+msgstr ""
+
+#: metadata/metadata.c:338
+#, c-format
+msgid "New size %lu for %s%s not an exact number of new extents."
+msgstr ""
+
+#: metadata/metadata.c:346
+#, c-format
+msgid "New extent count %lu for %s%s exceeds 32 bits."
+msgstr ""
+
+#: metadata/metadata.c:556
+#, c-format
+msgid "Failed to create random uuid for %s."
+msgstr ""
+
+#: metadata/metadata.c:575 pvresize.c:128
+#, c-format
+msgid "WARNING: %s: Overriding real size. You could lose data."
+msgstr ""
+
+#: metadata/metadata.c:577
+#, c-format
+msgid "%s: Pretending size is %lu sectors."
+msgstr ""
+
+#: metadata/metadata.c:583 pvresize.c:136
+#, c-format
+msgid "%s: Size must exceed minimum of %ld sectors."
+msgstr ""
+
+#: metadata/metadata.c:601
+#, c-format
+msgid "%s: Format-specific setup of physical volume failed."
+msgstr ""
+
+#: metadata/metadata.c:699
+#, c-format
+msgid "Physical volume %s not found"
+msgstr ""
+
+#: metadata/metadata.c:704
+#, c-format
+msgid "Physical volume %s not in a volume group"
+msgstr ""
+
+#: metadata/metadata.c:780
+#, c-format
+msgid "Internal error: Duplicate PV id %s detected for %s in %s."
+msgstr ""
+
+#: metadata/metadata.c:789
+#, c-format
+msgid "Internal error: VG name for PV %s is corrupted"
+msgstr ""
+
+#: metadata/metadata.c:796 metadata/metadata.c:1278
+#, c-format
+msgid "Internal error: PV segments corrupted in %s."
+msgstr ""
+
+#: metadata/metadata.c:806
+#, c-format
+msgid "Internal error: Duplicate LV name %s detected in %s."
+msgstr ""
+
+#: metadata/metadata.c:816
+#, c-format
+msgid "Internal error: Duplicate LV id %s detected for %s and %s in %s."
+msgstr ""
+
+#: metadata/metadata.c:827 metadata/metadata.c:1285
+#, c-format
+msgid "Internal error: LV segments corrupted in %s."
+msgstr ""
+
+#: metadata/metadata.c:851
+#, c-format
+msgid "Cannot change metadata for partial volume group %s"
+msgstr ""
+
+#: metadata/metadata.c:857
+msgid "Aborting vg_write: No metadata areas to write to!"
+msgstr ""
+
+#: metadata/metadata.c:866
+msgid "Format does not support writing volumegroup metadata areas"
+msgstr ""
+
+#: metadata/metadata.c:969
+msgid "vg allocation failed"
+msgstr ""
+
+#: metadata/metadata.c:977
+msgid "vg name allocation failed"
+msgstr ""
+
+#: metadata/metadata.c:1049
+msgid "Internal error: vg_read requires vgname with pre-commit."
+msgstr ""
+
+#: metadata/metadata.c:1113 metadata/metadata.c:1122
+#, c-format
+msgid "Cached VG %s had incorrect PV list"
+msgstr ""
+
+#: metadata/metadata.c:1201
+#, c-format
+msgid "Inconsistent pre-commit metadata copies for volume group %s"
+msgstr ""
+
+#: metadata/metadata.c:1212
+#, c-format
+msgid "Inconsistent metadata copies found for partial volume group %s"
+msgstr ""
+
+#: metadata/metadata.c:1220
+#, c-format
+msgid "Inconsistent metadata UUIDs found for volume group %s"
+msgstr ""
+
+#: metadata/metadata.c:1226
+#, c-format
+msgid "Inconsistent metadata found for VG %s - updating to use version %u"
+msgstr ""
+
+#: metadata/metadata.c:1230
+msgid "Automatic metadata correction failed"
+msgstr ""
+
+#: metadata/metadata.c:1235
+msgid "Automatic metadata correction commit failed"
+msgstr ""
+
+#: metadata/metadata.c:1247
+#, c-format
+msgid "Removing PV %s (%s) that no longer belongs to VG %s"
+msgstr ""
+
+#: metadata/metadata.c:1257
+#, c-format
+msgid "WARNING: Interrupted pvmove detected in volume group %s"
+msgstr ""
+
+#: metadata/metadata.c:1259
+msgid "Please restore the metadata by running vgcfgrestore."
+msgstr ""
+
+#: metadata/metadata.c:1316 metadata/metadata.c:1348
+#, c-format
+msgid "Volume group %s metadata is inconsistent"
+msgstr ""
+
+#: metadata/metadata.c:1335
+msgid "vg_read_by_vgid: get_vgs failed"
+msgstr ""
+
+#: metadata/metadata.c:1369
+#, c-format
+msgid "Finding volume group for uuid %s"
+msgstr ""
+
+#: metadata/metadata.c:1371
+#, c-format
+msgid "Volume group for uuid not found: %s"
+msgstr ""
+
+#: metadata/metadata.c:1375
+#, c-format
+msgid "Found volume group \"%s\""
+msgstr ""
+
+#: metadata/metadata.c:1381
+#, c-format
+msgid "Can't find logical volume id %s"
+msgstr ""
+
+#: metadata/metadata.c:1405
+#, c-format
+msgid "No physical volume label read from %s"
+msgstr ""
+
+#: metadata/metadata.c:1415
+#, c-format
+msgid "pv allocation for '%s' failed"
+msgstr ""
+
+#: metadata/metadata.c:1424
+#, c-format
+msgid "Failed to read existing physical volume '%s'"
+msgstr ""
+
+#: metadata/metadata.c:1466
+msgid "PV list allocation failed"
+msgstr ""
+
+#: metadata/metadata.c:1474
+msgid "get_pvs: get_vgs failed"
+msgstr ""
+
+#: metadata/metadata.c:1498
+#, c-format
+msgid "Warning: Volume Group %s is not consistent"
+msgstr ""
+
+#: metadata/metadata.c:1516
+msgid "Format does not support writing physical volumes"
+msgstr ""
+
+#: metadata/metadata.c:1521
+#, c-format
+msgid "Assertion failed: can't _pv_write non-orphan PV (in VG %s)"
+msgstr ""
+
+#: metadata/metadata.c:1547 vgreduce.c:410
+#, c-format
+msgid ""
+"Failed to clear metadata from physical volume \"%s\" after removal from \"%s"
+"\""
+msgstr ""
+
+#: metadata/metadata.c:1570 pvcreate.c:81
+#, c-format
+msgid "Device %s not found (or ignored by filtering)."
+msgstr ""
+
+#: metadata/metadata.c:1579
+#, c-format
+msgid "Could not find LVM label on %s"
+msgstr ""
+
+#: metadata/metadata.c:1584
+#, c-format
+msgid "Found label on %s, sector %lu, type=%s"
+msgstr ""
+
+#: metadata/mirror.c:52 mirror/mirrored.c:322
+#, c-format
+msgid "Using reduced mirror region size of %u sectors"
+msgstr ""
+
+#: metadata/mirror.c:94
+msgid "Aborting. Unable to tag."
+msgstr ""
+
+#: metadata/mirror.c:100
+msgid "Intermediate VG commit for orphan volume failed."
+msgstr ""
+
+#: metadata/mirror.c:138
+#, c-format
+msgid "Reducing mirror set from %u to %u image(s)%s."
+msgstr ""
+
+#: metadata/mirror.c:183
+msgid "No mirror images found using specified PVs."
+msgstr ""
+
+#: metadata/mirror.c:222
+msgid "intermediate VG write failed."
+msgstr ""
+
+#: metadata/mirror.c:277
+msgid "Bad activation/mirror_log_fault_policy"
+msgstr ""
+
+#: metadata/mirror.c:279
+msgid "Bad activation/mirror_device_fault_policy"
+msgstr ""
+
+#: metadata/mirror.c:317
+#, c-format
+msgid "WARNING: Failed to replace mirror device in %s/%s"
+msgstr ""
+
+#: metadata/mirror.c:321
+#, c-format
+msgid ""
+"WARNING: Use 'lvconvert -m %d %s/%s --corelog' to replace failed devices"
+msgstr ""
+
+#: metadata/mirror.c:324 metadata/mirror.c:341
+#, c-format
+msgid "WARNING: Use 'lvconvert -m %d %s/%s' to replace failed devices"
+msgstr ""
+
+#: metadata/mirror.c:338
+#, c-format
+msgid "WARNING: Failed to replace mirror log device in %s/%s"
+msgstr ""
+
+#: metadata/mirror.c:362
+#, c-format
+msgid "WARNING: Unable to determine mirror sync status of %s/%s."
+msgstr ""
+
+#: metadata/mirror.c:380
+#, c-format
+msgid "WARNING: Bad device removed from mirror volume, %s/%s"
+msgstr ""
+
+#: metadata/mirror.c:393
+#, c-format
+msgid "WARNING: Unable to find substitute device for mirror volume, %s/%s"
+msgstr ""
+
+#: metadata/mirror.c:397
+#, c-format
+msgid ""
+"WARNING: Mirror volume, %s/%s restored - substitute for failed device found."
+msgstr ""
+
+#: metadata/mirror.c:402
+#, c-format
+msgid ""
+"WARNING: Mirror volume, %s/%s converted to linear due to device failure."
+msgstr ""
+
+#: metadata/mirror.c:405
+#, c-format
+msgid "WARNING: Mirror volume, %s/%s disk log removed due to device failure."
+msgstr ""
+
+#: metadata/mirror.c:428 metadata/mirror.c:434
+msgid "img_name allocation failed. Remove new LV and retry."
+msgstr ""
+
+#: metadata/mirror.c:443
+msgid "Aborting. Failed to create mirror image LV. Remove new LV and retry."
+msgstr ""
+
+#: metadata/mirror.c:455
+#, c-format
+msgid ""
+"Aborting. Failed to add mirror image segment to %s. Remove new LV and retry."
+msgstr ""
+
+#: metadata/mirror.c:477 metadata/mirror.c:518
+msgid "img_lvs allocation failed. Remove new LV and retry."
+msgstr ""
+
+#: metadata/mirror.c:499
+msgid "Aborting. Failed to add mirror segment. Remove new LV and retry."
+msgstr ""
+
+#: metadata/mirror.c:632
+#, c-format
+msgid "Matched PE range %u-%u against %s %u len %u"
+msgstr ""
+
+#: metadata/mirror.c:641 metadata/mirror.c:872 vgreduce.c:139
+msgid "lv_list alloc failed"
+msgstr ""
+
+#: metadata/mirror.c:651
+#, c-format
+msgid "Moving %s:%u-%u of %s/%s"
+msgstr ""
+
+#: metadata/mirror.c:664
+msgid "Unable to allocate temporary LV for pvmove."
+msgstr ""
+
+#: metadata/mirror.c:679
+#, c-format
+msgid "Moving %u extents of logical volume %s/%s"
+msgstr ""
+
+#: metadata/mirror.c:711
+msgid "No segment found with LE"
+msgstr ""
+
+#: metadata/mirror.c:722
+msgid "Incompatible segments"
+msgstr ""
+
+#: metadata/mirror.c:747
+msgid "Missing error segtype"
+msgstr ""
+
+#: metadata/mirror.c:853
+msgid "lvs list alloc failed"
+msgstr ""
+
+#: metadata/pv_manip.c:30
+msgid "pv_segment allocation failed"
+msgstr ""
+
+#: metadata/pv_manip.c:121
+#, c-format
+msgid "Segment with extent %u in PV %s not found"
+msgstr ""
+
+#: metadata/pv_manip.c:161
+#, c-format
+msgid "Missing PV segment on %s at %u."
+msgstr ""
+
+#: metadata/pv_manip.c:178
+#, c-format
+msgid "release_pv_segment with unallocated segment: %s PE %u"
+msgstr ""
+
+#: metadata/pv_manip.c:238
+#, c-format
+msgid "%s %u: %6u %6u: %s(%u:%u)"
+msgstr ""
+
+#: metadata/pv_manip.c:244
+#, c-format
+msgid "Gap in pvsegs: %u, %u"
+msgstr ""
+
+#: metadata/pv_manip.c:250
+msgid "Wrong lvseg area type"
+msgstr ""
+
+#: metadata/pv_manip.c:254
+msgid "Inconsistent pvseg pointers"
+msgstr ""
+
+#: metadata/pv_manip.c:258
+#, c-format
+msgid "Inconsistent length: %u %u"
+msgstr ""
+
+#: metadata/pv_manip.c:269
+#, c-format
+msgid "PV segment pe_count mismatch: %u != %u"
+msgstr ""
+
+#: metadata/pv_manip.c:275
+#, c-format
+msgid "PV segment pe_alloc_count mismatch: %u != %u"
+msgstr ""
+
+#: metadata/pv_manip.c:285
+#, c-format
+msgid "PV segment VG pv_count mismatch: %u != %u"
+msgstr ""
+
+#: metadata/pv_manip.c:291
+#, c-format
+msgid "PV segment VG free_count mismatch: %u != %u"
+msgstr ""
+
+#: metadata/pv_manip.c:297
+#, c-format
+msgid "PV segment VG extent_count mismatch: %u != %u"
+msgstr ""
+
+#: metadata/pv_manip.c:311
+#, c-format
+msgid "%s: cannot resize to %u extents as %u are allocated."
+msgstr ""
+
+#: metadata/pv_manip.c:324
+#, c-format
+msgid "%s: cannot resize to %u extents as later ones are allocated."
+msgstr ""
+
+#: metadata/pv_manip.c:356
+#, c-format
+msgid "%s: cannot resize to %u extents as there is only room for %lu."
+msgstr ""
+
+#: metadata/pv_manip.c:385
+#, c-format
+msgid "No change to size of physical volume %s."
+msgstr ""
+
+#: metadata/pv_manip.c:390
+#, c-format
+msgid "Resizing physical volume %s from %u to %u extents."
+msgstr ""
+
+#: metadata/pv_map.c:48
+#, c-format
+msgid "Allowing allocation on %s start PE %u length %u"
+msgstr ""
+
+#: metadata/pv_map.c:176
+msgid "create_pv_maps alloc failed"
+msgstr ""
+
+#: metadata/pv_map.c:183
+#, c-format
+msgid "Couldn't create physical volume maps in %s"
+msgstr ""
+
+#: metadata/segtype.c:30
+#, c-format
+msgid "Unrecognised segment type %s"
+msgstr ""
+
+#: metadata/snapshot_manip.c:63
+#, c-format
+msgid "'%s' is already in use as a snapshot."
+msgstr ""
+
+#: metadata/snapshot_manip.c:104
+#, c-format
+msgid "Failed to remove internal snapshot LV %s"
+msgstr ""
+
+#: mirror/mirrored.c:57
+#, c-format
+msgid " Mirrors\t\t%u"
+msgstr ""
+
+#: mirror/mirrored.c:58
+#, c-format
+msgid " Mirror size\t\t%u"
+msgstr ""
+
+#: mirror/mirrored.c:60
+#, c-format
+msgid " Mirror log volume\t%s"
+msgstr ""
+
+#: mirror/mirrored.c:65
+#, c-format
+msgid " Mirror region size\t%s"
+msgstr ""
+
+#: mirror/mirrored.c:68
+msgid " Mirror original:"
+msgstr ""
+
+#: mirror/mirrored.c:70
+msgid " Mirror destinations:"
+msgstr ""
+
+#: mirror/mirrored.c:79
+#, c-format
+msgid "Couldn't read 'mirror_count' for segment '%s'."
+msgstr ""
+
+#: mirror/mirrored.c:98
+#, c-format
+msgid "Couldn't read 'extents_moved' for segment '%s'."
+msgstr ""
+
+#: mirror/mirrored.c:107
+#, c-format
+msgid "Couldn't read 'region_size' for segment '%s'."
+msgstr ""
+
+#: mirror/mirrored.c:115
+msgid "Mirror log type must be a string."
+msgstr ""
+
+#: mirror/mirrored.c:120
+#, c-format
+msgid "Unrecognised mirror log in segment %s."
+msgstr ""
+
+#: mirror/mirrored.c:128
+#, c-format
+msgid "Missing region size for mirror log for segment '%s'."
+msgstr ""
+
+#: mirror/mirrored.c:134
+#, c-format
+msgid "Couldn't find mirrors array for segment '%s'."
+msgstr ""
+
+#: mirror/mirrored.c:163
+msgid "struct mirr_state allocation failed"
+msgstr ""
+
+#: mirror/mirrored.c:193
+#, c-format
+msgid "Mirror status: %s"
+msgstr ""
+
+#: mirror/mirrored.c:196
+#, c-format
+msgid "Failure parsing mirror status mirror count: %s"
+msgstr ""
+
+#: mirror/mirrored.c:204
+#, c-format
+msgid "Failure parsing mirror status devices: %s"
+msgstr ""
+
+#: mirror/mirrored.c:213
+#, c-format
+msgid "Failure parsing mirror status fraction: %s"
+msgstr ""
+
+#: mirror/mirrored.c:245
+#, c-format
+msgid "Failed to build uuid for log LV %s."
+msgstr ""
+
+#: mirror/mirrored.c:252
+#, c-format
+msgid "Failed to build uuid for mirror LV %s."
+msgstr ""
+
+#: mirror/mirrored.c:310
+msgid "Missing region size for mirror segment."
+msgstr ""
+
+#: mirror/mirrored.c:505
+msgid "cluster log string list allocation failed"
+msgstr ""
+
+#: mirror/mirrored.c:510
+msgid "mirror string list allocation failed"
+msgstr ""
+
+#: misc/lvm-exec.c:31
+#, c-format
+msgid "Executing: %s %s %s %s"
+msgstr ""
+
+#: misc/lvm-exec.c:34 polldaemon.c:39
+#, c-format
+msgid "fork failed: %s"
+msgstr ""
+
+#: misc/lvm-exec.c:48
+#, c-format
+msgid "wait4 child process %u failed: %s"
+msgstr ""
+
+#: misc/lvm-exec.c:54
+#, c-format
+msgid "Child %u exited abnormally"
+msgstr ""
+
+#: misc/lvm-exec.c:59
+#, c-format
+msgid "%s failed: %u"
+msgstr ""
+
+#: misc/lvm-file.c:55
+msgid "Not enough space to build temporary file string."
+msgstr ""
+
+#: misc/lvm-file.c:102
+#, c-format
+msgid "%s: rename to %s failed"
+msgstr ""
+
+#: misc/lvm-file.c:148
+#, c-format
+msgid "Creating directory \"%s\""
+msgstr ""
+
+#: misc/lvm-file.c:189
+#, c-format
+msgid "Directory \"%s\" not found"
+msgstr ""
+
+#: misc/lvm-file.c:220
+msgid "sync_dir failed in strdup"
+msgstr ""
+
+#: misc/lvm-file.c:269
+msgid "fcntl_lock_file failed in strdup."
+msgstr ""
+
+#: misc/lvm-file.c:283
+#, c-format
+msgid "Locking %s (%s, %hd)"
+msgstr ""
+
+#: misc/lvm-file.c:313
+#, c-format
+msgid "Unlocking fd %d"
+msgstr ""
+
+#: misc/lvm-file.c:316
+#, c-format
+msgid "fcntl unlock failed on fd %d: %s"
+msgstr ""
+
+#: misc/lvm-file.c:320
+#, c-format
+msgid "lock file close failed on fd %d: %s"
+msgstr ""
+
+#: misc/lvm-string.c:107
+#, c-format
+msgid "build_dm_name: Allocation failed for %zu for %s %s %s."
+msgstr ""
+
+#: misc/sharedlib.c:48
+#, c-format
+msgid "Not loading shared %s library %s in static mode."
+msgstr ""
+
+#: misc/sharedlib.c:55
+#, c-format
+msgid "Opening shared %s library %s"
+msgstr ""
+
+#: misc/sharedlib.c:59 misc/sharedlib.c:62
+#, c-format
+msgid "Unable to open external %s library %s: %s"
+msgstr ""
+
+#: mm/memlock.c:99
+msgid "Locking memory"
+msgstr ""
+
+#: mm/memlock.c:108 mm/memlock.c:122
+#, c-format
+msgid "setpriority %u failed: %s"
+msgstr ""
+
+#: mm/memlock.c:118
+msgid "Unlocking memory"
+msgstr ""
+
+#: mm/memlock.c:130
+#, c-format
+msgid "memlock_count inc to %d"
+msgstr ""
+
+#: mm/memlock.c:137
+#, c-format
+msgid "memlock_count dec to %d"
+msgstr ""
+
+#: polldaemon.c:34
+msgid "Forking background process"
+msgstr ""
+
+#: polldaemon.c:49
+#, c-format
+msgid "Background process failed to setsid: %s"
+msgstr ""
+
+#: polldaemon.c:80
+msgid "Failed to generate list of copied LVs: can't abort."
+msgstr ""
+
+#: polldaemon.c:90
+msgid "ABORTING: Mirror percentage check failed."
+msgstr ""
+
+#: polldaemon.c:96 polldaemon.c:98
+#, c-format
+msgid "%s: Moved: %.1f%%"
+msgstr ""
+
+#: polldaemon.c:107
+msgid "ABORTING: Failed to generate list of copied LVs"
+msgstr ""
+
+#: polldaemon.c:119
+msgid "ABORTING: Segment progression failed."
+msgstr ""
+
+#: polldaemon.c:149
+#, c-format
+msgid "ABORTING: Can't reread VG for %s"
+msgstr ""
+
+#: polldaemon.c:156
+#, c-format
+msgid "ABORTING: Can't find mirror LV in %s for %s"
+msgstr ""
+
+#: polldaemon.c:184
+#, c-format
+msgid "Couldn't read volume group %s"
+msgstr ""
+
+#: polldaemon.c:189
+#, c-format
+msgid "Volume Group %s inconsistent - skipping"
+msgstr ""
+
+#: polldaemon.c:241
+#, c-format
+msgid "Checking progress every %u seconds"
+msgstr ""
+
+#: pvchange.c:55
+#, c-format
+msgid "Finding volume group of physical volume \"%s\""
+msgstr ""
+
+#: pvchange.c:65 pvresize.c:75
+#, c-format
+msgid "Unable to find volume group of \"%s\""
+msgstr ""
+
+#: pvchange.c:90 pvresize.c:101
+#, c-format
+msgid "Unable to find \"%s\" in volume group \"%s\""
+msgstr ""
+
+#: pvchange.c:97
+#, c-format
+msgid "Volume group containing %s does not support tags"
+msgstr ""
+
+#: pvchange.c:103
+#, c-format
+msgid "Volume group containing %s has active logical volumes"
+msgstr ""
+
+#: pvchange.c:112
+#, c-format
+msgid "Can't change tag on Physical Volume %s not in volume group"
+msgstr ""
+
+#: pvchange.c:117 pvresize.c:48
+msgid "Can't get lock for orphans"
+msgstr ""
+
+#: pvchange.c:123 pvresize.c:54
+#, c-format
+msgid "Unable to read PV \"%s\""
+msgstr ""
+
+#: pvchange.c:132
+#, c-format
+msgid "Allocatability not supported by orphan %s format PV %s"
+msgstr ""
+
+#: pvchange.c:140
+#, c-format
+msgid "Physical volume \"%s\" is already allocatable"
+msgstr ""
+
+#: pvchange.c:150
+#, c-format
+msgid "Physical volume \"%s\" is already unallocatable"
+msgstr ""
+
+#: pvchange.c:160
+#, c-format
+msgid "Setting physical volume \"%s\" allocatable"
+msgstr ""
+
+#: pvchange.c:164
+#, c-format
+msgid "Setting physical volume \"%s\" NOT allocatable"
+msgstr ""
+
+#: pvchange.c:172
+#, c-format
+msgid "Failed to add tag %s to physical volume %s"
+msgstr ""
+
+#: pvchange.c:178
+#, c-format
+msgid "Failed to remove tag %s from physical volume%s"
+msgstr ""
+
+#: pvchange.c:186
+#, c-format
+msgid "Failed to generate new random UUID for %s."
+msgstr ""
+
+#: pvchange.c:194
+#, c-format
+msgid "Changing uuid of %s to %s."
+msgstr ""
+
+#: pvchange.c:201
+#, c-format
+msgid "pv_write with new uuid failed for %s."
+msgstr ""
+
+#: pvchange.c:210 pvresize.c:174
+#, c-format
+msgid "Updating physical volume \"%s\""
+msgstr ""
+
+#: pvchange.c:214 pvresize.c:178
+#, c-format
+msgid "Failed to store physical volume \"%s\" in volume group \"%s\""
+msgstr ""
+
+#: pvchange.c:223 pvresize.c:187
+#, c-format
+msgid "Failed to store physical volume \"%s\""
+msgstr ""
+
+#: pvchange.c:230 pvresize.c:194
+#, c-format
+msgid "Physical volume \"%s\" changed"
+msgstr ""
+
+#: pvchange.c:252
+msgid "Please give exactly one option of -x, -uuid, --addtag or --deltag"
+msgstr ""
+
+#: pvchange.c:258
+msgid "Please give a physical volume path"
+msgstr ""
+
+#: pvchange.c:263
+msgid "Option a and PhysicalVolumePath are exclusive"
+msgstr ""
+
+#: pvchange.c:268 toollib.c:683
+msgid "Using physical volume(s) on command line"
+msgstr ""
+
+#: pvchange.c:273
+#, c-format
+msgid "Failed to read physical volume %s"
+msgstr ""
+
+#: pvchange.c:281 toollib.c:766
+msgid "Scanning for physical volume names"
+msgstr ""
+
+#: pvchange.c:292
+#, c-format
+msgid "%d physical volume%s changed / %d physical volume%s not changed"
+msgstr ""
+
+#: pvck.c:32
+#, c-format
+msgid "Scanning %s"
+msgstr ""
+
+#: pvcreate.c:37 pvremove.c:31
+#, c-format
+msgid "%s: Not LVM partition type: use -f to override"
+msgstr ""
+
+#: pvcreate.c:49
+#, c-format
+msgid ""
+"Can't initialize physical volume \"%s\" of volume group \"%s\" without -ff"
+msgstr ""
+
+#: pvcreate.c:57
+#, c-format
+msgid "%s: physical volume not initialized"
+msgstr ""
+
+#: pvcreate.c:72 pvcreate.c:168 pvremove.c:81 vgcreate.c:135 vgextend.c:40
+#: vgremove.c:96
+msgid "Can't get lock for orphan PVs"
+msgstr ""
+
+#: pvcreate.c:86
+#, c-format
+msgid "Can't open %s exclusively. Mounted filesystem?"
+msgstr ""
+
+#: pvcreate.c:98
+#, c-format
+msgid "Wiping software RAID md superblock on %s"
+msgstr ""
+
+#: pvcreate.c:100
+#, c-format
+msgid "Failed to wipe RAID md superblock on %s"
+msgstr ""
+
+#: pvcreate.c:107
+#, c-format
+msgid "WARNING: Forcing physical volume creation on %s%s%s%s"
+msgstr ""
+
+#: pvcreate.c:140
+#, c-format
+msgid "uuid %s already in use on \"%s\""
+msgstr ""
+
+#: pvcreate.c:152
+#, c-format
+msgid "Unable to read volume group from %s"
+msgstr ""
+
+#: pvcreate.c:158
+#, c-format
+msgid "Can't find uuid %s in backup file %s"
+msgstr ""
+
+#: pvcreate.c:176 pvresize.c:212
+msgid "Physical volume size may not be negative"
+msgstr ""
+
+#: pvcreate.c:182 vgconvert.c:66
+msgid "Metadata size may not be negative"
+msgstr ""
+
+#: pvcreate.c:199 pvremove.c:89
+#, c-format
+msgid "%s: Couldn't find device. Check your filters?"
+msgstr ""
+
+#: pvcreate.c:208 vgconvert.c:127
+#, c-format
+msgid "Failed to setup physical volume \"%s\""
+msgstr ""
+
+#: pvcreate.c:212 vgconvert.c:138
+#, c-format
+msgid "Set up physical volume for \"%s\" with %lu available sectors"
+msgstr ""
+
+#: pvcreate.c:217 vgconvert.c:143
+#, c-format
+msgid "Failed to wipe existing label on %s"
+msgstr ""
+
+#: pvcreate.c:222
+#, c-format
+msgid "Zeroing start of device %s"
+msgstr ""
+
+#: pvcreate.c:224
+#, c-format
+msgid "%s not opened: device not zeroed"
+msgstr ""
+
+#: pvcreate.c:229
+#, c-format
+msgid "%s not wiped: aborting"
+msgstr ""
+
+#: pvcreate.c:236 vgconvert.c:150
+#, c-format
+msgid "Writing physical volume data to disk \"%s\""
+msgstr ""
+
+#: pvcreate.c:240 vgconvert.c:155
+#, c-format
+msgid "Failed to write physical volume \"%s\""
+msgstr ""
+
+#: pvcreate.c:244 vgconvert.c:161
+#, c-format
+msgid "Physical volume \"%s\" successfully created"
+msgstr ""
+
+#: pvcreate.c:261 pvremove.c:123
+msgid "Please enter a physical volume path"
+msgstr ""
+
+#: pvcreate.c:266
+msgid "--uuid is required with --restorefile"
+msgstr ""
+
+#: pvcreate.c:271
+msgid "Can only set uuid on one volume at once"
+msgstr ""
+
+#: pvcreate.c:276 pvremove.c:128
+msgid "Option y can only be given with option f"
+msgstr ""
+
+#: pvcreate.c:281 vgconvert.c:205
+#, c-format
+msgid "labelsector must be less than %lu"
+msgstr ""
+
+#: pvcreate.c:289 vgconvert.c:213
+msgid "Metadata parameters only apply to text format"
+msgstr ""
+
+#: pvcreate.c:295 vgconvert.c:219
+msgid "Metadatacopies may only be 0, 1 or 2"
+msgstr ""
+
+#: pvdisplay.c:30 reporter.c:65 reporter.c:113 toollib.c:347 toollib.c:477
+#, c-format
+msgid "Can't lock %s: skipping"
+msgstr ""
+
+#: pvdisplay.c:35 reporter.c:70 reporter.c:118
+#, c-format
+msgid "Can't read %s: skipping"
+msgstr ""
+
+#: pvdisplay.c:54
+#, c-format
+msgid "Device \"%s\" has a capacity of %s"
+msgstr ""
+
+#: pvdisplay.c:60
+#, c-format
+msgid "Physical volume \"%s\" of volume group \"%s\" is exported"
+msgstr ""
+
+#: pvdisplay.c:64
+#, c-format
+msgid "\"%s\" is a new physical volume of \"%s\""
+msgstr ""
+
+#: pvdisplay.c:104
+msgid "Option -v not allowed with option -c"
+msgstr ""
+
+#: pvmove.c:34
+msgid "--name takes a logical volume name"
+msgstr ""
+
+#: pvmove.c:39
+msgid "Named LV and old PV must be in the same VG"
+msgstr ""
+
+#: pvmove.c:45
+msgid "Incomplete LV name supplied with --name"
+msgstr ""
+
+#: pvmove.c:127
+msgid "No extents available for allocation"
+msgstr ""
+
+#: pvmove.c:150
+msgid "Creation of temporary pvmove LV failed"
+msgstr ""
+
+#: pvmove.c:157
+msgid "lvs_changed list struct allocation failed"
+msgstr ""
+
+#: pvmove.c:170
+#, c-format
+msgid "Skipping snapshot-related LV %s"
+msgstr ""
+
+#: pvmove.c:174
+#, c-format
+msgid "Skipping mirror LV %s"
+msgstr ""
+
+#: pvmove.c:178
+#, c-format
+msgid "Skipping mirror log LV %s"
+msgstr ""
+
+#: pvmove.c:182
+#, c-format
+msgid "Skipping mirror image LV %s"
+msgstr ""
+
+#: pvmove.c:186
+#, c-format
+msgid "Skipping locked LV %s"
+msgstr ""
+
+#: pvmove.c:199
+#, c-format
+msgid "No data to move for %s"
+msgstr ""
+
+#: pvmove.c:210
+msgid "Updating volume group metadata"
+msgstr ""
+
+#: pvmove.c:212 pvmove.c:236
+msgid "ABORTING: Volume group metadata update failed."
+msgstr ""
+
+#: pvmove.c:249
+msgid "ABORTING: Temporary mirror activation failed. Run pvmove --abort."
+msgstr ""
+
+#: pvmove.c:257 pvmove.c:438
+#, c-format
+msgid "Unable to reactivate logical volume \"%s\""
+msgstr ""
+
+#: pvmove.c:265
+msgid "Unable to resume logical volumes"
+msgstr ""
+
+#: pvmove.c:313
+#, c-format
+msgid "Detected pvmove in progress for %s"
+msgstr ""
+
+#: pvmove.c:315
+msgid "Ignoring remaining command line arguments"
+msgstr ""
+
+#: pvmove.c:318
+msgid "ABORTING: Failed to generate list of moving LVs"
+msgstr ""
+
+#: pvmove.c:326
+msgid "ABORTING: Temporary mirror activation failed."
+msgstr ""
+
+#: pvmove.c:403
+msgid "ABORTING: Removal of temporary mirror failed"
+msgstr ""
+
+#: pvmove.c:409 pvmove.c:428 pvmove.c:462
+msgid "ABORTING: Failed to write new data locations to disk."
+msgstr ""
+
+#: pvmove.c:416
+msgid "Locking LVs to remove temporary mirror failed"
+msgstr ""
+
+#: pvmove.c:422
+msgid "Suspension of temporary mirror LV failed"
+msgstr ""
+
+#: pvmove.c:448
+#, c-format
+msgid "ABORTING: Unable to deactivate temporary logical volume \"%s\""
+msgstr ""
+
+#: pvmove.c:453
+msgid "Removing temporary pvmove LV"
+msgstr ""
+
+#: pvmove.c:455
+msgid "ABORTING: Removal of temporary pvmove LV failed"
+msgstr ""
+
+#: pvmove.c:460
+msgid "Writing out final volume group after pvmove"
+msgstr ""
+
+#: pvmove.c:480
+#, c-format
+msgid "ABORTING: Can't reread PV %s"
+msgstr ""
+
+#: pvmove.c:516 toollib.c:1074
+msgid "Failed to clone PV name"
+msgstr ""
+
+#: pvremove.c:41 vgsplit.c:107
+#, c-format
+msgid "Physical Volume %s not found"
+msgstr ""
+
+#: pvremove.c:52
+#, c-format
+msgid ""
+"Can't pvremove physical volume \"%s\" of volume group \"%s\" without -ff"
+msgstr ""
+
+#: pvremove.c:60
+#, c-format
+msgid "%s: physical volume label not removed"
+msgstr ""
+
+#: pvremove.c:65
+#, c-format
+msgid "WARNING: Wiping physical volume label from %s%s%s%s"
+msgstr ""
+
+#: pvremove.c:95
+#, c-format
+msgid "Can't open %s exclusively - not removing. Mounted filesystem?"
+msgstr ""
+
+#: pvremove.c:102
+#, c-format
+msgid "Failed to wipe existing label(s) on %s"
+msgstr ""
+
+#: pvremove.c:106
+#, c-format
+msgid "Labels on physical volume \"%s\" successfully wiped"
+msgstr ""
+
+#: pvresize.c:60
+#, c-format
+msgid "%s: too many metadata areas for pvresize"
+msgstr ""
+
+#: pvresize.c:113
+#, c-format
+msgid "Physical volume %s format does not support resizing."
+msgstr ""
+
+#: pvresize.c:130
+#, c-format
+msgid "%s: Pretending size is %lu not %lu sectors."
+msgstr ""
+
+#: pvresize.c:143
+#, c-format
+msgid "%s: Size must exceed physical extent start of %lu sectors."
+msgstr ""
+
+#: pvresize.c:156
+#, c-format
+msgid ""
+"%s: Size must leave space for at least one physical extent of %u sectors."
+msgstr ""
+
+#: pvresize.c:171
+#, c-format
+msgid "Resizing volume \"%s\" to %lu sectors."
+msgstr ""
+
+#: pvresize.c:207
+msgid "Please supply physical volume(s)"
+msgstr ""
+
+#: pvresize.c:224
+#, c-format
+msgid "%d physical volume(s) resized / %d physical volume(s) not resized"
+msgstr ""
+
+#: pvscan.c:66
+#, c-format
+msgid "PV %-*s %-*s %s [%s]"
+msgstr ""
+
+#: pvscan.c:76
+#, c-format
+msgid "PV %-*s is in exported VG %s [%s / %s free]"
+msgstr ""
+
+#: pvscan.c:89
+#, c-format
+msgid "PV %-*s VG %-*s %s [%s / %s free]"
+msgstr ""
+
+#: pvscan.c:117
+msgid "Options -e and -n are incompatible"
+msgstr ""
+
+#: pvscan.c:122
+#, c-format
+msgid "WARNING: only considering physical volumes %s"
+msgstr ""
+
+#: pvscan.c:129
+msgid "Walking through all physical volumes"
+msgstr ""
+
+#: pvscan.c:182
+msgid "No matching physical volumes found"
+msgstr ""
+
+#: pvscan.c:186
+#, c-format
+msgid "Total: %d [%s] / in use: %d [%s] / in no VG: %d [%s]"
+msgstr ""
+
+#: report/report.c:118
+msgid "Extent number dm_snprintf failed"
+msgstr ""
+
+#: report/report.c:182
+msgid "modules str_list allocation failed"
+msgstr ""
+
+#: report/report.c:259 report/report.c:342 report/report.c:368
+#: report/report.c:466 report/report.c:523 report/report.c:553
+#: report/report.c:694 report/report.c:750 report/report.c:768
+#: report/report.c:793 report/report.c:807
+msgid "dm_pool_alloc failed"
+msgstr ""
+
+#: report/report.c:471
+msgid "lvname snprintf failed"
+msgstr ""
+
+#: report/report.c:476 report/report.c:518 report/report.c:548
+msgid "dm_pool_strdup failed"
+msgstr ""
+
+#: report/report.c:773
+msgid "snapshot percentage too large"
+msgstr ""
+
+#: report/report.c:812
+msgid "copy percentage too large"
+msgstr ""
+
+#: reporter.c:24 reporter.c:146 reporter.c:158
+#, c-format
+msgid "Volume group %s not found"
+msgstr ""
+
+#: reporter.c:254
+#, c-format
+msgid "Invalid options string: %s"
+msgstr ""
+
+#: reporter.c:260
+msgid "options string allocation failed"
+msgstr ""
+
+#: reporter.c:297
+msgid "Can't report LV and PV fields at the same time"
+msgstr ""
+
+#: snapshot/snapshot.c:40
+msgid "Couldn't read chunk size for snapshot."
+msgstr ""
+
+#: snapshot/snapshot.c:48
+msgid "Snapshot cow storage not specified."
+msgstr ""
+
+#: snapshot/snapshot.c:54
+msgid "Snapshot origin not specified."
+msgstr ""
+
+#: snapshot/snapshot.c:61
+msgid "Unknown logical volume specified for snapshot cow store."
+msgstr ""
+
+#: snapshot/snapshot.c:67
+msgid "Unknown logical volume specified for snapshot origin."
+msgstr ""
+
+#: snapshot/snapshot.c:135
+msgid "snapshot string list allocation failed"
+msgstr ""
+
+#: striped/striped.c:41
+#, c-format
+msgid " Stripes\t\t%u"
+msgstr ""
+
+#: striped/striped.c:42
+#, c-format
+msgid " Stripe size\t\t%u KB"
+msgstr ""
+
+#: striped/striped.c:45
+#, c-format
+msgid " Stripe %d:"
+msgstr ""
+
+#: striped/striped.c:55
+#, c-format
+msgid "Couldn't read 'stripe_count' for segment '%s'."
+msgstr ""
+
+#: striped/striped.c:70
+#, c-format
+msgid "Couldn't read stripe_size for segment '%s'."
+msgstr ""
+
+#: striped/striped.c:76
+#, c-format
+msgid "Couldn't find stripes array for segment '%s'."
+msgstr ""
+
+#: striped/striped.c:163
+#, c-format
+msgid "Internal error: striped add_target_line called with no areas for %s."
+msgstr ""
+
+#: stub.h:24 stub.h:31
+msgid "Command not implemented yet."
+msgstr ""
+
+#: stub.h:38
+msgid "There's no 'pvdata' command in LVM2."
+msgstr ""
+
+#: stub.h:39
+msgid ""
+"Use lvs, pvs, vgs instead; or use vgcfgbackup and read the text file backup."
+msgstr ""
+
+#: stub.h:40
+msgid ""
+"Metadata in LVM1 format can still be displayed using LVM1's pvdata command."
+msgstr ""
+
+#: toollib.c:115
+#, c-format
+msgid "skip_dev_dir: Couldn't split up device name %s"
+msgstr ""
+
+#: toollib.c:124 toollib.c:322
+msgid "vg/lv string alloc failed"
+msgstr ""
+
+#: toollib.c:215
+msgid "One or more specified logical volume(s) not found."
+msgstr ""
+
+#: toollib.c:251
+msgid "Using logical volume(s) on command line"
+msgstr ""
+
+#: toollib.c:264 toollib.c:540 toollib.c:689 toollib.c:1051
+#, c-format
+msgid "Skipping invalid tag %s"
+msgstr ""
+
+#: toollib.c:281 toollib.c:807 toollib.c:818
+#, c-format
+msgid "\"%s\": Invalid path for Logical Volume"
+msgstr ""
+
+#: toollib.c:335
+msgid "Finding all logical volumes"
+msgstr ""
+
+#: toollib.c:337 toollib.c:572
+msgid "No volume groups found"
+msgstr ""
+
+#: toollib.c:357 toollib.c:483 toollib.c:731 vgcfgbackup.c:59 vgck.c:24
+#: vgreduce.c:505 vgscan.c:23
+#, c-format
+msgid "Volume group \"%s\" not found"
+msgstr ""
+
+#: toollib.c:369 vgchange.c:523 vgck.c:29 vgconvert.c:43 vgscan.c:30
+#, c-format
+msgid "Volume group \"%s\" inconsistent"
+msgstr ""
+
+#: toollib.c:534
+msgid "Using volume group(s) on command line"
+msgstr ""
+
+#: toollib.c:555
+#, c-format
+msgid "Invalid volume group name: %s"
+msgstr ""
+
+#: toollib.c:570
+msgid "Finding all volume groups"
+msgstr ""
+
+#: toollib.c:705 toollib.c:1080
+#, c-format
+msgid "Physical Volume \"%s\" not found in Volume Group \"%s\""
+msgstr ""
+
+#: toollib.c:716
+#, c-format
+msgid "Failed to read physical volume \"%s\""
+msgstr ""
+
+#: toollib.c:755
+msgid "Using all physical volume(s) in volume group"
+msgstr ""
+
+#: toollib.c:825
+msgid "Allocation of vg_name failed"
+msgstr ""
+
+#: toollib.c:835
+#, c-format
+msgid "Path required for Logical Volume \"%s\""
+msgstr ""
+
+#: toollib.c:858
+#, c-format
+msgid "Environment Volume Group in LVM_VG_NAME invalid: \"%s\""
+msgstr ""
+
+#: toollib.c:874
+#, c-format
+msgid "Adding PE range: start PE %u length %u on %s"
+msgstr ""
+
+#: toollib.c:882
+#, c-format
+msgid "Overlapping PE ranges specified (%u-%u, %u-%u) on %s"
+msgstr ""
+
+#: toollib.c:892 toollib.c:1039 toollib.c:1103
+msgid "Allocation of list failed"
+msgstr ""
+
+#: toollib.c:956
+#, c-format
+msgid "PE range error: start extent %u to end extent %u"
+msgstr ""
+
+#: toollib.c:971
+#, c-format
+msgid "Physical extent parsing error at %s"
+msgstr ""
+
+#: toollib.c:984
+#, c-format
+msgid "Physical volume %s not allocatable"
+msgstr ""
+
+#: toollib.c:990
+#, c-format
+msgid "No free extents on physical volume \"%s\""
+msgstr ""
+
+#: toollib.c:1002 toollib.c:1110
+msgid "Unable to allocate physical volume list."
+msgstr ""
+
+#: toollib.c:1009
+msgid "Allocation of pe_ranges list failed"
+msgstr ""
+
+#: toollib.c:1091
+msgid "No specified PVs have space available"
+msgstr ""
+
+#: toollib.c:1137
+#, c-format
+msgid "Can't lock %s for metadata recovery: skipping"
+msgstr ""
+
+#: toollib.c:1148
+msgid ""
+"Names starting \"snapshot\" are reserved. Please choose a different LV name."
+msgstr ""
+
+#: toollib.c:1154
+msgid ""
+"Names starting \"pvmove\" are reserved. Please choose a different LV name."
+msgstr ""
+
+#: toollib.c:1160
+msgid ""
+"Names including \"_mlog\" are reserved. Please choose a different LV name."
+msgstr ""
+
+#: toollib.c:1166
+msgid ""
+"Names including \"_mimage\" are reserved. Please choose a different LV name."
+msgstr ""
+
+#: toollib.c:1183
+#, c-format
+msgid "%s: already exists in filesystem"
+msgstr ""
+
+#: toollib.c:1227
+msgid "Name allocation failed - device not cleared"
+msgstr ""
+
+#: toollib.c:1233
+#, c-format
+msgid "Name too long - device not cleared (%s)"
+msgstr ""
+
+#: toollib.c:1237
+#, c-format
+msgid "Clearing start of logical volume \"%s\""
+msgstr ""
+
+#: toollib.c:1240
+#, c-format
+msgid "%s: not found: device not cleared"
+msgstr ""
+
+#: toollib.c:1276
+#, c-format
+msgid "Name allocation failed - log header not written (%s)"
+msgstr ""
+
+#: toollib.c:1283
+#, c-format
+msgid "Name too long - log header not written (%s)"
+msgstr ""
+
+#: toollib.c:1287
+#, c-format
+msgid "Writing log header to device, %s"
+msgstr ""
+
+#: toollib.c:1290
+#, c-format
+msgid "%s: not found: log header not written"
+msgstr ""
+
+#: toollib.c:1298
+#, c-format
+msgid "Failed to write log header to %s"
+msgstr ""
+
+#: toollib.c:1324
+msgid "log_name allocation failed. Remove new LV and retry."
+msgstr ""
+
+#: toollib.c:1344
+msgid "Aborting. Unable to tag mirror log."
+msgstr ""
+
+#: toollib.c:1362
+msgid ""
+"Aborting. Unable to create in-sync mirror log while activation is disabled."
+msgstr ""
+
+#: toollib.c:1368
+msgid "Aborting. Failed to activate mirror log. Remove new LVs and retry."
+msgstr ""
+
+#: toollib.c:1375
+#, c-format
+msgid "Failed to remove tag %s from mirror log."
+msgstr ""
+
+#: toollib.c:1380
+msgid "Aborting. Failed to wipe mirror log. Remove new LV and retry."
+msgstr ""
+
+#: toollib.c:1386
+msgid "Aborting. Failed to write mirror log header. Remove new LV and retry."
+msgstr ""
+
+#: toollib.c:1392
+msgid "Aborting. Failed to deactivate mirror log. Remove new LV and retry."
+msgstr ""
+
+#: uuid/uuid.c:132
+msgid "UUID contains invalid character"
+msgstr ""
+
+#: uuid/uuid.c:156
+msgid "Couldn't write uuid, buffer too small."
+msgstr ""
+
+#: uuid/uuid.c:184
+msgid "Too many characters to be uuid."
+msgstr ""
+
+#: uuid/uuid.c:192
+msgid "Couldn't read uuid, incorrect number of characters."
+msgstr ""
+
+#: vgcfgbackup.c:27
+msgid "Failed to allocate filename."
+msgstr ""
+
+#: vgcfgbackup.c:32
+#, c-format
+msgid "Error processing filename template %s"
+msgstr ""
+
+#: vgcfgbackup.c:39
+#, c-format
+msgid ""
+"VGs must be backed up into different files. Use %%s in filename for VG name."
+msgstr ""
+
+#: vgcfgbackup.c:64
+#, c-format
+msgid "Warning: Volume group \"%s\" inconsistent"
+msgstr ""
+
+#: vgcfgbackup.c:76
+msgid "No backup taken: specify filename with -f to backup an inconsistent VG"
+msgstr ""
+
+#: vgcfgbackup.c:90
+#, c-format
+msgid "Volume group \"%s\" successfully backed up."
+msgstr ""
+
+#: vgcfgrestore.c:23
+msgid "Please specify a *single* volume group to restore."
+msgstr ""
+
+#: vgcfgrestore.c:30 vgextend.c:45 vgreduce.c:469 vgsplit.c:228
+#, c-format
+msgid "Volume group name \"%s\" is invalid"
+msgstr ""
+
+#: vgcfgrestore.c:46
+msgid "Unable to lock orphans"
+msgstr ""
+
+#: vgcfgrestore.c:51
+#, c-format
+msgid "Unable to lock volume group %s"
+msgstr ""
+
+#: vgcfgrestore.c:62
+msgid "Restore failed."
+msgstr ""
+
+#: vgcfgrestore.c:66
+#, c-format
+msgid "Restored volume group %s"
+msgstr ""
+
+#: vgchange.c:92
+#, c-format
+msgid "Spawning background process for %s %s"
+msgstr ""
+
+#: vgchange.c:111
+#, c-format
+msgid "%d logical volume(s) in volume group \"%s\" %smonitored"
+msgstr ""
+
+#: vgchange.c:132
+#, c-format
+msgid "Can't deactivate volume group \"%s\" with %d open logical volume(s)"
+msgstr ""
+
+#: vgchange.c:138
+#, c-format
+msgid "Locking inactive: ignoring clustered volume group %s"
+msgstr ""
+
+#: vgchange.c:148
+#, c-format
+msgid "%d logical volume(s) in volume group \"%s\" already active"
+msgstr ""
+
+#: vgchange.c:152
+#, c-format
+msgid "%d existing logical volume(s) in volume group \"%s\" %smonitored"
+msgstr ""
+
+#: vgchange.c:160
+#, c-format
+msgid "Activated logical volumes in volume group \"%s\""
+msgstr ""
+
+#: vgchange.c:164
+#, c-format
+msgid "Deactivated logical volumes in volume group \"%s\""
+msgstr ""
+
+#: vgchange.c:167
+#, c-format
+msgid "%d logical volume(s) in volume group \"%s\" now active"
+msgstr ""
+
+#: vgchange.c:179 vgcreate.c:47
+msgid "Volume Group allocation policy cannot inherit from anything"
+msgstr ""
+
+#: vgchange.c:185
+#, c-format
+msgid "Volume group allocation policy is already %s"
+msgstr ""
+
+#: vgchange.c:200 vgchange.c:235 vgchange.c:282 vgchange.c:324 vgchange.c:371
+#: vgchange.c:429 vgchange.c:471 vgchange.c:504
+#, c-format
+msgid "Volume group \"%s\" successfully changed"
+msgstr ""
+
+#: vgchange.c:211
+#, c-format
+msgid "Volume group \"%s\" is already resizeable"
+msgstr ""
+
+#: vgchange.c:217
+#, c-format
+msgid "Volume group \"%s\" is already not resizeable"
+msgstr ""
+
+#: vgchange.c:247
+#, c-format
+msgid "Volume group \"%s\" is already clustered"
+msgstr ""
+
+#: vgchange.c:253
+#, c-format
+msgid "Volume group \"%s\" is already not clustered"
+msgstr ""
+
+#: vgchange.c:261
+#, c-format
+msgid "Volume group %s contains snapshots that are not yet supported."
+msgstr ""
+
+#: vgchange.c:293
+#, c-format
+msgid "Volume group \"%s\" must be resizeable to change MaxLogicalVolume"
+msgstr ""
+
+#: vgchange.c:302
+msgid "MaxLogicalVolume limit is 255"
+msgstr ""
+
+#: vgchange.c:308
+#, c-format
+msgid "MaxLogicalVolume is less than the current number %d of LVs for \"%s\""
+msgstr ""
+
+#: vgchange.c:335
+#, c-format
+msgid "Volume group \"%s\" must be resizeable to change MaxPhysicalVolumes"
+msgstr ""
+
+#: vgchange.c:341
+msgid "MaxPhysicalVolumes may not be negative"
+msgstr ""
+
+#: vgchange.c:349
+msgid "MaxPhysicalVolume limit is 255"
+msgstr ""
+
+#: vgchange.c:355
+#, c-format
+msgid "MaxPhysicalVolumes is less than the current number %d of PVs for \"%s\""
+msgstr ""
+
+#: vgchange.c:381
+#, c-format
+msgid "Volume group \"%s\" must be resizeable to change PE size"
+msgstr ""
+
+#: vgchange.c:387 vgcreate.c:64
+msgid "Physical extent size may not be negative"
+msgstr ""
+
+#: vgchange.c:393 vgcreate.c:83
+msgid "Physical extent size may not be zero"
+msgstr ""
+
+#: vgchange.c:398
+#, c-format
+msgid "Physical extent size of VG %s is already %s"
+msgstr ""
+
+#: vgchange.c:404
+msgid "Physical extent size must be a power of 2."
+msgstr ""
+
+#: vgchange.c:411
+msgid "New extent size is not a perfect fit"
+msgstr ""
+
+#: vgchange.c:454 vgcreate.c:117
+#, c-format
+msgid "Failed to add tag %s to volume group %s"
+msgstr ""
+
+#: vgchange.c:460
+#, c-format
+msgid "Failed to remove tag %s from volume group %s"
+msgstr ""
+
+#: vgchange.c:482
+msgid "Volume group has active logical volumes"
+msgstr ""
+
+#: vgchange.c:490
+#, c-format
+msgid "Failed to generate new random UUID for VG %s."
+msgstr ""
+
+#: vgchange.c:516 vgconvert.c:36 vgexport.c:27
+#, c-format
+msgid "Unable to find volume group \"%s\""
+msgstr ""
+
+#: vgchange.c:588
+msgid ""
+"One of -a, -c, -l, -p, -s, -x, --uuid, --alloc, --addtag or --deltag required"
+msgstr ""
+
+#: vgchange.c:600
+msgid ""
+"Only one of -a, -c, -l, -p, -s, -x, --uuid, --alloc, --addtag or --deltag "
+"allowed"
+msgstr ""
+
+#: vgchange.c:607
+msgid "--ignorelockingfailure only available with -a"
+msgstr ""
+
+#: vgchange.c:613
+msgid "-A option not necessary with -a option"
+msgstr ""
+
+#: vgconvert.c:59
+#, c-format
+msgid "Volume group \"%s\" already uses format %s"
+msgstr ""
+
+#: vgconvert.c:87
+#, c-format
+msgid "Archive of \"%s\" metadata failed."
+msgstr ""
+
+#: vgconvert.c:100
+#, c-format
+msgid "Logical volume %s must be deactivated before conversion."
+msgstr ""
+
+#: vgconvert.c:130 vgconvert.c:145 vgconvert.c:157 vgconvert.c:170
+#: vgconvert.c:186
+msgid "Use pvcreate and vgcfgrestore to repair from archived metadata."
+msgstr ""
+
+#: vgconvert.c:166
+#, c-format
+msgid "Deleting existing metadata for VG %s"
+msgstr ""
+
+#: vgconvert.c:168
+#, c-format
+msgid "Removal of existing metadata for %s failed."
+msgstr ""
+
+#: vgconvert.c:177
+#, c-format
+msgid "Test mode: Skipping metadata writing for VG %s in format %s"
+msgstr ""
+
+#: vgconvert.c:182
+#, c-format
+msgid "Writing metadata for VG %s using format %s"
+msgstr ""
+
+#: vgconvert.c:185
+#, c-format
+msgid "Conversion failed for volume group %s."
+msgstr ""
+
+#: vgconvert.c:190
+#, c-format
+msgid "Volume group %s successfully converted"
+msgstr ""
+
+#: vgconvert.c:200
+msgid "Please enter volume group(s)"
+msgstr ""
+
+#: vgcreate.c:31
+msgid "Please provide volume group name and physical volumes"
+msgstr ""
+
+#: vgcreate.c:37
+msgid "Please enter physical volume name(s)"
+msgstr ""
+
+#: vgcreate.c:58
+msgid "Number of volumes may not exceed 255"
+msgstr ""
+
+#: vgcreate.c:69
+msgid "Max Logical Volumes may not be negative"
+msgstr ""
+
+#: vgcreate.c:74
+msgid "Max Physical Volumes may not be negative"
+msgstr ""
+
+#: vgcreate.c:88 vgrename.c:52 vgsplit.c:290
+#, c-format
+msgid "New volume group name \"%s\" is invalid"
+msgstr ""
+
+#: vgcreate.c:98
+#, c-format
+msgid "Warning: Setting maxlogicalvolumes to %d (0 means unlimited)"
+msgstr ""
+
+#: vgcreate.c:102
+#, c-format
+msgid "Warning: Setting maxphysicalvolumes to %d (0 means unlimited)"
+msgstr ""
+
+#: vgcreate.c:112
+msgid "Volume group format does not support tags"
+msgstr ""
+
+#: vgcreate.c:163
+#, c-format
+msgid "Volume group \"%s\" successfully created"
+msgstr ""
+
+#: vgdisplay.c:29
+#, c-format
+msgid "WARNING: Volume group \"%s\" inconsistent"
+msgstr ""
+
+#: vgdisplay.c:32
+#, c-format
+msgid "WARNING: volume group \"%s\" is exported"
+msgstr ""
+
+#: vgdisplay.c:52
+msgid "--- Physical volumes ---"
+msgstr ""
+
+#: vgdisplay.c:81
+msgid "Option -c is not allowed with option -s"
+msgstr ""
+
+#: vgdisplay.c:86
+msgid "Option -A is not allowed with volume group names"
+msgstr ""
+
+#: vgexport.c:32
+#, c-format
+msgid "Volume group %s inconsistent"
+msgstr ""
+
+#: vgexport.c:37
+#, c-format
+msgid "Volume group \"%s\" is already exported"
+msgstr ""
+
+#: vgexport.c:47
+#, c-format
+msgid "Volume group \"%s\" has active logical volumes"
+msgstr ""
+
+#: vgexport.c:67
+#, c-format
+msgid "Volume group \"%s\" successfully exported"
+msgstr ""
+
+#: vgexport.c:78 vgimport.c:68
+msgid "Please supply volume groups or use -a for all."
+msgstr ""
+
+#: vgexport.c:83 vgimport.c:73
+msgid "No arguments permitted when using -a for all."
+msgstr ""
+
+#: vgextend.c:25
+msgid "Please enter volume group name and physical volume(s)"
+msgstr ""
+
+#: vgextend.c:31
+msgid "Please enter physical volume(s)"
+msgstr ""
+
+#: vgextend.c:50 vgmerge.c:32 vgmerge.c:63 vgsplit.c:238 vgsplit.c:275
+#, c-format
+msgid "Checking for volume group \"%s\""
+msgstr ""
+
+#: vgextend.c:58
+#, c-format
+msgid "Volume group \"%s\" not found."
+msgstr ""
+
+#: vgextend.c:79
+#, c-format
+msgid "Volume group \"%s\" is not resizeable."
+msgstr ""
+
+#: vgextend.c:98
+#, c-format
+msgid "Volume group \"%s\" will be extended by %d new physical volumes"
+msgstr ""
+
+#: vgextend.c:110
+#, c-format
+msgid "Volume group \"%s\" successfully extended"
+msgstr ""
+
+#: vgimport.c:27
+#, c-format
+msgid "Unable to find exported volume group \"%s\""
+msgstr ""
+
+#: vgimport.c:33
+#, c-format
+msgid "Volume group \"%s\" is not exported"
+msgstr ""
+
+#: vgimport.c:38
+#, c-format
+msgid "Volume group \"%s\" is partially missing"
+msgstr ""
+
+#: vgimport.c:57
+#, c-format
+msgid "Volume group \"%s\" successfully imported"
+msgstr ""
+
+#: vgmerge.c:28 vgsplit.c:234
+#, c-format
+msgid "Duplicate volume group name \"%s\""
+msgstr ""
+
+#: vgmerge.c:93 vgsplit.c:297
+#, c-format
+msgid "Logical volumes in \"%s\" must be inactive"
+msgstr ""
+
+#: vgmerge.c:100
+#, c-format
+msgid "Extent sizes differ: %d (%s) and %d (%s)"
+msgstr ""
+
+#: vgmerge.c:108
+#, c-format
+msgid "Maximum number of physical volumes (%d) exceeded for \"%s\" and \"%s\""
+msgstr ""
+
+#: vgmerge.c:116
+#, c-format
+msgid "Maximum number of logical volumes (%d) exceeded for \"%s\" and \"%s\""
+msgstr ""
+
+#: vgmerge.c:130
+#, c-format
+msgid "Duplicate logical volume name \"%s\" in \"%s\" and \"%s\""
+msgstr ""
+
+#: vgmerge.c:142 vgmerge.c:151
+#, c-format
+msgid "Physical volume %s might be constructed from same volume group %s."
+msgstr ""
+
+#: vgmerge.c:186
+#, c-format
+msgid "Failed to generate new random LVID for %s"
+msgstr ""
+
+#: vgmerge.c:197
+#, c-format
+msgid "Changed LVID for %s to %s"
+msgstr ""
+
+#: vgmerge.c:235
+#, c-format
+msgid "Volume group \"%s\" successfully merged into \"%s\""
+msgstr ""
+
+#: vgmerge.c:252
+msgid "Please enter 2 or more volume groups to merge"
+msgstr ""
+
+#: vgreduce.c:24
+msgid "Volume Groups must always contain at least one PV"
+msgstr ""
+
+#: vgreduce.c:33
+#, c-format
+msgid "Removing PV with UUID %s from VG %s"
+msgstr ""
+
+#: vgreduce.c:36
+#, c-format
+msgid "LVs still present on PV with UUID %s: Can't remove from VG %s"
+msgstr ""
+
+#: vgreduce.c:61
+#, c-format
+msgid "%s/%s has missing extents: removing (including dependencies)"
+msgstr ""
+
+#: vgreduce.c:68
+#, c-format
+msgid "Deactivating (if active) logical volume %s (origin of %s)"
+msgstr ""
+
+#: vgreduce.c:72 vgreduce.c:89 vgreduce.c:333
+#, c-format
+msgid "Failed to deactivate LV %s"
+msgstr ""
+
+#: vgreduce.c:99 vgreduce.c:146 vgreduce.c:348
+#, c-format
+msgid "Removing LV %s from VG %s"
+msgstr ""
+
+#: vgreduce.c:191
+#, c-format
+msgid "Non-mirror-image LV %s found: can't remove."
+msgstr ""
+
+#: vgreduce.c:207
+msgid "Aborting because --mirrorsonly was specified."
+msgstr ""
+
+#: vgreduce.c:232 vgreduce.c:529
+#, c-format
+msgid "Failed to write out a consistent VG for %s"
+msgstr ""
+
+#: vgreduce.c:250
+#, c-format
+msgid "Failed to commit consistent VG for %s"
+msgstr ""
+
+#: vgreduce.c:258
+msgid "Failed to resume LVs using error segments."
+msgstr ""
+
+#: vgreduce.c:290
+#, c-format
+msgid "The log device for %s/%s has failed."
+msgstr ""
+
+#: vgreduce.c:296
+#, c-format
+msgid "Log device for %s/%s has failed."
+msgstr ""
+
+#: vgreduce.c:312
+#, c-format
+msgid "Failed to write out updated VG for %s"
+msgstr ""
+
+#: vgreduce.c:318
+#, c-format
+msgid "Failed to commit updated VG for %s"
+msgstr ""
+
+#: vgreduce.c:329
+#, c-format
+msgid "Deactivating (if active) logical volume %s"
+msgstr ""
+
+#: vgreduce.c:371
+#, c-format
+msgid "Physical volume \"%s\" still in use"
+msgstr ""
+
+#: vgreduce.c:376
+#, c-format
+msgid "Can't remove final physical volume \"%s\" from volume group \"%s\""
+msgstr ""
+
+#: vgreduce.c:386
+#, c-format
+msgid "Removing \"%s\" from volume group \"%s\""
+msgstr ""
+
+#: vgreduce.c:404
+#, c-format
+msgid "Removal of physical volume \"%s\" from \"%s\" failed"
+msgstr ""
+
+#: vgreduce.c:418
+#, c-format
+msgid "Removed \"%s\" from volume group \"%s\""
+msgstr ""
+
+#: vgreduce.c:431
+msgid "Please give volume group name and physical volume paths"
+msgstr ""
+
+#: vgreduce.c:437
+msgid "Please give volume group name"
+msgstr ""
+
+#: vgreduce.c:443
+msgid "--mirrorsonly requires --removemissing"
+msgstr ""
+
+#: vgreduce.c:449
+msgid "Please enter physical volume paths or option -a"
+msgstr ""
+
+#: vgreduce.c:454
+msgid "Option -a and physical volume paths mutually exclusive"
+msgstr ""
+
+#: vgreduce.c:460
+msgid "Please only specify the volume group"
+msgstr ""
+
+#: vgreduce.c:496
+#, c-format
+msgid "Volume group \"%s\" is already consistent"
+msgstr ""
+
+#: vgreduce.c:537
+#, c-format
+msgid "Wrote out consistent volume group %s"
+msgstr ""
+
+#: vgreduce.c:553
+#, c-format
+msgid "Volume group \"%s\" is not reducible"
+msgstr ""
+
+#: vgremove.c:27
+#, c-format
+msgid "Volume group \"%s\" not found or inconsistent."
+msgstr ""
+
+#: vgremove.c:29
+msgid "Consider vgreduce --removemissing if metadata is inconsistent."
+msgstr ""
+
+#: vgremove.c:40
+#, c-format
+msgid "Volume group \"%s\" still contains %d logical volume(s)"
+msgstr ""
+
+#: vgremove.c:49
+#, c-format
+msgid "vg_remove %s failed"
+msgstr ""
+
+#: vgremove.c:56
+#, c-format
+msgid "Removing physical volume \"%s\" from volume group \"%s\""
+msgstr ""
+
+#: vgremove.c:69
+#, c-format
+msgid "Failed to remove physical volume \"%s\" from volume group \"%s\""
+msgstr ""
+
+#: vgremove.c:79
+#, c-format
+msgid "Volume group \"%s\" successfully removed"
+msgstr ""
+
+#: vgremove.c:81
+#, c-format
+msgid "Volume group \"%s\" not properly removed"
+msgstr ""
+
+#: vgremove.c:91
+msgid "Please enter one or more volume group paths"
+msgstr ""
+
+#: vgrename.c:34
+msgid "Old and new volume group names need specifying"
+msgstr ""
+
+#: vgrename.c:46
+#, c-format
+msgid "New volume group path exceeds maximum length of %d!"
+msgstr ""
+
+#: vgrename.c:58
+msgid "Old and new volume group names must differ"
+msgstr ""
+
+#: vgrename.c:66
+msgid "No complete volume groups found"
+msgstr ""
+
+#: vgrename.c:76
+#, c-format
+msgid "Found more than one VG called %s. Please supply VG uuid."
+msgstr ""
+
+#: vgrename.c:99
+#, c-format
+msgid "Volume group %s %s%s%snot found."
+msgstr ""
+
+#: vgrename.c:123
+#, c-format
+msgid "Volume group \"%s\" still has active LVs"
+msgstr ""
+
+#: vgrename.c:129
+#, c-format
+msgid "Checking for new volume group \"%s\""
+msgstr ""
+
+#: vgrename.c:139
+#, c-format
+msgid "New volume group \"%s\" already exists"
+msgstr ""
+
+#: vgrename.c:154
+#, c-format
+msgid "Renaming \"%s\" to \"%s\""
+msgstr ""
+
+#: vgrename.c:156
+msgid "Test mode: Skipping rename."
+msgstr ""
+
+#: vgrename.c:158
+#, c-format
+msgid "Renaming \"%s\" to \"%s\" failed: %s"
+msgstr ""
+
+#: vgrename.c:177
+#, c-format
+msgid "Volume group \"%s\" successfully renamed to \"%s\""
+msgstr ""
+
+#: vgscan.c:36
+#, c-format
+msgid "Found %svolume group \"%s\" using metadata type %s"
+msgstr ""
+
+#: vgscan.c:50
+msgid "Too many parameters on command line"
+msgstr ""
+
+#: vgscan.c:57
+msgid "Reading all physical volumes. This may take a while..."
+msgstr ""
+
+#: vgsplit.c:25
+#, c-format
+msgid "Physical volume %s not in volume group %s"
+msgstr ""
+
+#: vgsplit.c:90
+#, c-format
+msgid "Can't split Logical Volume %s between two Volume Groups"
+msgstr ""
+
+#: vgsplit.c:152
+#, c-format
+msgid "Snapshot %s split"
+msgstr ""
+
+#: vgsplit.c:193
+#, c-format
+msgid "Mirror %s split"
+msgstr ""
+
+#: vgsplit.c:218
+msgid "Existing VG, new VG and physical volumes required."
+msgstr ""
+
+#: vgsplit.c:264
+#, c-format
+msgid "Volume group \"%s\" is not resizeable"
+msgstr ""
+
+#: vgsplit.c:285
+#, c-format
+msgid "Volume group \"%s\" already exists"
+msgstr ""
+
+#: vgsplit.c:339
+msgid "Cannot split: Nowhere to store metadata for new Volume Group"
+msgstr ""
+
+#: vgsplit.c:348
+msgid "Writing out updated volume groups"
+msgstr ""
+
+#: vgsplit.c:370
+#, c-format
+msgid "Volume group \"%s\" became inconsistent: please fix manually"
+msgstr ""
+
+#: vgsplit.c:385
+#, c-format
+msgid "Volume group \"%s\" successfully split from \"%s\""
+msgstr ""
+
+#: zero/zero.c:71
+msgid "zero module string list allocation failed"
+msgstr ""
--- /dev/null
+/*
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Macros to change log messages into a format that xgettext can handle.
+ *
+ * Note that different PRI* definitions lead to different strings for
+ * different architectures.
+ */
+
+#define print_log(level, dm_errno, file, line, format, args...) print_log(format, args)
+#define dm_log(level, file, line, format, args...) dm_log(format, args)
+#define dm_log_with_errno(level, dm_errno, file, line, format, args...) \
+ dm_log(level, file, line, format, args)
+
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Merely wraps the logger library with a bit of standard policy.
+require 'logger'
+
+module Log
+ $log = Logger.new(STDERR)
+
+ def init(io_)
+ $log = Logger.new(io_)
+ end
+end
+
+def fatal(*args)
+ $log.fatal(*args)
+end
+
+def error(*args)
+ $log.error(*args)
+end
+
+def info(*args)
+ $log.info(*args)
+end
+
+def warning(*args)
+ $log.warn(*args)
+end
+
+def debug(*args)
+ $log.debug(*args)
+end
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Policy for the location of report templates
+require 'string-store'
+
+class TemplateStringStore < StringStore
+ def initialize()
+ super(['report-generators/templates'])
+ end
+end
+
+module ReportTemplates
+ def generate_report(report, bs, dest_path = nil)
+ include Reports
+ reports = ReportRegister.new
+ template_store = TemplateStringStore.new
+ report = reports.get_report(report)
+ erb = ERB.new(template_store.lookup(report.template))
+ body = erb.result(bs)
+ title = report.short_desc
+
+ erb = ERB.new(template_store.lookup("boiler_plate.rhtml"))
+ txt = erb.result(binding)
+
+ dest_path = dest_path.nil? ? report.path : dest_path
+ dest_path.open("w") do |out|
+ out.puts txt
+ end
+ end
+end
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Data about the various reports we support
+require 'log'
+require 'pathname'
+
+module Reports
+ Report = Struct.new(:short_desc, :desc, :path, :template)
+
+ class ReportRegister
+ attr_reader :reports
+
+ private
+ def add_report(sym, *args)
+ @reports[sym] = Report.new(*args)
+ end
+
+ public
+ def initialize()
+ @reports = Hash.new
+
+ add_report(:unit_test,
+ "Unit Tests",
+ "unit tests",
+ Pathname.new("reports/unit.html"),
+ Pathname.new("unit_test.rhtml"))
+
+ add_report(:memcheck,
+ "Memory Tests",
+ "unit tests with valgrind memory checking",
+ Pathname.new("reports/memcheck.html"),
+ Pathname.new("memcheck.rhtml"))
+
+ add_report(:unit_detail,
+ "Unit Test Detail",
+ "unit test detail",
+ Pathname.new("reports/unit_detail.html"), # FIXME replace this with a lambda
+ Pathname.new("unit_detail.rhtml"))
+ end
+
+ def get_report(sym)
+ raise RuntimeError, "unknown report '#{sym}'" unless @reports.member?(sym)
+ @reports[sym]
+ end
+
+ def each(&block)
+ @reports.each(&block)
+ end
+ end
+end
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Parses the simple colon delimited test schedule files.
+
+ScheduledTest = Struct.new(:desc, :command_line, :status, :output)
+
+class Schedule
+ attr_reader :dir, :schedules
+
+ def initialize(dir, ss)
+ @dir = dir
+ @schedules = ss
+ end
+
+ def run
+ Dir::chdir(@dir.to_s) do
+ @schedules.each do |s|
+ reader, writer = IO.pipe
+ print "#{s.desc} ... "
+ pid = spawn(s.command_line, [ STDERR, STDOUT ] => writer)
+ writer.close
+ _, s.status = Process::waitpid2(pid)
+ puts (s.status.success? ? "pass" : "fail")
+ s.output = reader.read
+ end
+ end
+ end
+
+ def self.read(dir, io)
+ ss = Array.new
+
+ io.readlines.each do |line|
+ case line.strip
+ when /^\#.*/
+ next
+
+ when /([^:]+):(.*)/
+ ss << ScheduledTest.new($1.strip, $2.strip)
+
+ else
+ raise RuntimeError, "badly formatted schedule line"
+ end
+ end
+
+ Schedule.new(dir, ss)
+ end
+end
+
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Provides a simple way of accessing the contents of files by a symbol
+# name. Useful for erb templates.
+
+require 'pathname'
+
+class StringStore
+ attr_accessor :path
+
+ def initialize(p)
+ @paths = p.nil? ? Array.new : p # FIXME: do we need to copy p ?
+ end
+
+ def lookup(sym)
+ files = expansions(sym)
+
+ @paths.each do |p|
+ files.each do |f|
+ pn = Pathname.new("#{p}/#{f}")
+ if pn.file?
+ return pn.read
+ end
+ end
+ end
+
+ raise RuntimeError, "unknown string entry: #{sym}"
+ end
+
+ private
+ def expansions(sym)
+ ["#{sym}", "#{sym}.txt"]
+ end
+end
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Reads the schedule files given on the command line. Runs them and
+# generates the reports.
+
+# FIXME: a lot of duplication with unit_test.rb
+
+require 'schedule_file'
+require 'pathname'
+require 'reports'
+require 'erb'
+require 'report_templates'
+
+include ReportTemplates
+
+schedules = ARGV.map do |f|
+ p = Pathname.new(f)
+ Schedule.read(p.dirname, p)
+end
+
+total_passed = 0
+total_failed = 0
+
+# We need to make sure the lvm shared libs are in the LD_LIBRARY_PATH
+ENV['LD_LIBRARY_PATH'] = `pwd`.strip + "/libdm:" + (ENV['LD_LIBRARY_PATH'] || '')
+
+ENV['TEST_TOOL'] = "valgrind --leak-check=full --show-reachable=yes"
+
+schedules.each do |s|
+ s.run
+
+ s.schedules.each do |t|
+ if t.status.success?
+ total_passed += 1
+ else
+ total_failed += 1
+ end
+ end
+end
+
+def mangle(txt)
+ txt.gsub(/\s+/, '_')
+end
+
+MemcheckStats = Struct.new(:definitely_lost, :indirectly_lost, :possibly_lost, :reachable)
+
+def format(bytes, blocks)
+ "#{bytes} bytes, #{blocks} blocks"
+end
+
+# Examines the output for details of leaks
+def extract_stats(t)
+ d = i = p = r = '-'
+
+ t.output.split("\n").each do |l|
+ case l
+ when /==\d+== definitely lost: ([0-9,]+) bytes in ([0-9,]+) blocks/
+ d = format($1, $2)
+ when /==\d+== indirectly lost: ([0-9,]+) bytes in ([0-9,]+) blocks/
+ i = format($1, $2)
+ when /==\d+== possibly lost: ([0-9,]+) bytes in ([0-9,]+) blocks/
+ p = format($1, $2)
+ when /==\d+== still reachable: ([0-9,]+) bytes in ([0-9,]+) blocks/
+ r = format($1, $2)
+ end
+ end
+
+ MemcheckStats.new(d, i, p, r)
+end
+
+generate_report(:memcheck, binding)
+
+# now we generate a detail report for each schedule
+schedules.each do |s|
+ s.schedules.each do |t|
+ generate_report(:unit_detail, binding, Pathname.new("reports/memcheck_#{mangle(t.desc)}.html"))
+ end
+end
--- /dev/null
+<html>
+<head>
+<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
+<title><%= title %></title>
+<link title="Style" type="text/css" rel="stylesheet" href="stylesheet.css">
+</head>
+
+<body>
+<div id="banner">
+<h2><%= title %></h2>
+</div>
+<div id="main">
+ <div id="controls">
+ <table>
+ <tr><td><a href="index.html">Generation times</a></td></tr>
+ <tr><td><a href="unit.html">Unit tests</a></td></tr>
+ <tr><td><a href="memcheck.html">Memory tests</a></td></tr>
+ </table>
+ </div>
+
+ <div id="body">
+ <%= body %>
+ </div>
+</div>
+</body>
--- /dev/null
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+<tr><th>Report</th><th>Generation time</th></tr>
+<% [:unit_test, :memcheck].each do |sym| %>
+<% r = reports.get_report(sym) %>
+<tr>
+ <td>
+ <% if r.path.file? %>
+ <a href="<%= r.path.to_s.gsub(/^reports\//, '') %>"><%= r.short_desc %></a>
+ <% else %>
+ <%= r.short_desc %>
+ <% end %>
+ </td>
+ <td><%= safe_mtime(r) %></td>
+</tr>
+<% end %>
+</table>
+
--- /dev/null
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+ <tr><th>Tests passed</th><th>Tests failed</th></tr>
+ <tr><td class="pass"><%= total_passed %></td><td <%= total_failed == 0 ? "" : "class=\"fail\""%>><%= total_failed %></td></tr>
+</table>
+
+<% schedules.each do |s| %>
+<h3><%= s.dir.sub('./unit-tests/', '') %></h3>
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+<tr><th>Test</th><th>Result</th><th>Definitely lost</th><th>indirectly lost</th><th>possibly lost</th><th>still reachable</th><tr>
+
+<% s.schedules.each do |t| %>
+<tr>
+ <td>
+ <a href="memcheck_<%= mangle(t.desc) %>.html"><%= t.desc %></a>
+ </td>
+ <% if t.status.success? %>
+ <td class="pass">pass</td>
+ <% else %>
+ <td class="fail">fail</td>
+ <% end %>
+
+ <% stats = extract_stats(t) %>
+ <td><%= stats.definitely_lost %></td>
+ <td><%= stats.indirectly_lost %></td>
+ <td><%= stats.possibly_lost %></td>
+ <td><%= stats.reachable %></td>
+</tr>
+<% end %>
+</table>
+<% end %>
--- /dev/null
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+<tr><th>Test</th><th>Result</th></tr>
+<tr>
+ <td>
+ <%= t.desc %>
+ </td>
+ <% if t.status.success? %>
+ <td class="pass">pass</td>
+ <% else %>
+ <td class="fail">fail</td>
+ <% end %>
+</tr>
+</table>
+
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+<tr><th>Command line</th></tr>
+<tr>
+ <td>
+ <pre>
+<%= t.command_line %>
+ </pre>
+ </td>
+</tr>
+</table>
+
+
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+<tr><th>Output</th></tr>
+<tr>
+ <td>
+ <pre>
+<%= t.output %>
+ </pre>
+ </td>
+</tr>
+</table>
+
--- /dev/null
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+ <tr><th>Tests passed</th><th>Tests failed</th></tr>
+ <tr><td class="pass"><%= total_passed %></td><td <%= total_failed == 0 ? "" : "class=\"fail\""%>><%= total_failed %></td></tr>
+</table>
+
+<% schedules.each do |s| %>
+<h3><%= s.dir.sub('./unit-tests/', '') %></h3>
+<table width="95%" cellspacing="2" cellpadding="5" border="0" class="stripes">
+<tr><th>Test</th><th>Result</th></tr>
+<% s.schedules.each do |t| %>
+<tr>
+ <td>
+ <a href="detail_<%= mangle(t.desc) %>.html"><%= t.desc %></a>
+ </td>
+ <% if t.status.success? %>
+ <td class="pass">pass</td>
+ <% else %>
+ <td class="fail">fail</td>
+ <% end %>
+</tr>
+<% end %>
+</table>
+<% end %>
--- /dev/null
+# This is a comment
+description number 1:$TEST_TOOL ls
+foo bar: $TEST_TOOL du -hs .
+ this comment is prefixed with whitespace: $TEST_TOOL date
\ No newline at end of file
--- /dev/null
+Hello, world!
--- /dev/null
+one
+two
+three
\ No newline at end of file
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+require 'test/unit'
+require 'stringio'
+require 'log'
+
+class TestLog < Test::Unit::TestCase
+ include Log
+
+ private
+ def remove_timestamps(l)
+ l.gsub(/\[[^\]]*\]/, '')
+ end
+
+ public
+ def test_log
+ StringIO.open do |out|
+ init(out)
+
+ info("msg1")
+ warning("msg2")
+ debug("msg3")
+
+ assert_equal("I, INFO -- : msg1\nW, WARN -- : msg2\nD, DEBUG -- : msg3\n",
+ remove_timestamps(out.string))
+ end
+ end
+end
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+require 'test/unit'
+require 'pathname'
+require 'schedule_file'
+
+class TestScheduleFile < Test::Unit::TestCase
+ def test_reading
+ p = Pathname.new("report-generators/test/example.schedule")
+ p.open do |f|
+ s = Schedule.read(p.dirname, f)
+
+ assert_equal(3, s.schedules.size)
+ assert_equal(s.schedules[2].desc, "this comment is prefixed with whitespace")
+ assert_equal(s.schedules[0].command_line, "$TEST_TOOL ls")
+ end
+ end
+
+ def test_running
+ p = Pathname.new("report-generators/test/example.schedule")
+ p.open do |f|
+ s = Schedule.read(p.dirname, f)
+ s.run
+
+ s.schedules.each do |t|
+ assert(t.status.success?)
+ end
+ end
+ end
+end
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+require 'string-store'
+require 'test/unit'
+
+class TestStringStore < Test::Unit::TestCase
+ def setup
+ @ss = StringStore.new(['report-generators/test/strings',
+ 'report-generators/test/strings/more_strings'])
+ end
+
+ def test_lookup
+ assert_equal("Hello, world!\n", @ss.lookup(:test1))
+ assert_equal("one\ntwo\nthree", @ss.lookup(:test2))
+ assert_equal("lorem\n", @ss.lookup(:test3))
+
+ assert_raises(RuntimeError) do
+ @ss.lookup(:unlikely_name)
+ end
+ end
+end
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+require 'tc_log'
+require 'tc_string_store'
+require 'tc_schedule_file'
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# This generates the index for the reports, including generation
+# times.
+
+require 'log'
+require 'string-store'
+require 'reports'
+require 'erb'
+require 'report_templates'
+
+include Reports
+
+reports = ReportRegister.new
+
+def safe_mtime(r)
+ r.path.file? ? r.path.mtime.to_s : "not generated"
+end
+
+template_store = TemplateStringStore.new
+
+# FIXME: use generate_report() method
+erb = ERB.new(template_store.lookup("index.rhtml"))
+body = erb.result(binding)
+title = "Generation times"
+
+erb = ERB.new(template_store.lookup("boiler_plate.rhtml"))
+txt = erb.result(binding)
+
+Pathname.new("reports/index.html").open("w") do |f|
+ f.puts txt
+end
+
+
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Reads the schedule files given on the command line. Runs them and
+# generates the reports.
+
+require 'schedule_file'
+require 'pathname'
+require 'reports'
+require 'erb'
+require 'report_templates'
+
+include ReportTemplates
+
+schedules = ARGV.map do |f|
+ p = Pathname.new(f)
+ Schedule.read(p.dirname, p)
+end
+
+total_passed = 0
+total_failed = 0
+
+# We need to make sure the lvm shared libs are in the LD_LIBRARY_PATH
+ENV['LD_LIBRARY_PATH'] = `pwd`.strip + "/libdm:" + (ENV['LD_LIBRARY_PATH'] || '')
+
+schedules.each do |s|
+ s.run
+
+ s.schedules.each do |t|
+ if t.status.success?
+ total_passed += 1
+ else
+ total_failed += 1
+ end
+ end
+end
+
+def mangle(txt)
+ txt.gsub(/\s+/, '_')
+end
+
+generate_report(:unit_test, binding)
+
+# now we generate a detail report for each schedule
+schedules.each do |s|
+ s.schedules.each do |t|
+ generate_report(:unit_detail, binding, Pathname.new("reports/detail_#{mangle(t.desc)}.html"))
+ end
+end
--- /dev/null
+/* Styles for main page */
+#banner {
+ background: #9c9;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ border-bottom: 2px solid;
+ font: small-caps 20px/20px "Times New Roman", serif;
+ color: #282;
+ text-align: center;
+}
+
+#banner img {
+ float: left;
+}
+
+#main {
+ margin-left: 0em;
+ padding-top: 4ex;
+ padding-left: 2em;
+ background: white;
+}
+
+h1 {
+ font: 150% sans-serif;
+ color: #226;
+ border-bottom: 3px dotted #77d;
+}
+
+body {
+ font: normal 75% verdana,arial,helvetica;
+ color:#000000;
+}
+
+table tr td, table tr th {
+ font-size: 75%;
+}
+
+table.stripes tr th {
+ font-weight: bold;
+ text-align: left;
+ background: #a0a0a0;
+}
+
+table.stripes tr td {
+ background: #ccccc0;
+}
+
+td.pass {
+ color: green;
+}
+
+td.fail {
+ color: red;
+ font-weight: bold;
+}
+
+#main {
+ padding-left: 0em;
+}
+
+#controls {
+ float: left;
+ padding-top: 1em;
+ padding-left: 1em;
+ padding-right: 1em;
+ padding-bottom: 1em;
+ width: 14em;
+ border-right: 2px solid;
+}
+
+#body {
+ margin-left: 16em;
+ padding-top: 4ex;
+ padding-left: 2em;
+ background: white;
+ border-left: 2px solid;
+}
--- /dev/null
+#
+# Copyright (C) 2006-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+include $(top_builddir)/make.tmpl
+
+SCRIPTS = lvmdump.sh lvmconf.sh vgimportclone.sh
+ifeq ("@FSADM@", "yes")
+ SCRIPTS += fsadm.sh
+endif
+
+OCF_SCRIPTS =
+ifeq ("@OCF@", "yes")
+ OCF_SCRIPTS += VolumeGroup.ocf
+endif
+
+vpath %.sh $(srcdir)
+
+%_install: %.sh
+ $(INSTALL_PROGRAM) -D $< $(sbindir)/$(basename $(<F))
+
+%_install: %.ocf
+ $(INSTALL_DIR) $(ocf_scriptdir)
+ $(INSTALL_SCRIPT) $< $(ocf_scriptdir)/$(basename $(<F))
+
+install_lvm2: $(SCRIPTS:.sh=_install)
+
+install_ocf: $(OCF_SCRIPTS:.ocf=_install)
+
+install: install_lvm2 install_ocf
+
+# FIXME Customise for other distributions
+install_initscripts:
+ $(INSTALL_DIR) $(initdir)
+ $(INSTALL_SCRIPT) lvm2_monitoring_init_red_hat $(initdir)/lvm2-monitor
+ifneq ("@CLVMD@", "none")
+ $(INSTALL_SCRIPT) clvmd_init_red_hat $(initdir)/clvmd
+endif
+ifeq ("@BUILD_CMIRRORD@", "yes")
+ $(INSTALL_SCRIPT) cmirrord_init_red_hat $(initdir)/cmirrord
+endif
+
+DISTCLEAN_TARGETS += clvmd_init_red_hat cmirrord_init_red_hat lvm2_monitoring_init_red_hat
--- /dev/null
+#!/bin/sh
+#
+# VolumeGroup
+#
+# Description: Manages an LVM2 volume group as an HA resource in
+# an OCF-compliant cluster
+#
+#
+# Authors: Alan Robertson, Lars Marowsky-Bree, Florian Haas,
+# and others from the Linux-HA project
+# License: GNU General Public License (GPL)
+# Copyright: (C) 2002 - 2005 International Business Machines, Inc.
+# (C) 2010 LINBIT HA-Solutions GmbH
+#
+# This code significantly inspired by the LVM resource
+# in FailSafe by Lars Marowsky-Bree
+#
+#######################################################################
+# Initialization:
+
+: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
+. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs
+
+#######################################################################
+
+
+usage() {
+ methods=`VolumeGroup_methods`
+ methods=`echo $methods | tr ' ' '|'`
+ cat <<EOF
+ usage: $0 $methods
+
+ $0 manages an LVM Volume Group (VG) as an HA resource
+
+ The 'start' operation brings the given volume online
+ The 'stop' operation takes the given volume offline
+ The 'status' operation reports whether the volume is available
+ The 'monitor' operation reports whether the volume seems present
+ The 'validate-all' operation checks whether the OCF parameters are valid
+ The 'methods' operation reports on the methods $0 supports
+
+EOF
+}
+
+meta_data() {
+ cat <<EOF
+<?xml version="1.0"?>
+<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
+<resource-agent name="VolumeGroup">
+<version>1.0</version>
+
+<longdesc lang="en">
+Resource script for an LVM Volume Group.
+</longdesc>
+<shortdesc lang="en">Controls the availability of an LVM Volume Group</shortdesc>
+
+<parameters>
+<parameter name="volgrpname" unique="0" required="1">
+<longdesc lang="en">
+The name of volume group.
+</longdesc>
+<shortdesc lang="en">Volume group name</shortdesc>
+<content type="string" default="" />
+</parameter>
+<parameter name="exclusive" unique="0" required="0">
+<longdesc lang="en">
+If set, the volume group will be activated exclusively.
+</longdesc>
+<shortdesc lang="en">Exclusive activation</shortdesc>
+<content type="boolean" default="false" />
+</parameter>
+</parameters>
+
+<actions>
+<action name="start" timeout="30" />
+<action name="stop" timeout="30" />
+<action name="status" timeout="30" />
+<action name="monitor" depth="0" timeout="30" interval="10" />
+<action name="methods" timeout="5" />
+<action name="meta-data" timeout="5" />
+<action name="validate-all" timeout="5" />
+</actions>
+</resource-agent>
+EOF
+}
+
+#
+# methods: What methods/operations do we support?
+#
+VolumeGroup_methods() {
+ cat <<EOF
+ start
+ stop
+ status
+ monitor
+ methods
+ validate-all
+ usage
+EOF
+}
+
+#
+# Report on LVM volume status. VG may be reported as active
+# ($OCF_SUCCESS) or inactive ($OCF_NOT_RUNNING)
+#
+VolumeGroup_status() {
+
+ VGOUT=`vgdisplay -v $OCF_RESKEY_volgrpname 2>&1` || exit $OCF_ERR_GENERIC
+ echo "$VGOUT" | grep -i 'Status[ \t]*available' >/dev/null
+ rc=$?
+
+ if [ $rc -eq 0 ]; then
+ ocf_log debug "LVM Volume Group $OCF_RESKEY_volgrpname is available (started)"
+ else
+ ocf_log debug "LVM Volume Group $OCF_RESKEY_volgrpname is not available (stopped)"
+ return $OCF_NOT_RUNNING
+ fi
+
+ if echo "$VGOUT" | grep -i 'Access.*read/write' >/dev/null; then
+ ocf_log debug "Volume $OCF_RESKEY_volgrpname is available read/write (running)"
+ else
+ ocf_log debug "Volume $OCF_RESKEY_volgrpname is available read-only (running)"
+ fi
+
+ return $OCF_SUCCESS
+}
+
+#
+# Monitor the volume - does it really seem to be working? May report
+# $OCF_SUCCESS or $OCF_NOT_RUNNING like VolumeGroup_status, plus
+# $OCF_ERR_GENERIC in case vgck reports an error.
+#
+VolumeGroup_monitor() {
+ if ! VolumeGroup_status $OCF_RESKEY_volgrpname; then
+ ocf_log info "LVM Volume Group $OCF_RESKEY_volgrpname is offline"
+ return $OCF_NOT_RUNNING
+ fi
+
+ ocf_run vgck $OCF_RESKEY_volgrpname || exit $OCF_ERR_GENERIC
+
+ return $OCF_SUCCESS
+}
+
+#
+# Activate the volume group, either locally (if $OCF_RESKEY_exclusive
+# is false or unset), or exclusively (if $OCF_RESKEY_exclusive is
+# true).
+# Either returns successfully, or exits with $OCF_ERR_GENERIC.
+#
+VolumeGroup_start() {
+
+ ocf_log info "Activating volume group $OCF_RESKEY_volgrpname"
+ ocf_run vgscan
+
+ local active_mode
+ active_mode="ly"
+ if ocf_is_true "$OCF_RESKEY_exclusive" ; then
+ active_mode="ey"
+ fi
+
+ ocf_run vgchange -a $active_mode $OCF_RESKEY_volgrpname || exit $OCF_ERR_GENERIC
+
+ if ! VolumeGroup_status $OCF_RESKEY_volgrpname; then
+ ocf_log err "LVM: $OCF_RESKEY_volgrpname did not activate correctly"
+ exit $OCF_ERR_GENERIC
+ fi
+
+ return $OCF_SUCCESS
+}
+
+#
+# Deactivate the volume group.
+# Either returns successfully, or exits with $OCF_ERR_GENERIC.
+#
+VolumeGroup_stop() {
+ if ! VolumeGroup_status; then
+ ocf_log debug "Volume Group $OCF_RESKEY_volgrpname already stopped"
+ return $OCF_SUCCESS
+ fi
+
+ ocf_log info "Deactivating volume group $OCF_RESKEY_volgrpname"
+ ocf_run vgchange -a ln $OCF_RESKEY_volgrpname || exit $OCF_ERR_GENERIC
+
+ if VolumeGroup_status; then
+ ocf_log err "LVM: $OCF_RESKEY_volgrpname did not stop correctly"
+ exit $OCF_ERR_GENERIC
+ fi
+
+ return $OCF_SUCCESS
+}
+
+#
+# Check whether the OCF instance parameters are valid.
+# Either returns successfully, or exits with
+# $OCF_ERR_CONFIGURED if required parameters are missing;
+# $OCF_ERR_INSTALLED if required binaries are missing;
+# $OCF_ERR_GENERIC in case of any other error.
+#
+VolumeGroup_validate_all() {
+
+ if [ -z $OCF_RESKEY_volgrpname ]; then
+ ocf_log err 'Missing required parameter "volgrpname"!'
+ exit $OCF_ERR_CONFIGURED
+ fi
+
+ check_binary vgchange
+ check_binary vgck
+ check_binary vgdisplay
+
+ # Run the following tests only if we're not invoked by a probe
+ # operation
+ if ! ocf_is_probe; then
+ # Off-the-shelf tests...
+ vgck "$OCF_RESKEY_volgrpname" >/dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ ocf_log err "Volume group $OCF_RESKEY_volgrpname does not exist or contains error!"
+ exit $OCF_ERR_GENERIC
+ fi
+
+ # Double-check
+ vgdisplay -v "$OCF_RESKEY_volgrpname" >/dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ ocf_log err "Volume group $OCF_RESKEY_volgrpname does not exist or contains error!"
+ exit $OCF_ERR_GENERIC
+ fi
+ fi
+
+ return $OCF_SUCCESS
+}
+
+#
+# 'main' starts here...
+#
+if [ $# -ne 1 ]; then
+ usage
+ exit $OCF_ERR_ARGS
+fi
+
+case $1 in
+ meta-data) meta_data
+ exit $OCF_SUCCESS;;
+
+ methods) VolumeGroup_methods
+ exit $OCF_SUCCESS;;
+
+ usage) usage
+ exit $OCF_SUCCESS;;
+ *) ;;
+esac
+
+# Everything except usage and meta-data must pass the validate test
+VolumeGroup_validate_all
+
+# What kind of method was invoked?
+case "$1" in
+ start)
+ VolumeGroup_start
+ ;;
+ stop)
+ VolumeGroup_stop
+ ;;
+ status)
+ VolumeGroup_status
+ ;;
+ monitor)
+ VolumeGroup_monitor
+ ;;
+ validate-all)
+ ;;
+ notify|promote|demote|migrate_from|migrate_to)
+ usage
+ exit $OCF_ERR_UNIMPLEMENTED
+ ;;
+ *) usage
+ exit $OCF_ERR_ARGS
+ ;;
+esac
+
+exit $?
--- /dev/null
+#!/bin/bash
+#
+# Edit an lvm.conf file to enable cluster locking.
+#
+# $1 is the directory where the locking library is installed.
+# $2 (optional) is the config file
+# $3 (optional) is the locking library name
+#
+#
+PREFIX=$1
+LVMCONF=$2
+LIB=$3
+
+if [ -z "$PREFIX" ]
+then
+ echo "usage: $0 <prefix> [<config file>] [<library>]"
+ echo ""
+ echo "<prefix>|UNDO location of the cluster locking shared library. (no default)"
+ echo " UNDO will reset the locking back to local"
+ echo "<config file> name of the LVM config file (default: /etc/lvm/lvm.conf)"
+ echo "<library> name of the shared library (default: liblvm2clusterlock.so)"
+ echo ""
+ exit 0
+fi
+
+[ -z "$LVMCONF" ] && LVMCONF="/etc/lvm/lvm.conf"
+[ -z "$LIB" ] && LIB="liblvm2clusterlock.so"
+
+if [ "$PREFIX" = "UNDO" ]
+then
+ locking_type="1"
+else
+ locking_type="2"
+
+ if [ "${PREFIX:0:1}" != "/" ]
+ then
+ echo "Prefix must be an absolute path name (starting with a /)"
+ exit 12
+ fi
+
+ if [ ! -f "$PREFIX/$LIB" ]
+ then
+ echo "$PREFIX/$LIB does not exist, did you do a \"make install\" ?"
+ exit 11
+ fi
+fi
+
+if [ ! -f "$LVMCONF" ]
+then
+ echo "$LVMCONF does not exist"
+ exit 10
+fi
+
+
+SCRIPTFILE=`mktemp -t lvmscript.XXXXXXXXXX`
+TMPFILE=`mktemp -t lvmtmp.XXXXXXXXXX`
+
+
+# Flags so we know which parts of the file we can replace and which need
+# adding. These are return codes from grep, so zero means it IS present!
+have_type=1
+have_dir=1
+have_library=1
+have_global=1
+
+grep -q '^[[:blank:]]*locking_type[[:blank:]]*=' $LVMCONF
+have_type=$?
+
+grep -q '^[[:blank:]]*library_dir[[:blank:]]*=' $LVMCONF
+have_dir=$?
+
+grep -q '^[[:blank:]]*locking_library[[:blank:]]*=' $LVMCONF
+have_library=$?
+
+# Those options are in section "global {" so we must have one if any are present.
+if [ "$have_type" = "0" -o "$have_dir" = "0" -o "$have_library" = "0" ]
+then
+
+ # See if we can find it...
+ grep -q '^[[:blank:]]*global[[:blank:]]*{' $LVMCONF
+ have_global=$?
+
+ if [ "$have_global" = "1" ]
+ then
+ echo "global keys but no 'global {' found, can't edit file"
+ exit 12
+ fi
+fi
+
+# So if we don't have "global {" we need to create one and
+# populate it
+
+if [ "$have_global" = "1" ]
+then
+ cat $LVMCONF - <<EOF > $TMPFILE
+global {
+ # Enable locking for cluster LVM
+ locking_type = $locking_type
+ library_dir = "$PREFIX"
+ locking_library = "$LIB"
+}
+EOF
+ if [ $? != 0 ]
+ then
+ echo "failed to create temporary config file, $LVMCONF not updated"
+ exit 1
+ fi
+else
+ #
+ # We have a "global {" section, so add or replace the
+ # locking entries as appropriate
+ #
+
+ if [ "$have_type" = "0" ]
+ then
+ SEDCMD=" s/^[[:blank:]]*locking_type[[:blank:]]*=.*/\ \ \ \ locking_type = $locking_type/g"
+ else
+ SEDCMD=" /global[[:blank:]]*{/a\ \ \ \ locking_type = 2"
+ fi
+
+ if [ "$have_dir" = "0" ]
+ then
+ SEDCMD="${SEDCMD}\ns'^[[:blank:]]*library_dir[[:blank:]]*=.*'\ \ \ \ library_dir = \"$PREFIX\"'g"
+ else
+ SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ library_dir = \"$PREFIX\""
+ fi
+
+ if [ "$have_library" = "0" ]
+ then
+ SEDCMD="${SEDCMD}\ns/^[[:blank:]]*locking_library[[:blank:]]*=.*/\ \ \ \ locking_library = \"$LIB\"/g"
+ else
+ SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ locking_library = \"$LIB\""
+ fi
+
+ echo -e $SEDCMD > $SCRIPTFILE
+ sed <$LVMCONF >$TMPFILE -f $SCRIPTFILE
+ if [ $? != 0 ]
+ then
+ echo "sed failed, $LVMCONF not updated"
+ exit 1
+ fi
+fi
+
+# Now we have a suitably editted config file in a temp place,
+# backup the original and copy our new one into place.
+
+cp $LVMCONF $LVMCONF.nocluster
+if [ $? != 0 ]
+ then
+ echo "failed to backup old config file, $LVMCONF not updated"
+ exit 2
+fi
+
+cp $TMPFILE $LVMCONF
+if [ $? != 0 ]
+ then
+ echo "failed to copy new config file into place, check $LVMCONF is still OK"
+ exit 3
+fi
+
+rm -f $SCRIPTFILE $TMPFILE
+
--- /dev/null
+#!/bin/bash
+#
+# clvmd - Clustered LVM Daemon init script
+#
+# chkconfig: - 24 76
+# description: Cluster daemon for userland logical volume management tools.
+#
+# For Red-Hat-based distributions such as Fedora, RHEL, CentOS.
+#
+### BEGIN INIT INFO
+# Provides: clvmd
+# Required-Start: $local_fs@CLVMD_CMANAGERS@
+# Required-Stop: $local_fs@CLVMD_CMANAGERS@
+# Short-Description: This service is Clusterd LVM Daemon.
+# Description: Cluster daemon for userland logical volume management tools.
+### END INIT INFO
+
+. /etc/rc.d/init.d/functions
+
+DAEMON=clvmd
+
+exec_prefix=@exec_prefix@
+sbindir=@sbindir@
+
+lvm_vgchange=${sbindir}/vgchange
+lvm_vgdisplay=${sbindir}/vgdisplay
+lvm_vgscan=${sbindir}/vgscan
+lvm_lvdisplay=${sbindir}/lvdisplay
+
+CLVMDOPTS="-T30"
+
+[ -f /etc/sysconfig/cluster ] && . /etc/sysconfig/cluster
+[ -f /etc/sysconfig/$DAEMON ] && . /etc/sysconfig/$DAEMON
+
+[ -n "$CLVMD_CLUSTER_IFACE" ] && CLVMDOPTS="$CLVMDOPTS -I $CLVMD_CLUSTER_IFACE"
+
+# allow up to $CLVMD_STOP_TIMEOUT seconds to clvmd to complete exit operations
+# default to 10 seconds
+
+[ -z $CLMVD_STOP_TIMEOUT ] && CLVMD_STOP_TIMEOUT=10
+
+LOCK_FILE="/var/lock/subsys/$DAEMON"
+
+# NOTE: replace this with vgs, once display filter per attr is implemented.
+clustered_vgs() {
+ ${lvm_vgdisplay} 2>/dev/null | \
+ awk 'BEGIN {RS="VG Name"} {if (/Clustered/) print $1;}'
+}
+
+clustered_active_lvs() {
+ for i in $(clustered_vgs); do
+ ${lvm_lvdisplay} $i 2>/dev/null | \
+ awk 'BEGIN {RS="LV Name"} {if (/[^N^O^T] available/) print $1;}'
+ done
+}
+
+rh_status() {
+ status $DAEMON
+}
+
+rh_status_q() {
+ rh_status >/dev/null 2>&1
+}
+
+start()
+{
+ if ! rh_status_q; then
+ echo -n "Starting $DAEMON: "
+ $DAEMON $CLVMDOPTS || return $?
+ echo
+ fi
+
+ # Refresh local cache.
+ #
+ # It's possible that new PVs were added to this, or other VGs
+ # while this node was down. So we run vgscan here to avoid
+ # any potential "Missing UUID" messages with subsequent
+ # LVM commands.
+
+ # The following step would be better and more informative to the user:
+ # 'action "Refreshing VG(s) local cache:" ${lvm_vgscan}'
+ # but it could show warnings such as:
+ # 'clvmd not running on node x-y-z Unable to obtain global lock.'
+ # and the action would be shown as FAILED when in reality it didn't.
+ # Ideally vgscan should have a startup mode that would not print
+ # unnecessary warnings.
+
+ ${lvm_vgscan} > /dev/null 2>&1
+
+ action "Activating VG(s):" ${lvm_vgchange} -ayl $LVM_VGS || return $?
+
+ touch $LOCK_FILE
+
+ return 0
+}
+
+wait_for_finish()
+{
+ count=0
+ while [ "$count" -le "$CLVMD_STOP_TIMEOUT" ] && \
+ rh_status_q ]; do
+ sleep 1
+ count=$((count+1))
+ done
+
+ ! rh_status_q
+}
+
+stop()
+{
+ rh_status_q || return 0
+
+ [ -z "$LVM_VGS" ] && LVM_VGS="$(clustered_vgs)"
+ if [ -n "$LVM_VGS" ]; then
+ action "Deactivating clustered VG(s):" ${lvm_vgchange} -anl $LVM_VGS || return $?
+ fi
+
+ action "Signaling $DAEMON to exit" kill -TERM $(pidofproc $DAEMON) || return $?
+
+ # wait half second before we start the waiting loop or we will show
+ # the loop more time than really necessary
+ usleep 500000
+
+ # clvmd could take some time to stop
+ rh_status_q && action "Waiting for $DAEMON to exit:" wait_for_finish
+
+ if rh_status_q; then
+ echo -n "$DAEMON failed to exit"
+ failure
+ echo
+ return 1
+ else
+ echo -n "$DAEMON terminated"
+ success
+ echo
+ fi
+
+ rm -f $LOCK_FILE
+
+ return 0
+}
+
+reload() {
+ rh_status_q || exit 7
+ action "Reloading $DAEMON configuration: " $DAEMON -R || return $?
+}
+
+restart() {
+ # if stop fails, restart will return the error and not attempt
+ # another start. Even if start is protected by rh_status_q,
+ # that would avoid spawning another daemon, it would try to
+ # reactivate the VGs.
+
+ # Try to get clvmd to restart itself. This will preserve
+ # exclusive LV locks
+ action "Restarting $DAEMON: " $DAEMON -S
+
+ # If that fails then do a normal stop & restart
+ if [ $? != 0 ]; then
+ stop && start
+ return $?
+ else
+ touch $LOCK_FILE
+ return 0
+ fi
+}
+
+[ "$EUID" != "0" ] && {
+ echo "clvmd init script can only be executed as root user"
+ exit 4
+}
+
+# See how we were called.
+case "$1" in
+ start)
+ start
+ rtrn=$?
+ ;;
+
+ stop)
+ stop
+ rtrn=$?
+ ;;
+
+ restart|force-reload)
+ restart
+ rtrn=$?
+ ;;
+
+ condrestart|try-restart)
+ rh_status_q || exit 0
+ restart
+ rtrn=$?
+ ;;
+
+ reload)
+ reload
+ rtrn=$?
+ ;;
+
+ status)
+ rh_status
+ rtrn=$?
+ if [ $rtrn = 0 ]; then
+ cvgs="$(clustered_vgs)"
+ echo Clustered Volume Groups: ${cvgs:-"(none)"}
+ clvs="$(clustered_active_lvs)"
+ echo Active clustered Logical Volumes: ${clvs:-"(none)"}
+ fi
+ ;;
+
+ *)
+ echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
+ rtrn=2
+ ;;
+esac
+
+exit $rtrn
--- /dev/null
+#!/bin/bash
+#
+# chkconfig: - 22 78
+# description: Starts and stops cmirrord
+#
+# For Red-Hat-based distributions such as Fedora, RHEL, CentOS.
+#
+### BEGIN INIT INFO
+# Provides: cmirrord
+# Required-Start: $network $time $local_fs
+# Required-Stop: $network $time $local_fs
+# Short-Description: Starts and stops cmirrord
+# Description: Starts and stops the cluster mirror log daemon
+### END INIT INFO
+
+. /etc/init.d/functions
+
+DAEMON=cmirrord
+
+LOCK_FILE="/var/lock/subsys/$DAEMON"
+
+start()
+{
+ if ! pidof $DAEMON > /dev/null
+ then
+ echo -n "Starting $DAEMON: "
+ daemon $DAEMON
+ rtrn=$?
+ echo
+ fi
+
+ return $rtrn
+}
+
+stop()
+{
+ echo -n "Stopping $DAEMON:"
+ killproc $DAEMON -TERM
+ rtrn=$?
+ echo
+
+ return $rtrn
+}
+
+wait_for_finish()
+{
+ count=0
+
+ while [ "$count" -le 10 -a -n "`pidof $DAEMON`" ]
+ do
+ sleep 1
+ count=$((count + 1))
+ done
+
+ if [ `pidof $DAEMON` ]
+ then
+ return 1
+ else
+ return 0
+ fi
+}
+
+cmirror_status()
+{
+ status $DAEMON
+}
+
+rtrn=1
+
+# See how we were called.
+case "$1" in
+ start)
+ start
+ rtrn=$?
+ [ $rtrn = 0 ] && touch $LOCK_FILE
+ ;;
+
+ stop)
+ stop
+ rtrn=$?
+ [ $rtrn = 0 ] && rm -f $LOCK_FILE
+ ;;
+
+ restart)
+ if stop
+ then
+ wait_for_finish
+ start
+ fi
+ rtrn=$?
+ ;;
+
+ status)
+ cmirror_status
+ rtrn=$?
+ if [ $rtrn -eq 0 ]; then
+ echo "cmirror is running."
+ fi
+ ;;
+
+ *)
+ echo $"Usage: $0 {start|stop|restart|status}"
+ ;;
+esac
+
+exit $rtrn
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2007-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Author: Zdenek Kabelac <zkabelac at redhat.com>
+#
+# Script for resizing devices (usable for LVM resize)
+#
+# Needed utilities:
+# mount, umount, grep, readlink, blockdev, blkid, fsck, xfs_check
+#
+# ext2/ext3/ext4: resize2fs, tune2fs
+# reiserfs: resize_reiserfs, reiserfstune
+# xfs: xfs_growfs, xfs_info
+#
+# Return values:
+# 0 success
+# 1 error
+# 2 break detected
+# 3 unsupported online filesystem check for given mounted fs
+
+TOOL=fsadm
+
+PATH=/sbin:/usr/sbin:/bin:/usr/sbin:$PATH
+
+# utilities
+TUNE_EXT=tune2fs
+RESIZE_EXT=resize2fs
+TUNE_REISER=reiserfstune
+RESIZE_REISER=resize_reiserfs
+TUNE_XFS=xfs_info
+RESIZE_XFS=xfs_growfs
+
+MOUNT=mount
+UMOUNT=umount
+MKDIR=mkdir
+RMDIR=rmdir
+BLOCKDEV=blockdev
+BLKID=blkid
+DATE=date
+GREP=grep
+READLINK=readlink
+READLINK_E="-e"
+FSCK=fsck
+XFS_CHECK=xfs_check
+
+# user may override lvm location by setting LVM_BINARY
+LVM=${LVM_BINARY:-lvm}
+
+YES=${_FSADM_YES}
+DRY=0
+VERB=
+FORCE=
+EXTOFF=0
+DO_LVRESIZE=0
+FSTYPE=unknown
+VOLUME=unknown
+TEMPDIR="${TMPDIR:-/tmp}/${TOOL}_${RANDOM}$$/m"
+BLOCKSIZE=
+BLOCKCOUNT=
+MOUNTPOINT=
+MOUNTED=
+REMOUNT=
+PROCMOUNTS="/proc/mounts"
+
+IFS_OLD=$IFS
+# without bash $'\n'
+NL='
+'
+
+tool_usage() {
+ echo "${TOOL}: Utility to resize or check the filesystem on a device"
+ echo
+ echo " ${TOOL} [options] check device"
+ echo " - Check the filesystem on device using fsck"
+ echo
+ echo " ${TOOL} [options] resize device [new_size[BKMGTPE]]"
+ echo " - Change the size of the filesystem on device to new_size"
+ echo
+ echo " Options:"
+ echo " -h | --help Show this help message"
+ echo " -v | --verbose Be verbose"
+ echo " -e | --ext-offline unmount filesystem before ext2/ext3/ext4 resize"
+ echo " -f | --force Bypass sanity checks"
+ echo " -n | --dry-run Print commands without running them"
+ echo " -l | --lvresize Resize given device (if it is LVM device)"
+ echo " -y | --yes Answer \"yes\" at any prompts"
+ echo
+ echo " new_size - Absolute number of filesystem blocks to be in the filesystem,"
+ echo " or an absolute size using a suffix (in powers of 1024)."
+ echo " If new_size is not supplied, the whole device is used."
+
+ exit
+}
+
+verbose() {
+ test -n "$VERB" && echo "$TOOL: $@" || true
+}
+
+error() {
+ echo "$TOOL: $@" >&2
+ cleanup 1
+}
+
+dry() {
+ if [ "$DRY" -ne 0 ]; then
+ verbose "Dry execution $@"
+ return 0
+ fi
+ verbose "Executing $@"
+ $@
+}
+
+cleanup() {
+ trap '' 2
+ # reset MOUNTPOINT - avoid recursion
+ test "$MOUNTPOINT" = "$TEMPDIR" && MOUNTPOINT="" temp_umount
+ if [ -n "$REMOUNT" ]; then
+ verbose "Remounting unmounted filesystem back"
+ dry $MOUNT "$VOLUME" "$MOUNTED"
+ fi
+ IFS=$IFS_OLD
+ trap 2
+
+ test "$1" -eq 2 && verbose "Break detected"
+
+ if [ "$DO_LVRESIZE" -eq 2 ]; then
+ # start LVRESIZE with the filesystem modification flag
+ # and allow recursive call of fsadm
+ _FSADM_YES=$YES
+ export _FSADM_YES
+ unset FSADM_RUNNING
+ dry exec $LVM lvresize $VERB $FORCE -r -L${NEWSIZE}b $VOLUME_ORIG
+ fi
+
+ # error exit status for break
+ exit ${1:-1}
+}
+
+# convert parameter from Exa/Peta/Tera/Giga/Mega/Kilo/Bytes and blocks
+# (2^(60/50/40/30/20/10/0))
+decode_size() {
+ case "$1" in
+ *[eE]) NEWSIZE=$(( ${1%[eE]} * 1152921504606846976 )) ;;
+ *[pP]) NEWSIZE=$(( ${1%[pP]} * 1125899906842624 )) ;;
+ *[tT]) NEWSIZE=$(( ${1%[tT]} * 1099511627776 )) ;;
+ *[gG]) NEWSIZE=$(( ${1%[gG]} * 1073741824 )) ;;
+ *[mM]) NEWSIZE=$(( ${1%[mM]} * 1048576 )) ;;
+ *[kK]) NEWSIZE=$(( ${1%[kK]} * 1024 )) ;;
+ *[bB]) NEWSIZE=${1%[bB]} ;;
+ *) NEWSIZE=$(( $1 * $2 )) ;;
+ esac
+ #NEWBLOCKCOUNT=$(round_block_size $NEWSIZE $2)
+ NEWBLOCKCOUNT=$(( $NEWSIZE / $2 ))
+
+ if [ $DO_LVRESIZE -eq 1 ]; then
+ # start lvresize, but first cleanup mounted dirs
+ DO_LVRESIZE=2
+ cleanup 0
+ fi
+}
+
+# detect filesystem on the given device
+# dereference device name if it is symbolic link
+detect_fs() {
+ VOLUME_ORIG=$1
+ VOLUME=${1#/dev/}
+ VOLUME=$($READLINK $READLINK_E "/dev/$VOLUME") || error "Cannot get readlink $1"
+ RVOLUME=$VOLUME
+ case "$RVOLUME" in
+ /dev/dm-[0-9]*)
+ read </sys/block/${RVOLUME#/dev/}/dm/name SYSVOLUME 2>&1 && VOLUME="/dev/mapper/$SYSVOLUME"
+ ;;
+ esac
+ # use /dev/null as cache file to be sure about the result
+ # not using option '-o value' to be compatible with older version of blkid
+ FSTYPE=$($BLKID -c /dev/null -s TYPE "$VOLUME") || error "Cannot get FSTYPE of \"$VOLUME\""
+ FSTYPE=${FSTYPE##*TYPE=\"} # cut quotation marks
+ FSTYPE=${FSTYPE%%\"*}
+ verbose "\"$FSTYPE\" filesystem found on \"$VOLUME\""
+}
+
+# check if the given device is already mounted and where
+# FIXME: resolve swap usage and device stacking
+detect_mounted() {
+ test -e $PROCMOUNTS || error "Cannot detect mounted device $VOLUME"
+
+ MOUNTED=$($GREP ^"$VOLUME" $PROCMOUNTS)
+
+ # for empty string try again with real volume name
+ test -z "$MOUNTED" && MOUNTED=$($GREP ^"$RVOLUME" $PROCMOUNTS)
+
+ # cut device name prefix and trim everything past mountpoint
+ # echo translates \040 to spaces
+ MOUNTED=${MOUNTED#* }
+ MOUNTED=$(echo -n -e ${MOUNTED%% *})
+
+ # for systems with different device names - check also mount output
+ if test -z "$MOUNTED" ; then
+ MOUNTED=$(LANG=C $MOUNT | $GREP ^"$VOLUME")
+ test -z "$MOUNTED" && MOUNTED=$(LANG=C $MOUNT | $GREP ^"$RVOLUME")
+ MOUNTED=${MOUNTED##* on }
+ MOUNTED=${MOUNTED% type *} # allow type in the mount name
+ fi
+
+ test -n "$MOUNTED"
+}
+
+# get the full size of device in bytes
+detect_device_size() {
+ # check if blockdev supports getsize64
+ $BLOCKDEV 2>&1 | $GREP getsize64 >/dev/null
+ if test $? -eq 0; then
+ DEVSIZE=$($BLOCKDEV --getsize64 "$VOLUME") || error "Cannot read size of device \"$VOLUME\""
+ else
+ DEVSIZE=$($BLOCKDEV --getsize "$VOLUME") || error "Cannot read size of device \"$VOLUME\""
+ SSSIZE=$($BLOCKDEV --getss "$VOLUME") || error "Cannot block size read device \"$VOLUME\""
+ DEVSIZE=$(($DEVSIZE * $SSSIZE))
+ fi
+}
+
+# round up $1 / $2
+# could be needed to gaurantee 'at least given size'
+# but it makes many troubles
+round_up_block_size() {
+ echo $(( ($1 + $2 - 1) / $2 ))
+}
+
+temp_mount() {
+ dry $MKDIR -p -m 0000 "$TEMPDIR" || error "Failed to create $TEMPDIR"
+ dry $MOUNT "$VOLUME" "$TEMPDIR" || error "Failed to mount $TEMPDIR"
+}
+
+temp_umount() {
+ dry $UMOUNT "$TEMPDIR" || error "Failed to umount $TEMPDIR"
+ dry $RMDIR "${TEMPDIR}" || error "Failed to remove $TEMPDIR"
+ dry $RMDIR "${TEMPDIR%%m}" || error "Failed to remove ${TEMPDIR%%m}"
+}
+
+yes_no() {
+ echo -n "$@? [Y|n] "
+
+ if [ -n "$YES" ]; then
+ echo y ; return 0
+ fi
+
+ while read -r -s -n 1 ANS ; do
+ case "$ANS" in
+ "y" | "Y" | "") echo y ; return 0 ;;
+ "n" | "N") echo n ; return 1 ;;
+ esac
+ done
+}
+
+try_umount() {
+ yes_no "Do you want to unmount \"$MOUNTED\"" && dry $UMOUNT "$MOUNTED" && return 0
+ error "Cannot proceed with mounted filesystem \"$MOUNTED\""
+}
+
+validate_parsing() {
+ test -n "$BLOCKSIZE" -a -n "$BLOCKCOUNT" || error "Cannot parse $1 output"
+}
+####################################
+# Resize ext2/ext3/ext4 filesystem
+# - unmounted or mounted for upsize
+# - unmounted for downsize
+####################################
+resize_ext() {
+ verbose "Parsing $TUNE_EXT -l \"$VOLUME\""
+ for i in $(LANG=C $TUNE_EXT -l "$VOLUME"); do
+ case "$i" in
+ "Block size"*) BLOCKSIZE=${i##* } ;;
+ "Block count"*) BLOCKCOUNT=${i##* } ;;
+ esac
+ done
+ validate_parsing $TUNE_EXT
+ decode_size $1 $BLOCKSIZE
+ FSFORCE=$FORCE
+
+ if [ "$NEWBLOCKCOUNT" -lt "$BLOCKCOUNT" -o "$EXTOFF" -eq 1 ]; then
+ detect_mounted && verbose "$RESIZE_EXT needs unmounted filesystem" && try_umount
+ REMOUNT=$MOUNTED
+ if test -n "$MOUNTED" ; then
+ # Forced fsck -f for umounted extX filesystem.
+ case "$-" in
+ *i*) dry $FSCK $YES -f "$VOLUME" ;;
+ *) dry $FSCK -f -p "$VOLUME" ;;
+ esac
+ fi
+ fi
+
+ verbose "Resizing filesystem on device \"$VOLUME\" to $NEWSIZE bytes ($BLOCKCOUNT -> $NEWBLOCKCOUNT blocks of $BLOCKSIZE bytes)"
+ dry $RESIZE_EXT $FSFORCE "$VOLUME" $NEWBLOCKCOUNT
+}
+
+#############################
+# Resize reiserfs filesystem
+# - unmounted for upsize
+# - unmounted for downsize
+#############################
+resize_reiser() {
+ detect_mounted && verbose "ReiserFS resizes only unmounted filesystem" && try_umount
+ REMOUNT=$MOUNTED
+ verbose "Parsing $TUNE_REISER \"$VOLUME\""
+ for i in $(LANG=C $TUNE_REISER "$VOLUME"); do
+ case "$i" in
+ "Blocksize"*) BLOCKSIZE=${i##*: } ;;
+ "Count of blocks"*) BLOCKCOUNT=${i##*: } ;;
+ esac
+ done
+ validate_parsing $TUNE_REISER
+ decode_size $1 $BLOCKSIZE
+ verbose "Resizing \"$VOLUME\" $BLOCKCOUNT -> $NEWBLOCKCOUNT blocks ($NEWSIZE bytes, bs: $NEWBLOCKCOUNT)"
+ if [ -n "$YES" ]; then
+ echo y | dry $RESIZE_REISER -s $NEWSIZE "$VOLUME"
+ else
+ dry $RESIZE_REISER -s $NEWSIZE "$VOLUME"
+ fi
+}
+
+########################
+# Resize XFS filesystem
+# - mounted for upsize
+# - cannot downsize
+########################
+resize_xfs() {
+ detect_mounted
+ MOUNTPOINT=$MOUNTED
+ if [ -z "$MOUNTED" ]; then
+ MOUNTPOINT=$TEMPDIR
+ temp_mount || error "Cannot mount Xfs filesystem"
+ fi
+ verbose "Parsing $TUNE_XFS \"$MOUNTPOINT\""
+ for i in $(LANG=C $TUNE_XFS "$MOUNTPOINT"); do
+ case "$i" in
+ "data"*) BLOCKSIZE=${i##*bsize=} ; BLOCKCOUNT=${i##*blocks=} ;;
+ esac
+ done
+ BLOCKSIZE=${BLOCKSIZE%%[^0-9]*}
+ BLOCKCOUNT=${BLOCKCOUNT%%[^0-9]*}
+ validate_parsing $TUNE_XFS
+ decode_size $1 $BLOCKSIZE
+ if [ $NEWBLOCKCOUNT -gt $BLOCKCOUNT ]; then
+ verbose "Resizing Xfs mounted on \"$MOUNTPOINT\" to fill device \"$VOLUME\""
+ dry $RESIZE_XFS $MOUNTPOINT
+ elif [ $NEWBLOCKCOUNT -eq $BLOCKCOUNT ]; then
+ verbose "Xfs filesystem already has the right size"
+ else
+ error "Xfs filesystem shrinking is unsupported"
+ fi
+}
+
+####################
+# Resize filesystem
+####################
+resize() {
+ NEWSIZE=$2
+ detect_fs "$1"
+ detect_device_size
+ verbose "Device \"$VOLUME\" size is $DEVSIZE bytes"
+ # if the size parameter is missing use device size
+ #if [ -n "$NEWSIZE" -a $NEWSIZE <
+ test -z "$NEWSIZE" && NEWSIZE=${DEVSIZE}b
+ IFS=$NL
+ case "$FSTYPE" in
+ "ext3"|"ext2"|"ext4") resize_ext $NEWSIZE ;;
+ "reiserfs") resize_reiser $NEWSIZE ;;
+ "xfs") resize_xfs $NEWSIZE ;;
+ *) error "Filesystem \"$FSTYPE\" on device \"$VOLUME\" is not supported by this tool" ;;
+ esac || error "Resize $FSTYPE failed"
+ cleanup 0
+}
+
+####################################
+# Calclulate diff between two dates
+# LANG=C input is expected the
+# only one supported
+####################################
+diff_dates() {
+ echo $(( $($DATE -u -d"$1" +%s 2>/dev/null) - $($DATE -u -d"$2" +%s 2>/dev/null) ))
+}
+
+###################
+# Check filesystem
+###################
+check() {
+ detect_fs "$1"
+ if detect_mounted ; then
+ verbose "Skipping filesystem check for device \"$VOLUME\" as the filesystem is mounted on $MOUNTED";
+ cleanup 3
+ fi
+
+ case "$FSTYPE" in
+ "ext2"|"ext3"|"ext4")
+ IFS_CHECK=$IFS
+ IFS=$NL
+ for i in $(LANG=C $TUNE_EXT -l "$VOLUME"); do
+ case "$i" in
+ "Last mount"*) LASTMOUNT=${i##*: } ;;
+ "Last checked"*) LASTCHECKED=${i##*: } ;;
+ esac
+ done
+ case "$LASTMOUNT" in
+ *"n/a") ;; # nothing to do - system was not mounted yet
+ *)
+ LASTDIFF=$(diff_dates $LASTMOUNT $LASTCHECKED)
+ if test "$LASTDIFF" -gt 0 ; then
+ verbose "Filesystem has not been checked after the last mount, using fsck -f"
+ FORCE="-f"
+ fi
+ ;;
+ esac
+ IFS=$IFS_CHECK
+ esac
+
+ case "$FSTYPE" in
+ "xfs") dry $XFS_CHECK "$VOLUME" ;;
+ *) # check if executed from interactive shell environment
+ case "$-" in
+ *i*) dry $FSCK $YES $FORCE "$VOLUME" ;;
+ *) dry $FSCK $FORCE -p "$VOLUME" ;;
+ esac
+ esac
+}
+
+#############################
+# start point of this script
+# - parsing parameters
+#############################
+trap "cleanup 2" 2
+
+# test if we are not invoked recursively
+test -n "$FSADM_RUNNING" && exit 0
+
+# test some prerequisities
+test -n "$TUNE_EXT" -a -n "$RESIZE_EXT" -a -n "$TUNE_REISER" -a -n "$RESIZE_REISER" \
+ -a -n "$TUNE_XFS" -a -n "$RESIZE_XFS" -a -n "$MOUNT" -a -n "$UMOUNT" -a -n "$MKDIR" \
+ -a -n "$RMDIR" -a -n "$BLOCKDEV" -a -n "$BLKID" -a -n "$GREP" -a -n "$READLINK" \
+ -a -n "$DATE" -a -n "$FSCK" -a -n "$XFS_CHECK" -a -n "LVM" \
+ || error "Required command definitions in the script are missing!"
+
+$LVM version >/dev/null 2>&1 || error "Could not run lvm binary '$LVM'"
+$($READLINK -e / >/dev/null 2>&1) || READLINK_E="-f"
+TEST64BIT=$(( 1000 * 1000000000000 ))
+test $TEST64BIT -eq 1000000000000000 || error "Shell does not handle 64bit arithmetic"
+$(echo Y | $GREP Y >/dev/null) || error "Grep does not work properly"
+test $($DATE -u -d"Jan 01 00:00:01 1970" +%s) -eq 1 || error "Date translation does not work"
+
+
+if [ "$#" -eq 0 ] ; then
+ tool_usage
+fi
+
+while [ "$#" -ne 0 ]
+do
+ case "$1" in
+ "") ;;
+ "-h"|"--help") tool_usage ;;
+ "-v"|"--verbose") VERB="-v" ;;
+ "-n"|"--dry-run") DRY=1 ;;
+ "-f"|"--force") FORCE="-f" ;;
+ "-e"|"--ext-offline") EXTOFF=1 ;;
+ "-y"|"--yes") YES="-y" ;;
+ "-l"|"--lvresize") DO_LVRESIZE=1 ;;
+ "check") CHECK="$2" ; shift ;;
+ "resize") RESIZE="$2"; NEWSIZE="$3" ; shift 2 ;;
+ *) error "Wrong argument \"$1\". (see: $TOOL --help)"
+ esac
+ shift
+done
+
+if [ -n "$CHECK" ]; then
+ check "$CHECK"
+elif [ -n "$RESIZE" ]; then
+ export FSADM_RUNNING="fsadm"
+ resize "$RESIZE" "$NEWSIZE"
+else
+ error "Missing command. (see: $TOOL --help)"
+fi
--- /dev/null
+#!/bin/bash
+#$Header: /sourceware/projects/lvm2-home/cvsfiles/LVM2/scripts/last_cvs_update.sh,v 1.2 2010/05/14 11:33:21 zkabelac Exp $
+################################################################################
+##
+## Copyright 2001 Sistina Software, Inc.
+##
+## This is free software released under the GNU General Public License.
+## There is no warranty for this software. See the file COPYING for
+## details.
+##
+## See the file CONTRIBUTORS for a list of contributors.
+##
+## This file is maintained by:
+## AJ Lewis <lewis@sistina.com>
+##
+## File name: last_cvs_update.sh
+##
+## Description: displays the last file updated by CVS commands and the date
+## it was updated. May be given a relative or absolute path.
+## Based on this information, you should be able to do a
+## cvs co -D $date GFS and get the same version of the source
+## tree as this tool was run on.
+##
+## Will also give you the CVS tag the source tree is based off
+## off when appropriate
+##
+## Output format:
+## [Tag: $TAG]
+## The last file updated by CVS was:
+## $path/$file
+## on
+## $date
+##
+################################################################################
+
+if [[ -z $1 ]];
+ then path=.;
+else
+ if [[ $1 == "-h" ]];
+ then echo "usage: $0 [ -h | path ]"
+ exit 0;
+ else
+ if [[ -d $1 ]]
+ then path=$1;
+ else
+ echo "usage: $0 [ -h | path ]"
+ exit 0;
+ fi
+ fi
+fi
+
+# grab the tag from the path passed in
+if [[ -f $path/CVS/Tag ]];
+ then echo "Tag: " `cat $path/CVS/Tag | sed -e 's/^[NT]//'`
+fi
+
+awk '
+BEGIN {
+ FS = "/"
+}
+{
+ # find the path for the Entries file
+ path = FILENAME
+ sub(/^\.\//, "", path)
+
+ # remove the CVS part of it
+ sub(/CVS\/Entries/, "", path)
+
+ # add the path the the filename that was modified, and put the date it was
+ # modified in as well
+ print path $2 " " $4
+
+}' `find $path -name "Entries" -printf "%h/%f "` | awk '
+# converts string name of month the a numeral
+function cvt_month(month) {
+ if(month == "Jan")
+ return 0
+ if(month == "Feb")
+ return 1
+ if(month == "Mar")
+ return 2
+ if(month == "Apr")
+ return 3
+ if(month == "May")
+ return 4
+ if(month == "Jun")
+ return 5
+ if(month == "Jul")
+ return 6
+ if(month == "Aug")
+ return 7
+ if(month == "Sep")
+ return 8
+ if(month == "Oct")
+ return 9
+ if(month == "Nov")
+ return 10
+ if(month == "Dec")
+ return 11
+ return -1
+}
+BEGIN {
+ FS = " "
+ latest=""
+ maxyear = 0
+ maxdate = 0
+ maxmonth = "Jan"
+ maxtime = "00:00:00"
+}
+{
+ # check year first
+ if (maxyear < $6) {
+ date = $2 " " $3 " " $4 " " $5 " " $6
+ file = $1
+ maxyear = $6
+ maxmonth = $3
+ maxdate = $4
+ maxtime = $5
+ }
+ else {
+ if (maxyear == $6) {
+ # then month if year is the same
+ if (cvt_month(maxmonth) < cvt_month($3)) {
+ date = $2 " " $3 " " $4 " " $5 " " $6
+ file = $1
+ maxmonth = $3
+ maxdate = $4
+ maxtime = $5
+ }
+ else {
+ if (cvt_month(maxmonth) == cvt_month($3)) {
+ #then date if month is the same
+ if (maxdate < $4) {
+ date = $2 " " $3 " " $4 " " $5 " " $6
+ file = $1
+ maxdate = $4
+ maxtime = $5
+ }
+ else {
+ if (maxdate == $4) {
+ # then time if date is the same
+ if (maxtime < $5) {
+ date = $2 " " $3 " " $4 " " $5 " " $6
+ file = $1
+ maxtime = $5
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+END {
+ # strip leading "./" from filename
+ sub(/^\.\//, "", file)
+ print "The last file updated by CVS was:"
+ print file
+ print "on"
+ print date " GMT"
+}'
+
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2007-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# This file is part of LVM2.
+# It is required for the proper handling of failures of LVM2 mirror
+# devices that were created using the -m option of lvcreate.
+#
+#
+# chkconfig: 12345 02 99
+# description: Starts and stops dmeventd monitoring for lvm2
+#
+# For Red-Hat-based distributions such as Fedora, RHEL, CentOS.
+#
+### BEGIN INIT INFO
+# Provides: lvm2-monitor
+# Required-Start: $local_fs
+# Required-Stop: $local_fs
+# Default-Start: 1 2 3 4 5
+# Default-Stop: 0 6
+# Short-Description: Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling
+### END INIT INFO
+
+. /etc/init.d/functions
+
+DAEMON=lvm2-monitor
+
+exec_prefix=@exec_prefix@
+sbindir=@sbindir@
+
+VGCHANGE=${sbindir}/vgchange
+VGS=${sbindir}/vgs
+
+LOCK_FILE="/var/lock/subsys/$DAEMON"
+
+WARN=1
+
+start()
+{
+ ret=0
+ # TODO do we want to separate out already active groups only?
+ VGSLIST=`$VGS --noheadings -o name 2> /dev/null`
+ for vg in $VGSLIST
+ do
+ action "Starting monitoring for VG $vg:" $VGCHANGE --monitor y --poll y $vg || ret=$?
+ done
+
+ return $ret
+}
+
+
+stop()
+{
+ ret=0
+ # TODO do we want to separate out already active groups only?
+ if test "$WARN" = "1"; then
+ echo "Not stopping monitoring, this is a dangerous operation. Please use force-stop to override."
+ return 1
+ fi
+ VGSLIST=`$VGS --noheadings -o name 2> /dev/null`
+ for vg in $VGSLIST
+ do
+ action "Stopping monitoring for VG $vg:" $VGCHANGE --monitor n $vg || ret=$?
+ done
+ return $ret
+}
+
+rtrn=1
+
+# See how we were called.
+case "$1" in
+ start)
+ start
+ rtrn=$?
+ [ $rtrn = 0 ] && touch $LOCK_FILE
+ ;;
+
+ force-stop)
+ WARN=0
+ stop
+ rtrn=$?
+ [ $rtrn = 0 ] && rm -f $LOCK_FILE
+ ;;
+
+ stop)
+ test "$runlevel" = "0" && WARN=0
+ test "$runlevel" = "6" && WARN=0
+ stop
+ rtrn=$?
+ [ $rtrn = 0 ] && rm -f $LOCK_FILE
+ ;;
+
+ restart)
+ WARN=0
+ if stop
+ then
+ start
+ fi
+ rtrn=$?
+ ;;
+
+ status)
+ # TODO anyone with an idea how to dump monitored volumes?
+ ;;
+
+ *)
+ echo $"Usage: $0 {start|stop|restart|status|force-stop}"
+ ;;
+esac
+
+exit $rtrn
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# This file is part of LVM2.
+# It is required for the proper handling of failures of LVM2 mirror
+# devices that were created using the -m option of lvcreate.
+#
+#
+# chkconfig: 12345 02 99
+# description: Starts and stops dmeventd monitoring for lvm2
+#
+### BEGIN INIT INFO
+# Provides:
+### END INIT INFO
+
+. /etc/init.d/functions
+
+VGCHANGE="/usr/sbin/vgchange"
+WARN=1
+
+start()
+{
+ ret=0
+ # TODO do we want to separate out already active groups only?
+ VGS=`vgs --noheadings -o name 2> /dev/null`
+ for vg in $VGS
+ do
+ action "Starting monitoring for VG $vg:" $VGCHANGE --monitor y $vg || ret=$?
+ done
+
+ return $ret
+}
+
+
+stop()
+{
+ ret=0
+ # TODO do we want to separate out already active groups only?
+ if test "$WARN" = "1"; then
+ echo "Not stopping monitoring, this is a dangerous operation. Please use force-stop to override."
+ return 1
+ fi
+ VGS=`vgs --noheadings -o name 2> /dev/null`
+ for vg in $VGS
+ do
+ action "Stopping monitoring for VG $vg:" $VGCHANGE --monitor n $vg || ret=$?
+ done
+ return $ret
+}
+
+result=1
+
+# See how we were called.
+case "$1" in
+ start)
+ start
+ result=$?
+ ;;
+
+ force-stop)
+ WARN=0
+ stop
+ result=$?
+ ;;
+
+ stop)
+ test "$runlevel" = "0" && WARN=0
+ test "$runlevel" = "6" && WARN=0
+ stop
+ result=$?
+ ;;
+
+ restart)
+ WARN=0
+ if stop
+ then
+ start
+ fi
+ result=$?
+ ;;
+
+ status)
+ # TODO anyone with an idea how to dump monitored volumes?
+ ;;
+
+ *)
+ echo $"Usage: $0 {start|stop|restart|status|force-stop}"
+ ;;
+esac
+
+exit $result
--- /dev/null
+all:
+ echo "Nothing to do for make all"
+
+manpage:
+ pod2man --center="create LVM2 initrd" --name='lvm2create_initrd' --section=8 -r 'lvm2create_initrd' ./lvm2create_initrd.pod > lvm2create_initrd.8
+
--- /dev/null
+http://poochiereds.net/svn/lvm2/
+
+This is the lvm2create_initrd script written by Miguel Cabeca, with some small
+modifications by myself.
+
+Here are some other requirements and tips for using it:
+
+1) this script uses busybox on the initrd image, hence busybox needs to be
+installed when you create your initrd.
+
+2) Make sure /etc/lvm/lvm.conf is set up correctly before running this. In
+particular, if you're using LVM on RAID, make sure that you have a filter that
+excludes the RAID component devices (this may not be necessary with the latest
+patch by Luca Berra, but it doesn't hurt).
+
+3) This initrd image does not support modules. If you need to plug in any
+kernel modules during the initrd phase, then you'll need to hand-modify the
+image.
+
+4) The generated initrd image supports an 'lvm2rescue' mode as well. If you add
+the parameter 'lvmrescue' on the kernel command line, it will run a shell at
+the end of the initrd 'init' script. This can be helpful when trying to fix a
+corrupt root volume or root LVM2 volume group.
+
+5) No userspace md tools are installed, so if you're using LVM on RAID, then
+you'll probably want to mark your RAID partitions as type 'fd' so that the
+kernel will start them automagically (or hand-modify the image).
+
+6) I'm not sure if devfs will work with this or not. udev, however does work,
+and is recommended. Because the dm-* devices use dynamically allocated major
+and minor numbers, kernel upgrades and the like can renumber your devices. To
+fix this, you need to run a 'vgscan --mknodes' prior to fscking and mounting
+your rootfs. Doing this with a static /dev creates a problem though -- you
+will be modifying the root filesystem before it has been fsck'ed. udev gets
+around this by mounting a ramdisk over /dev, but you'll probably need to add
+a startup script that creates devices in /dev. The lvm2udev script in this
+directory is an example of such a beast.
+
+--
+Jeffrey Layton <jtlayton@poochiereds.net>
--- /dev/null
+#!/bin/bash
+#
+# lvm2create_initrd
+#
+# Miguel Cabeca
+# cabeca (at) ist (dot) utl (dot) pt
+#
+# Inspiration to write this script came from various sources
+#
+# Original LVM lvmcreate_initrd: ftp://ftp.sistina.com/pub/LVM/1.0/
+# Kernel initrd.txt: http://www.kernel.org/
+# EVMS INSTALL.initrd & linuxrc: http://evms.sourceforge.net/
+# Jeffrey Layton's lvm2create_initrd: http://poochiereds.net/svn/lvm2create_initrd/
+# Christophe Saout's initrd & linuxrc: http://www.saout.de/misc/
+#
+# This script was only tested with kernel 2.6 with everything required to boot
+# the root filesystem built-in (not as modules). Ex: SCSI or IDE, RAID, device mapper
+# It does not support devfs as it is deprecated in the 2.6 kernel series
+#
+# It needs lvm2 tools, busybox, pivot_root, MAKEDEV
+#
+# It has been tested on Debian sid (unstable) only
+#
+# Changelog
+# 26/02/2004 Initial release -- Miguel Cabeca
+# 27/02/2004 Removed the BUSYBOXSYMLINKS var. The links are now determined at runtime.
+# some changes in init script to call a shell if something goes wrong. -- Miguel Cabeca
+# 19/04/2004 Several small changes. Pass args to init so single user mode works. Add some
+# PATH entries to /sbin/init shell script so chroot works without /usr mounted. Remove
+# mkdir /initrd so we don't cause problems if root filesystem is corrupted. -- Jeff Layton
+# 15/05/2004 initial support for modules, create lvm.conf from lvm dumpconfig, other cleanups -- Jeff Layton
+# 14/11/2006 Update handling of ldd output to handle hardcoded library links and virtual dll linux-gate.
+# Add support for Gentoo-style MAKEDEV. Remove hardcoded BINUTILS paths -- Douglas Mayle
+#
+# Copyright Miguel Cabeca, Jeffrey Layton, 2004
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# $Id: lvm2create_initrd,v 1.2 2006/11/21 22:41:56 agk Exp $
+
+TMPMNT=/tmp/mnt.$$
+DEVRAM=/tmp/initrd.$$
+
+# set defaults
+BINFILES=${BINFILES:-"`which lvm` `which bash` `which busybox` `which pivot_root`"}
+BASICDEVICES=${BASICDEVICES:-"std consoleonly fd"}
+BLOCKDEVICES=${BLOCKDEVICES:-"md hda hdb hdc hdd sda sdb sdc sdd"}
+MAKEDEV=${MAKEDEV:-"debian"}
+
+# Uncomment this if you want to disable automatic size detection
+#INITRDSIZE=4096
+
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:$PATH
+
+usage () {
+ echo "Create an initial ramdisk image for LVM2 root filesystem"
+ echo "$cmd: [-h] [-v] [-c lvm.conf] [-m modulelist] [-e extrafiles] -r [raiddevs] [-R mdadm.conf] [-M style] [kernel version]"
+ echo " -h|--help print this usage message"
+ echo " -v|--verbose verbose progress messages"
+ echo " -c|--lvmconf path to lvm.conf (/etc/lvm/lvm.conf)"
+ echo " -m|--modules modules to copy to initrd image"
+ echo " -e|--extra extra files to add to initrd"
+ echo " -r|--raid raid devices to start in initrd"
+ echo " -R|--raidconf location of mdadm.conf file to include"
+ echo " -M|--makedev set MAKEDEV type (debian or redhat)"
+}
+
+verbose () {
+ [ "$VERBOSE" ] && echo "`echo $cmd | tr '[a-z0-9/_]' ' '` -- $1" || true
+}
+
+cleanup () {
+ [ "`mount | grep $DEVRAM`" ] && verbose "unmounting $DEVRAM" && umount $DEVRAM
+ [ -f $DEVRAM ] && verbose "removing $DEVRAM" && rm $DEVRAM
+ [ -d $TMPMNT ] && verbose "removing $TMPMNT" && rmdir $TMPMNT
+ verbose "exit with code $1"
+ exit $1
+}
+
+trap "
+ verbose 'Caught interrupt'
+ echo 'Bye bye...'
+ cleanup 1
+" 1 2 3 15
+
+create_init () {
+ cat << 'INIT' > $TMPMNT/sbin/init
+#!/bin/bash
+
+# include in the path some dirs from the real root filesystem
+# for chroot, blockdev
+PATH="/sbin:/bin:/usr/sbin:/usr/bin:/lib/lvm-200:/initrd/bin:/initrd/sbin"
+PRE="initrd:"
+
+do_shell(){
+ /bin/echo
+ /bin/echo "*** Entering LVM2 rescue shell. Exit shell to continue booting. ***"
+ /bin/echo
+ /bin/bash
+}
+
+echo "$PRE Remounting / read/write"
+mount -t ext2 -o remount,rw /dev/ram0 /
+
+
+# We need /proc for device mapper
+echo "$PRE Mounting /proc"
+mount -t proc none /proc
+
+# plug in modules listed in /etc/modules
+if [ -f /etc/modules ]; then
+ echo -n "$PRE plugging in kernel modules:"
+ cat /etc/modules |
+ while read module; do
+ echo -n " $module"
+ modprobe $module
+ done
+ echo '.'
+fi
+
+# start raid devices if raid_autostart file exists
+if [ -f /etc/raid_autostart ]; then
+ if [ ! -f /etc/mdadm/mdadm.conf ]; then
+ mdoptions='--super-minor=dev'
+ fi
+ cat /etc/raid_autostart|
+ while read dev; do
+ echo "Starting RAID device $dev"
+ /sbin/mdadm --assemble $dev $mdoptions
+ done
+fi
+
+# Create the /dev/mapper/control device for the ioctl
+# interface using the major and minor numbers that have been allocated
+# dynamically.
+
+echo -n "$PRE Finding device mapper major and minor numbers "
+
+MAJOR=$(sed -n 's/^ *\([0-9]\+\) \+misc$/\1/p' /proc/devices)
+MINOR=$(sed -n 's/^ *\([0-9]\+\) \+device-mapper$/\1/p' /proc/misc)
+if test -n "$MAJOR" -a -n "$MINOR" ; then
+ mkdir -p -m 755 /dev/mapper
+ mknod -m 600 /dev/mapper/control c $MAJOR $MINOR
+fi
+
+echo "($MAJOR,$MINOR)"
+
+# Device-Mapper dynamically allocates all device numbers. This means it is possible
+# that the root volume specified to LILO or Grub may have a different number when the
+# initrd runs than when the system was last running. In order to make sure the
+# correct volume is mounted as root, the init script must determine what the
+# desired root volume name is by getting the LVM2 root volume name from the kernel command line. In order for
+# this to work correctly, "lvm2root=/dev/Volume_Group_Name/Root_Volume_Name" needs to be passed
+# to the kernel command line (where Root_Volume_Name is replaced by your actual
+# root volume's name.
+for arg in `cat /proc/cmdline`; do
+ echo $arg | grep '^lvm2root=' > /dev/null
+ if [ $? -eq 0 ]; then
+ rootvol=${arg#lvm2root=}
+ break
+ fi
+done
+
+echo "$PRE Activating LVM2 volumes"
+
+
+# run a shell if we're passed lvm2rescue on commandline
+grep lvm2rescue /proc/cmdline 1>/dev/null 2>&1
+if [ $? -eq 0 ]; then
+ lvm vgchange --ignorelockingfailure -P -a y
+ do_shell
+else
+ lvm vgchange --ignorelockingfailure -a y
+fi
+
+echo "$PRE Mounting root filesystem $rootvol ro"
+mkdir /rootvol
+if ! mount -t auto -o ro $rootvol /rootvol; then
+ echo "\t*FAILED*";
+ do_shell
+fi
+
+echo "$PRE Umounting /proc"
+umount /proc
+
+echo "$PRE Changing roots"
+cd /rootvol
+if ! pivot_root . initrd ; then
+ echo "\t*FAILED*"
+ do_shell
+fi
+
+echo "$PRE Proceeding with boot..."
+
+exec chroot . /bin/sh -c "umount /initrd; blockdev --flushbufs /dev/ram0 ; exec /sbin/init $*" < dev/console > dev/console 2>&1
+
+INIT
+ chmod 555 $TMPMNT/sbin/init
+}
+
+# create lvm.conf file from dumpconfig. Just use filter options
+create_lvmconf () {
+ echo 'devices {' > $TMPMNT/etc/lvm/lvm.conf
+ lvm dumpconfig | grep 'filter=' >> $TMPMNT/etc/lvm/lvm.conf
+ echo '}' >> $TMPMNT/etc/lvm/lvm.conf
+}
+
+#
+# Main
+#
+
+cmd=`basename $0`
+
+VERSION=`uname -r`
+
+while [ $# -gt 0 ]; do
+ case $1 in
+ -h|--help) usage; exit 0;;
+ -v|--verbose) VERBOSE="y";;
+ -c|--lvmconf) LVMCONF=$2; shift;;
+ -m|--modules) MODULES=$2; shift;;
+ -e|--extra) EXTRAFILES=$2; shift;;
+ -r|--raid) RAID=$2; shift;;
+ -R|--raidconf) RAIDCONF=$2; shift;;
+ -M|--makedev) MAKEDEV=$2; shift;;
+ [2-9].[0-9]*.[0-9]*) VERSION=$1;;
+ *) echo "$cmd -- invalid option '$1'"; usage; exit 0;;
+ esac
+ shift
+done
+
+INITRD=${INITRD:-"/boot/initrd-lvm2-$VERSION.gz"}
+
+echo "$cmd -- make LVM initial ram disk $INITRD"
+echo ""
+
+if [ -n "$RAID" ]; then
+ BINFILES="$BINFILES /sbin/mdadm"
+ RAIDCONF=${RAIDCONF:-"/etc/mdadm/mdadm.conf"}
+ if [ -r $RAIDCONF ]; then
+ EXTRAFILES="$EXTRAFILES $RAIDCONF"
+ else
+ echo "$cmd -- WARNING: No $RAIDCONF! Your RAID device minor numbers must match their superblock values!"
+ fi
+fi
+
+# add modprobe if we declared any modules
+if [ -n "$MODULES" ]; then
+ BINFILES="$BINFILES /sbin/modprobe /sbin/insmod /sbin/rmmod"
+fi
+
+for a in $BINFILES $EXTRAFILES; do
+ if [ ! -r "$a" ] ; then
+ echo "$cmd -- ERROR: you need $a"
+ exit 1;
+ fi;
+done
+
+# Figure out which shared libraries we actually need in our initrd
+echo "$cmd -- finding required shared libraries"
+verbose "BINFILES: `echo $BINFILES`"
+
+# We need to strip certain lines from ldd output. This is the full output of an example ldd:
+#lvmhost~ # ldd /sbin/lvm /bin/bash
+#/sbin/lvm:
+# not a dynamic executable
+#/bin/bash:
+# linux-gate.so.1 => (0xbfffe000)
+# libncurses.so.5 => /lib/libncurses.so.5 (0xb7ee3000)
+# libdl.so.2 => /lib/libdl.so.2 (0xb7edf000)
+# libc.so.6 => /lib/libc.so.6 (0xb7dc1000)
+# /lib/ld-linux.so.2 (0xb7f28000)
+#
+# 1) Lines with a ":" contain the name of the original binary we're examining, and so are unnecessary.
+# We need to strip them because they contain "/", and can be confused with links with a hardcoded path.
+# 2) The linux-gate library is a virtual dll that does not exist on disk, but is instead loaded automatically
+# into the process space, and can't be copied to the ramdisk
+#
+# After these lines have been stripped, we're interested in the lines remaining if they
+# 1) Contain "=>" because they are pathless links, and the value following the token is the path on the disk
+# 2) Contain "/" because it's a link with a hardcoded path, and so we're interested in the link itself.
+LIBFILES=`ldd $BINFILES 2>/dev/null |grep -v -E \(linux-gate\|:\) | awk '{if (/=>/) { print $3 } else if (/\//) { print $1 }}' | sort -u`
+if [ $? -ne 0 ]; then
+ echo "$cmd -- ERROR figuring out needed shared libraries"
+ exit 1
+fi
+
+verbose "Shared libraries needed: `echo $LIBFILES`"
+
+INITRDFILES="$BINFILES $LIBFILES $MODULES $EXTRAFILES"
+
+# tack on stuff for modules if we declared any and the files exist
+if [ -n "$MODULES" ]; then
+ if [ -f "/etc/modprobe.conf" ]; then
+ INITRDFILES="$INITRDFILES /etc/modprobe.conf"
+ fi
+ if [ -f "/lib/modules/modprobe.conf" ]; then
+ INITRDFILES="$INITRDFILES /lib/modules/modprobe.conf"
+ fi
+fi
+
+# Calculate the the size of the ramdisk image.
+# Don't forget that inodes take up space too, as does the filesystem metadata.
+echo "$cmd -- calculating initrd filesystem parameters"
+if [ -z "$INITRDSIZE" ]; then
+ echo "$cmd -- calculating loopback file size"
+ verbose "finding size"
+ INITRDSIZE="`du -Lck $INITRDFILES | tail -1 | cut -f 1`"
+ verbose "minimum: $INITRDSIZE kB for files + inodes + filesystem metadata"
+ INITRDSIZE=`expr $INITRDSIZE + 512` # enough for ext2 fs + a bit
+fi
+
+echo "$cmd -- making loopback file ($INITRDSIZE kB)"
+verbose "using $DEVRAM as a temporary loopback file"
+dd if=/dev/zero of=$DEVRAM count=$INITRDSIZE bs=1024 > /dev/null 2>&1
+if [ $? -ne 0 ]; then
+ echo "$cmd -- ERROR creating loopback file"
+ cleanup 1
+fi
+
+echo "$cmd -- making ram disk filesystem"
+verbose "mke2fs -F -m0 -L LVM-$VERSION $DEVRAM $INITRDSIZE"
+[ "$VERBOSE" ] && OPT_Q="" || OPT_Q="-q"
+mke2fs $OPT_Q -F -m0 -L LVM-$VERSION $DEVRAM $INITRDSIZE
+if [ $? -ne 0 ]; then
+ echo "$cmd -- ERROR making ram disk filesystem"
+ echo "$cmd -- ERROR you need to use mke2fs >= 1.14 or increase INITRDSIZE"
+ cleanup 1
+fi
+
+verbose "creating mountpoint $TMPMNT"
+mkdir $TMPMNT
+if [ $? -ne 0 ]; then
+ echo "$cmd -- ERROR making $TMPMNT"
+ cleanup 1
+fi
+
+echo "$cmd -- mounting ram disk filesystem"
+verbose "mount -o loop $DEVRAM $TMPMNT"
+mount -oloop $DEVRAM $TMPMNT
+if [ $? -ne 0 ]; then
+ echo "$cmd -- ERROR mounting $DEVRAM on $TMPMNT"
+ cleanup 1
+fi
+
+verbose "creating basic set of directories in $TMPMNT"
+(cd $TMPMNT; mkdir bin dev etc lib proc sbin var)
+if [ $? -ne 0 ]; then
+ echo "$cmd -- ERROR creating directories in $TMPMNT"
+ cleanup 1
+fi
+
+# Add some /dev files. We have to handle different types of MAKEDEV invocations
+# here, so this is rather messy.
+RETCODE=0
+echo "$cmd -- adding required /dev files"
+verbose "BASICDEVICES: `echo $BASICDEVICES`"
+verbose "BLOCKDEVICES: `echo $BLOCKDEVICES`"
+[ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q=""
+case "$MAKEDEV" in
+debian)
+ (cd $TMPMNT/dev; /dev/MAKEDEV $OPT_Q $BASICDEVICES $BLOCKDEVICES)
+ RETCODE=$?
+ ;;
+redhat)
+ (cd $TMPMNT/dev; /dev/MAKEDEV $OPT_Q -d $TMPMNT/dev -m 2)
+ RETCODE=$?
+ ;;
+gentoo)
+ (cd $TMPMNT/dev; /usr/sbin/MAKEDEV $OPT_Q $BASICDEVICES $BLOCKDEVICES)
+ RETCODE=$?
+ ;;
+*)
+ echo "$cmd -- ERROR: $MAKEDEV is not a known MAKEDEV style."
+ RETCODE=1
+ ;;
+esac
+
+
+if [ $RETCODE -ne 0 ]; then
+ echo "$cmd -- ERROR adding /dev files"
+ cleanup 1
+fi
+
+
+# copy necessary files to ram disk
+echo "$cmd -- copying initrd files to ram disk"
+[ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q="--quiet"
+verbose "find \$INITRDFILES | cpio -pdmL $OPT_Q $TMPMNT"
+find $INITRDFILES | cpio -pdmL $OPT_Q $TMPMNT
+if [ $? -ne 0 ]; then
+ echo "$cmd -- ERROR cpio to ram disk"
+ cleanup 1
+fi
+
+
+echo "$cmd -- creating symlinks to busybox"
+shopt -s extglob
+[ "$VERBOSE" ] && OPT_Q="-v" || OPT_Q=""
+BUSYBOXSYMLINKS=`busybox 2>&1| awk '/^Currently defined functions:$/ {i++;next} i'|tr ',\t\n' ' '`
+for link in ${BUSYBOXSYMLINKS//@(linuxrc|init|busybox)}; do
+ ln -s $OPT_Q busybox $TMPMNT/bin/$link;
+done
+shopt -u extglob
+
+echo "$cmd -- creating new $TMPMNT/sbin/init"
+create_init
+if [ $? -ne 0 ]; then
+ echo "$cmd -- ERROR creating init"
+ cleanup
+ exit 1
+fi
+
+# copy LVMCONF into place or create a stripped down one from lvm dumpconfig
+mkdir -p $TMPMNT/etc/lvm
+if [ -n "$LVMCONF" ]; then
+ echo "$cmd -- copying $LVMCONF to $TMPMNT/etc/lvm/lvm.conf"
+ if [ -f "$LVMCONF" ]; then
+ cp $LVMCONF $TMPMNT/etc/lvm/lvm.conf
+ else
+ echo "$cmd -- ERROR: $LVMCONF does not exist!"
+ cleanup
+ exit 1
+ fi
+else
+ echo "$cmd -- creating new $TMPMNT/etc/lvm/lvm.conf"
+ create_lvmconf
+fi
+
+if [ -n "$RAID" ]; then
+ RAIDLIST="$TMPMNT/etc/raid_autostart"
+ echo "$cmd -- creating $RAIDLIST file."
+ for device in $RAID; do
+ echo $device >> $RAIDLIST
+ done
+fi
+
+# create modules.dep and /etc/modules files if needed
+if [ -n "$MODULES" ]; then
+ echo "$cmd -- creating $MODDIR/modules.dep file and $TMPMNT/etc/modules"
+ depmod -b $TMPMNT $VERSION
+ for module in $MODULES; do
+ basename $module | sed 's/\.k\{0,1\}o$//' >> $TMPMNT/etc/modules
+ done
+fi
+
+verbose "removing $TMPMNT/lost+found"
+rmdir $TMPMNT/lost+found
+
+echo "$cmd -- ummounting ram disk"
+umount $DEVRAM
+if [ $? -ne 0 ]; then
+ echo "$cmd -- ERROR umounting $DEVRAM"
+ cleanup 1
+fi
+
+echo "$cmd -- creating compressed initrd $INITRD"
+verbose "dd if=$DEVRAM bs=1k count=$INITRDSIZE | gzip -9"
+dd if=$DEVRAM bs=1k count=$INITRDSIZE 2>/dev/null | gzip -9 > $INITRD
+if [ $? -ne 0 ]; then
+ echo "$cmd -- ERROR creating $INITRD"
+ cleanup 1
+fi
+
+
+cat << FINALTXT
+--------------------------------------------------------
+Your initrd is ready in $INITRD
+
+Don't forget to set root=/dev/ram0 in kernel parameters
+Don't forget to set lvm2root=/dev/VG/LV in kernel parameters, where LV is your root volume
+If you use lilo try adding/modifying an entry similar to this one in lilo.conf:
+
+image=/boot/vmlinuz-lvm2-$VERSION
+ label="ramdisk_LVM"
+ initrd=/boot/initrd-lvm2-$VERSION.gz
+ append="root=/dev/ram0 lvm2root=/dev/system/root <other parameters>"
+
+If using grub try adding/modifying an entry similar to this one in menu.lst
+
+title ramdisk LVM
+ kernel /boot/vmlinuz-lvm2-$VERSION root=/dev/ram0 lvm2root=/dev/system/root <other parameters>
+ initrd /boot/initrd-lvm2-$VERSION.gz
+
+You can also pass lvm2rescue to the kernel to get a shell
+--------------------------------------------------------
+FINALTXT
+
+cleanup 0
+
--- /dev/null
+.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.14
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. | will give a
+.\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to
+.\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C'
+.\" expand to `' in nroff, nothing in troff, for use with C<>.
+.tr \(*W-|\(bv\*(Tr
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` ""
+. ds C' ""
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+'br\}
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
+.\" entries marked with X<> in POD. Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.if \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. nr % 0
+. rr F
+.\}
+.\"
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.hy 0
+.if n .na
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear. Run. Save yourself. No user-serviceable parts.
+. \" fudge factors for nroff and troff
+.if n \{\
+. ds #H 0
+. ds #V .8m
+. ds #F .3m
+. ds #[ \f1
+. ds #] \fP
+.\}
+.if t \{\
+. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+. ds #V .6m
+. ds #F 0
+. ds #[ \&
+. ds #] \&
+.\}
+. \" simple accents for nroff and troff
+.if n \{\
+. ds ' \&
+. ds ` \&
+. ds ^ \&
+. ds , \&
+. ds ~ ~
+. ds /
+.\}
+.if t \{\
+. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+. \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+. \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+. \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+. ds : e
+. ds 8 ss
+. ds o a
+. ds d- d\h'-1'\(ga
+. ds D- D\h'-1'\(hy
+. ds th \o'bp'
+. ds Th \o'LP'
+. ds ae ae
+. ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "lvm2create_initrd 8"
+.TH lvm2create_initrd 8 "2004-06-05" "lvm2create_initrd" "create LVM2 initrd"
+.SH "NAME"
+lvm2create_initrd \- create initrd image for booting to root\e\-on\e\-LVM2
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+\&\fBlvm2create_initrd\fR [ \fB\-h|\-\-help\fR ] [ \fB\-v|\-\-verbose\fR ] [ \fB\-c|\-\-lvmconf\fR \fI/path/to/lvm.conf\fR ] [ \fB\-m|\-\-modules\fR "\fImodule1 module2 ...\fR" ] [ \fB\-e|\-\-extra\fR "\fIfile1 file2 ...\fR" ] [ \fB\-r|\-\-raid\fR "\fI/dev/md1 /dev/md2 ...\fR" ]
+[ \fB\-R|\-\-raidconf\fR \fI/path/to/mdadm.conf\fR ] [ \fB\-M|\-\-makedev\fR \fIstyle\fR ]
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+lvm2create_initrd creates an initial ramdisk (initrd) image suitable for booting to system that has an \s-1LVM2\s0 volume as its root filesystem.
+.PP
+To boot to such a setup, you'll
+either need a bootloader that understands \s-1LVM2\s0 volumes, or you'll need a
+filesystem on a regular volume to act as a boot partition (typically mounted
+on /boot).
+.PP
+The resulting initrd image is fairly full\-featured. It can harbor and load
+kernel modules, start \s-1MD\s0 devices, and boot to a shell to perform rescue
+operations.
+.Sh "Booting to your initrd Image:"
+.IX Subsection "Booting to your initrd Image:"
+The filesystem image created is an ext2fs filesystem, hence your kernel must have
+ext2fs built into it statically in order to boot to the image.
+.PP
+Once you create your initrd image, you must pass the correct options to the kernel when
+you boot using it. Your kernel command line should look something like this:
+.PP
+\&\fBroot=/dev/ram0 lvm2root=/dev/rootvg/root [ lvm2rescue ]\fR
+.PP
+of course there may be other options.
+.IP "\fBroot=/dev/ram0\fR" 4
+.IX Item "root=/dev/ram0"
+This option is required. It tells the kernel that the root filesystem should initially
+be set to the ramdisk (/dev/ram0).
+.IP "\fBlvm2root=/dev/rootvg/root\fR" 4
+.IX Item "lvm2root=/dev/rootvg/root"
+This option is also required. It tells the initrd image which \s-1LVM2\s0 device the root filesystem is located on.
+.IP "\fBlvm2rescue\fR" 4
+.IX Item "lvm2rescue"
+Causes the initrd image to run a shell prior to mounting the root filesystem. This is
+helpful in disaster situations where your initrd image is accessable, but there is
+a problem with the root filesystem (corrupted image, incorrect device setup, etc.). This
+option is (of course) optional.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+Most of parameters that can be set via command-line options can also be set
+via environment variables. Options specified on the command-line always take
+precedence.
+.IP "\fB\-h|\-\-help\fR" 4
+.IX Item "-h|--help"
+Display short help text and exit. If used, other options are ignored.
+.IP "\fB\-v|\-\-verbose\fR" 4
+.IX Item "-v|--verbose"
+Turn on extra verbosity for debugging, etc.
+.IP "\fB\-c|\-\-lvmconf\fR \fI/path/to/lvm.conf\fR" 4
+.IX Item "-c|--lvmconf /path/to/lvm.conf"
+Specify an lvm.conf file to include in the image. This is useful if you have
+special device filters or other options you wish to use during the initrd
+stage. If this option is not
+included, then a lvm.conf file is created that contains only the current
+device filter from an \fBlvm dumpconfig\fR. This can also be set via the \fB$LVMCONF\fR
+environment variable.
+.ie n .IP "\fB\-m|\-\-modules\fR ""\fI/path/to/module1.ko /path/to/module2.ko ...\fR""" 4
+.el .IP "\fB\-m|\-\-modules\fR ``\fI/path/to/module1.ko /path/to/module2.ko ...\fR''" 4
+.IX Item "-m|--modules ""/path/to/module1.ko /path/to/module2.ko ..."""
+Specify modules to include and plug in during the initrd phase. This option
+takes a quoted, space-separated list of modules. Full pathnames are required.
+These modules are loaded into the kernel early in the initrd phase of the boot
+process. The current modprobe.conf file is also copied to the initrd image
+as well. This can also be specified via the \fB$MODULES\fR environment variable.
+.ie n .IP "\fB\-e|\-\-extra\fR ""\fI/path/to/file1 /path/to/file2 ...\fR""" 4
+.el .IP "\fB\-e|\-\-extra\fR ``\fI/path/to/file1 /path/to/file2 ...\fR''" 4
+.IX Item "-e|--extra ""/path/to/file1 /path/to/file2 ..."""
+Extra files that should be included in the initrd image. These files will be
+copied to the same location in the initrd image that they are in the current
+filesystem. Again full pathnames are required. This can also be specified via
+the \fB$EXTRAFILES\fR environment variable.
+.ie n .IP "\fB\-r|\-\-raid\fR ""\fI/dev/md1 /dev/md2...\fR""" 4
+.el .IP "\fB\-r|\-\-raid\fR ``\fI/dev/md1 /dev/md2...\fR''" 4
+.IX Item "-r|--raid ""/dev/md1 /dev/md2..."""
+\&\s-1RAID\s0 devices to be started prior to scanning for \s-1LVM2\s0 volume groups. If this
+option is used then then \fBmdadm\fR program must be installed. This can also be
+specified via the \fB$RAID\fR environment variable.
+.ie n .IP "\fB\-R|\-\-raidconf\fR ""\fI/path/to/mdadm.conf\fR""" 4
+.el .IP "\fB\-R|\-\-raidconf\fR ``\fI/path/to/mdadm.conf\fR''" 4
+.IX Item "-R|--raidconf ""/path/to/mdadm.conf"""
+Location of a mdadm.conf file to include. If this is not specified, then no
+files are included, and any devices specified with the \fB\-r\fR option above
+must have minor numbers that match their superblock values. This can also be
+specified via the \fB$RAIDCONF\fR environment variable.
+.IP "\fB\-M|\-\-makedev\fR \fIstyle\fR" 4
+.IX Item "-M|--makedev style"
+Set \s-1MAKEDEV\s0 invocation style. The script currently supports 2 styles of
+\&\s-1MAKEDEV\s0 programs \fIdebian\fR and \fIredhat\fR. The default is \fIdebian\fR. Set
+to \fIredhat\fR if using the RedHat/Fedora binary \s-1MAKEDEV\s0 program. Please send
+a bug report to maintainer if your distrib doesn't work with any of the
+current options.
+.SH "ENVIRONMENT VARIABLES"
+.IX Header "ENVIRONMENT VARIABLES"
+Most of the options to this script can be set via environment variables. In
+situations where both are set, then the command-line options take precedence.
+.IP "\fB$LVMCONF\fR" 4
+.IX Item "$LVMCONF"
+Same as \-c option.
+.IP "\fB$MODULES\fR" 4
+.IX Item "$MODULES"
+Same as \-m option.
+.IP "\fB$EXTRAFILES\fR" 4
+.IX Item "$EXTRAFILES"
+Same as \-e option.
+.IP "\fB$RAID\fR" 4
+.IX Item "$RAID"
+Same as \-r option.
+.IP "\fB$RAIDCONF\fR" 4
+.IX Item "$RAIDCONF"
+Same as \-R option.
+.IP "\fB$MAKEDEV\fR" 4
+.IX Item "$MAKEDEV"
+Same as \-M option.
+.IP "\fB$BASICDEVICES\fR" 4
+.IX Item "$BASICDEVICES"
+Overrides the default value of \f(CW$BASICDEVICES\fR in the script (which is \*(L"std consoleonly fd\*(R"). These values are passed to the \fB\s-1MAKEDEV\s0\fR program to create device
+entries in the initrd image.
+.IP "\fB$BLOCKDEVICES\fR" 4
+.IX Item "$BLOCKDEVICES"
+Overrides the default value of \f(CW$BLOCKDEVICES\fR in the script (which is \*(L"md hda hdb hdc hdd sda sdb sdc sdd\*(R"). This value is passed to the \fB\s-1MAKEDEV\s0\fR program to
+create device entries in the initrd image.
+.IP "\fB$BINFILES\fR" 4
+.IX Item "$BINFILES"
+Overrides the default value of \f(CW$BINFILES\fR (which is \*(L"/lib/lvm\-200/lvm /bin/bash /bin/busybox /sbin/pivot_root\*(R"). The difference between using this and adding
+a file to the \f(CW$EXTRAFILES\fR list above is that libraries that these depend upon are also included. You can still use \f(CW$EXTRAFILES\fR to achieve the same effect, but
+you must resolve library dependencies youself.
+.IP "\fB$INITRDSIZE\fR" 4
+.IX Item "$INITRDSIZE"
+Force a particular size for your initrd image. The default is to total up the size of
+the included files and to add 512K as a buffer.
+.SH "BUGS"
+.IX Header "BUGS"
+I don't like having to specify a \-M option to set the \s-1MAKEDEV\s0 style, but I know
+of no way to reliably detect what type of \s-1MAKEDEV\s0 is being used. We'll probably
+have to add other \s-1MAKEDEV\s0 styles in the future as this script is tested on
+other distributions.
+.SH "AUTHORS"
+.IX Header "AUTHORS"
+The script was originally written by Miguel Cabeca, with significant
+improvements by Jeffrey Layton. Comments, bug reports and patches should be
+sent to Jeffrey Layton at \fBjtlayton@poochiereds.net\fR.
+.SH "SEE ALSO"
+.IX Header "SEE ALSO"
+\&\fB\s-1MAKEDEV\s0\fR(8), \fBmdadm\fR(8), \fBbusybox\fR(8), \fBlvm.conf\fR(5)
--- /dev/null
+=head1 NAME
+
+lvm2create_initrd - create initrd image for booting to root\-on\-LVM2
+
+=head1 SYNOPSIS
+
+B<lvm2create_initrd> [ B<-h|--help> ] [ B<-v|--verbose> ] [ B<-c|--lvmconf> I</path/to/lvm.conf> ] [ B<-m|--modules> "I<module1 module2 ...>" ] [ B<-e|--extra> "I<file1 file2 ...>" ] [ B<-r|--raid> "I</dev/md1 /dev/md2 ...>" ]
+[ B<-R|--raidconf> I</path/to/mdadm.conf> ] [ B<-M|--makedev> I<style> ]
+
+=head1 DESCRIPTION
+
+lvm2create_initrd creates an initial ramdisk (initrd) image suitable for booting to system that has an LVM2 volume as its root filesystem.
+
+To boot to such a setup, you'll
+either need a bootloader that understands LVM2 volumes, or you'll need a
+filesystem on a regular volume to act as a boot partition (typically mounted
+on /boot).
+
+The resulting initrd image is fairly full-featured. It can harbor and load
+kernel modules, start MD devices, and boot to a shell to perform rescue
+operations.
+
+=head2 Booting to your initrd Image:
+
+The filesystem image created is an ext2fs filesystem, hence your kernel must have
+ext2fs built into it statically in order to boot to the image.
+
+Once you create your initrd image, you must pass the correct options to the kernel when
+you boot using it. Your kernel command line should look something like this:
+
+B<root=/dev/ram0 lvm2root=/dev/rootvg/root [ lvm2rescue ]>
+
+of course there may be other options.
+
+=over
+
+=item B<root=/dev/ram0>
+
+This option is required. It tells the kernel that the root filesystem should initially
+be set to the ramdisk (/dev/ram0).
+
+=item B<lvm2root=/dev/rootvg/root>
+
+This option is also required. It tells the initrd image which LVM2 device the root filesystem is located on.
+
+=item B<lvm2rescue>
+
+Causes the initrd image to run a shell prior to mounting the root filesystem. This is
+helpful in disaster situations where your initrd image is accessable, but there is
+a problem with the root filesystem (corrupted image, incorrect device setup, etc.). This
+option is (of course) optional.
+
+=back
+
+=head1 OPTIONS
+
+Most of parameters that can be set via command-line options can also be set
+via environment variables. Options specified on the command-line always take
+precedence.
+
+=over
+
+=item B<-h|--help>
+
+Display short help text and exit. If used, other options are ignored.
+
+=item B<-v|--verbose>
+
+Turn on extra verbosity for debugging, etc.
+
+=item B<-c|--lvmconf> I</path/to/lvm.conf>
+
+Specify an lvm.conf file to include in the image. This is useful if you have
+special device filters or other options you wish to use during the initrd
+stage. If this option is not
+included, then a lvm.conf file is created that contains only the current
+device filter from an B<lvm dumpconfig>. This can also be set via the B<$LVMCONF>
+environment variable.
+
+=item B<-m|--modules> "I</path/to/module1.ko /path/to/module2.ko ...>"
+
+Specify modules to include and plug in during the initrd phase. This option
+takes a quoted, space-separated list of modules. Full pathnames are required.
+These modules are loaded into the kernel early in the initrd phase of the boot
+process. The current modprobe.conf file is also copied to the initrd image
+as well. This can also be specified via the B<$MODULES> environment variable.
+
+=item B<-e|--extra> "I</path/to/file1 /path/to/file2 ...>"
+
+Extra files that should be included in the initrd image. These files will be
+copied to the same location in the initrd image that they are in the current
+filesystem. Again full pathnames are required. This can also be specified via
+the B<$EXTRAFILES> environment variable.
+
+=item B<-r|--raid> "I</dev/md1 /dev/md2...>"
+
+RAID devices to be started prior to scanning for LVM2 volume groups. If this
+option is used then then B<mdadm> program must be installed. This can also be
+specified via the B<$RAID> environment variable.
+
+=item B<-R|--raidconf> "I</path/to/mdadm.conf>"
+
+Location of a mdadm.conf file to include. If this is not specified, then no
+files are included, and any devices specified with the B<-r> option above
+must have minor numbers that match their superblock values. This can also be
+specified via the B<$RAIDCONF> environment variable.
+
+=item B<-M|--makedev> I<style>
+
+Set MAKEDEV invocation style. The script currently supports 2 styles of
+MAKEDEV programs I<debian> and I<redhat>. The default is I<debian>. Set
+to I<redhat> if using the RedHat/Fedora binary MAKEDEV program. Please send
+a bug report to maintainer if your distrib doesn't work with any of the
+current options.
+
+=back
+
+=head1 ENVIRONMENT VARIABLES
+
+Most of the options to this script can be set via environment variables. In
+situations where both are set, then the command-line options take precedence.
+
+=over
+
+=item B<$LVMCONF>
+
+Same as -c option.
+
+=item B<$MODULES>
+
+Same as -m option.
+
+=item B<$EXTRAFILES>
+
+Same as -e option.
+
+=item B<$RAID>
+
+Same as -r option.
+
+=item B<$RAIDCONF>
+
+Same as -R option.
+
+=item B<$MAKEDEV>
+
+Same as -M option.
+
+=item B<$BASICDEVICES>
+
+Overrides the default value of $BASICDEVICES in the script (which is "std consoleonly fd"). These values are passed to the B<MAKEDEV> program to create device
+entries in the initrd image.
+
+=item B<$BLOCKDEVICES>
+
+Overrides the default value of $BLOCKDEVICES in the script (which is "md hda hdb hdc hdd sda sdb sdc sdd"). This value is passed to the B<MAKEDEV> program to
+create device entries in the initrd image.
+
+=item B<$BINFILES>
+
+Overrides the default value of $BINFILES (which is "/lib/lvm-200/lvm /bin/bash /bin/busybox /sbin/pivot_root"). The difference between using this and adding
+a file to the $EXTRAFILES list above is that libraries that these depend upon are also included. You can still use $EXTRAFILES to achieve the same effect, but
+you must resolve library dependencies youself.
+
+=item B<$INITRDSIZE>
+
+Force a particular size for your initrd image. The default is to total up the size of
+the included files and to add 512K as a buffer.
+
+=back
+
+=head1 BUGS
+
+I don't like having to specify a -M option to set the MAKEDEV style, but I know
+of no way to reliably detect what type of MAKEDEV is being used. We'll probably
+have to add other MAKEDEV styles in the future as this script is tested on
+other distributions.
+
+=head1 AUTHORS
+
+The script was originally written by Miguel Cabeca, with significant
+improvements by Jeffrey Layton. Comments, bug reports and patches should be
+sent to Jeffrey Layton at S<B<jtlayton@poochiereds.net>>.
+
+=head1 SEE ALSO
+
+B<MAKEDEV>(8), B<mdadm>(8), B<busybox>(8), B<lvm.conf>(5)
--- /dev/null
+#!/bin/sh
+
+# $Id: lvm2udev,v 1.1 2004/06/07 16:20:05 agk Exp $
+
+# simple startup script to create lvm2 devices if /dev is a mountpoint, there
+# are active dm- devices, and an executable /sbin/vgscan.
+
+# this script is licensed under GPLv2.
+# See http://www.gnu.org/licenses/gpl.html
+
+case $1 in
+start)
+ # is /dev a mountpoint?
+ mountpoint -q /dev
+ DEVMNTPOINT=$?
+
+ # check to see if there are active dm entries under /sys
+ ls /sys/block/dm-*/dev 1>/dev/null 2>&1
+ ACTIVEDMDEVS=$?
+
+ # mknodes if conditions are right
+ if [ $DEVMNTPOINT -eq 0 -a $ACTIVEDMDEVS -eq 0 -a -x /sbin/vgscan ]; then
+ /sbin/vgscan --mknodes --ignorelockingfailure
+ fi
+ ;;
+stop)
+ exit 0
+ ;;
+*)
+ echo "usage:"
+ echo " $0 start|stop"
+ ;;
+esac
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of the lvm2 package.
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#
+# Edit an lvm.conf file to adjust various properties
+#
+
+function usage
+{
+ echo "usage: $0 <command>"
+ echo ""
+ echo "Commands:"
+ echo "Enable clvm: --enable-cluster [--lockinglibdir <dir>] [--lockinglib <lib>]"
+ echo "Disable clvm: --disable-cluster"
+ echo "Set locking library: --lockinglibdir <dir> [--lockinglib <lib>]"
+ echo ""
+ echo "Global options:"
+ echo "Config file location: --file <configfile>"
+ echo ""
+}
+
+
+function parse_args
+{
+ while [ -n "$1" ]; do
+ case $1 in
+ --enable-cluster)
+ LOCKING_TYPE=3
+ shift
+ ;;
+ --disable-cluster)
+ LOCKING_TYPE=1
+ shift
+ ;;
+ --lockinglibdir)
+ if [ -n "$2" ]; then
+ LOCKINGLIBDIR=$2
+ shift 2
+ else
+ usage
+ exit 1
+ fi
+ ;;
+ --lockinglib)
+ if [ -n "$2" ]; then
+ LOCKINGLIB=$2
+ shift 2
+ else
+ usage
+ exit 1
+ fi
+ ;;
+ --file)
+ if [ -n "$2" ]; then
+ CONFIGFILE=$2
+ shift 2
+ else
+ usage
+ exit 1
+ fi
+ ;;
+ *)
+ usage
+ exit 1
+ esac
+ done
+}
+
+function validate_args
+{
+ [ -z "$CONFIGFILE" ] && CONFIGFILE="/etc/lvm/lvm.conf"
+
+ if [ ! -f "$CONFIGFILE" ]
+ then
+ echo "$CONFIGFILE does not exist"
+ exit 10
+ fi
+
+ if [ -z "$LOCKING_TYPE" ] && [ -z "$LOCKINGLIBDIR" ]; then
+ usage
+ exit 1
+ fi
+
+ if [ -n "$LOCKINGLIBDIR" ]; then
+
+ if [ "${LOCKINGLIBDIR:0:1}" != "/" ]
+ then
+ echo "Prefix must be an absolute path name (starting with a /)"
+ exit 12
+ fi
+
+ if [ -n "$LOCKINGLIB" ] && [ ! -f "$LOCKINGLIBDIR/$LOCKINGLIB" ]
+ then
+ echo "$LOCKINGLIBDIR/$LOCKINGLIB does not exist, did you do a \"make install\" ?"
+ exit 11
+ fi
+
+ fi
+
+ if [ "$LOCKING_TYPE" = "1" ] && [ -n "$LOCKINGLIBDIR" -o -n "$LOCKINGLIB" ]; then
+ echo "Superfluous locking lib parameter, ignoring"
+ fi
+}
+
+umask 0077
+
+parse_args "$@"
+
+validate_args
+
+
+SCRIPTFILE=/etc/lvm/.lvmconf-script.tmp
+TMPFILE=/etc/lvm/.lvmconf-tmp.tmp
+
+
+# Flags so we know which parts of the file we can replace and which need
+# adding. These are return codes from grep, so zero means it IS present!
+have_type=1
+have_dir=1
+have_library=1
+have_global=1
+
+grep -q '^[[:blank:]]*locking_type[[:blank:]]*=' $CONFIGFILE
+have_type=$?
+
+grep -q '^[[:blank:]]*library_dir[[:blank:]]*=' $CONFIGFILE
+have_dir=$?
+
+grep -q '^[[:blank:]]*locking_library[[:blank:]]*=' $CONFIGFILE
+have_library=$?
+
+# Those options are in section "global {" so we must have one if any are present.
+if [ "$have_type" = "0" -o "$have_dir" = "0" -o "$have_library" = "0" ]
+then
+
+ # See if we can find it...
+ grep -q '^[[:blank:]]*global[[:blank:]]*{' $CONFIGFILE
+ have_global=$?
+
+ if [ "$have_global" = "1" ]
+ then
+ echo "global keys but no 'global {' found, can't edit file"
+ exit 13
+ fi
+fi
+
+if [ "$LOCKING_TYPE" = "2" ] && [ -z "$LOCKINGLIBDIR" ] && [ "$have_dir" = "1" ]; then
+ echo "no library_dir specified in $CONFIGFILE"
+ exit 16
+fi
+
+# So if we don't have "global {" we need to create one and
+# populate it
+
+if [ "$have_global" = "1" ]
+then
+ if [ -z "$LOCKING_TYPE" ]; then
+ LOCKING_TYPE=1
+ fi
+ if [ "$LOCKING_TYPE" = "3" ] || [ "$LOCKING_TYPE" = "2" ]; then
+ cat $CONFIGFILE - <<EOF > $TMPFILE
+global {
+ # Enable locking for cluster LVM
+ locking_type = $LOCKING_TYPE
+ library_dir = "$LOCKINGLIBDIR"
+EOF
+ if [ $? != 0 ]
+ then
+ echo "failed to create temporary config file, $CONFIGFILE not updated"
+ exit 14
+ fi
+ if [ -n "$LOCKINGLIB" ]; then
+ cat - <<EOF >> $TMPFILE
+ locking_library = "$LOCKINGLIB"
+EOF
+ if [ $? != 0 ]
+ then
+ echo "failed to create temporary config file, $CONFIGFILE not updated"
+ exit 16
+ fi
+ fi
+ cat - <<EOF >> $TMPFILE
+}
+EOF
+ fi # if we aren't setting cluster locking, we don't need to create a global section
+
+ if [ $? != 0 ]
+ then
+ echo "failed to create temporary config file, $CONFIGFILE not updated"
+ exit 17
+ fi
+else
+ #
+ # We have a "global {" section, so add or replace the
+ # locking entries as appropriate
+ #
+
+ if [ -n "$LOCKING_TYPE" ]; then
+ if [ "$have_type" = "0" ]
+ then
+ SEDCMD=" s/^[[:blank:]]*locking_type[[:blank:]]*=.*/\ \ \ \ locking_type = $LOCKING_TYPE/g"
+ else
+ SEDCMD=" /global[[:blank:]]*{/a\ \ \ \ locking_type = $LOCKING_TYPE"
+ fi
+ fi
+
+ if [ -n "$LOCKINGLIBDIR" ]; then
+ if [ "$have_dir" = "0" ]
+ then
+ SEDCMD="${SEDCMD}\ns'^[[:blank:]]*library_dir[[:blank:]]*=.*'\ \ \ \ library_dir = \"$LOCKINGLIBDIR\"'g"
+ else
+ SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ library_dir = \"$LOCKINGLIBDIR\""
+ fi
+ fi
+
+ if [ -n "$LOCKINGLIB" ]; then
+ if [ "$have_library" = "0" ]
+ then
+ SEDCMD="${SEDCMD}\ns/^[[:blank:]]*locking_library[[:blank:]]*=.*/\ \ \ \ locking_library = \"$LOCKINGLIB\"/g"
+ else
+ SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ locking_library = \"$LOCKINGLIB\""
+ fi
+ fi
+
+ echo -e $SEDCMD > $SCRIPTFILE
+ sed <$CONFIGFILE >$TMPFILE -f $SCRIPTFILE
+ if [ $? != 0 ]
+ then
+ echo "sed failed, $CONFIGFILE not updated"
+ exit 15
+ fi
+fi
+
+# Now we have a suitably editted config file in a temp place,
+# backup the original and copy our new one into place.
+
+cp $CONFIGFILE $CONFIGFILE.lvmconfold
+if [ $? != 0 ]
+ then
+ echo "failed to backup old config file, $CONFIGFILE not updated"
+ exit 2
+fi
+
+cp $TMPFILE $CONFIGFILE
+if [ $? != 0 ]
+ then
+ echo "failed to copy new config file into place, check $CONFIGFILE is still OK"
+ exit 3
+fi
+
+rm -f $SCRIPTFILE $TMPFILE
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+#
+# This file is part of the lvm2-cluster package.
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#
+# Edit an lvm.conf file to adjust various properties
+#
+
+function usage
+{
+ echo "usage: $0 <command>"
+ echo ""
+ echo "Commands:"
+ echo "Enable clvm: --enable-cluster [--lockinglibdir <dir>] [--lockinglib <lib>]"
+ echo "Disable clvm: --disable-cluster"
+ echo "Set locking library: --lockinglibdir <dir> [--lockinglib <lib>]"
+ echo ""
+ echo "Global options:"
+ echo "Config file location: --file <configfile>"
+ echo ""
+}
+
+
+function parse_args
+{
+ while [ -n "$1" ]; do
+ case $1 in
+ --enable-cluster)
+ LOCKING_TYPE=2
+ shift
+ ;;
+ --disable-cluster)
+ LOCKING_TYPE=1
+ shift
+ ;;
+ --lockinglibdir)
+ if [ -n "$2" ]; then
+ LOCKINGLIBDIR=$2
+ shift 2
+ else
+ usage
+ exit 1
+ fi
+ ;;
+ --lockinglib)
+ if [ -n "$2" ]; then
+ LOCKINGLIB=$2
+ shift 2
+ else
+ usage
+ exit 1
+ fi
+ ;;
+ --file)
+ if [ -n "$2" ]; then
+ CONFIGFILE=$2
+ shift 2
+ else
+ usage
+ exit 1
+ fi
+ ;;
+ *)
+ usage
+ exit 1
+ esac
+ done
+}
+
+function validate_args
+{
+ [ -z "$CONFIGFILE" ] && CONFIGFILE="/etc/lvm/lvm.conf"
+
+ if [ ! -f "$CONFIGFILE" ]
+ then
+ echo "$CONFIGFILE does not exist"
+ exit 10
+ fi
+
+ if [ -z "$LOCKING_TYPE" ] && [ -z "$LOCKINGLIBDIR" ]; then
+ usage
+ exit 1
+ fi
+
+ if [ -n "$LOCKINGLIBDIR" ]; then
+
+ [ -z "$LOCKINGLIB" ] && LOCKINGLIB="liblvm2clusterlock.so"
+
+ if [ "${LOCKINGLIBDIR:0:1}" != "/" ]
+ then
+ echo "Prefix must be an absolute path name (starting with a /)"
+ exit 12
+ fi
+
+ if [ ! -f "$LOCKINGLIBDIR/$LOCKINGLIB" ]
+ then
+ echo "$LOCKINGLIBDIR/$LOCKINGLIB does not exist, did you do a \"make install\" ?"
+ exit 11
+ fi
+
+ fi
+
+ if [ "$LOCKING_TYPE" = "1" ] && [ -n "$LOCKINGLIBDIR" -o -n "$LOCKINGLIB" ]; then
+ echo "Superfluous locking lib parameter, ignoring"
+ fi
+}
+
+umask 0077
+
+parse_args "$@"
+
+validate_args
+
+
+SCRIPTFILE=/etc/lvm/.lvmconf-script.tmp
+TMPFILE=/etc/lvm/.lvmconf-tmp.tmp
+
+
+# Flags so we know which parts of the file we can replace and which need
+# adding. These are return codes from grep, so zero means it IS present!
+have_type=1
+have_dir=1
+have_library=1
+have_global=1
+
+grep -q '^[[:blank:]]*locking_type[[:blank:]]*=' $CONFIGFILE
+have_type=$?
+
+grep -q '^[[:blank:]]*library_dir[[:blank:]]*=' $CONFIGFILE
+have_dir=$?
+
+grep -q '^[[:blank:]]*locking_library[[:blank:]]*=' $CONFIGFILE
+have_library=$?
+
+# Those options are in section "global {" so we must have one if any are present.
+if [ "$have_type" = "0" -o "$have_dir" = "0" -o "$have_library" = "0" ]
+then
+
+ # See if we can find it...
+ grep -q '^[[:blank:]]*global[[:blank:]]*{' $CONFIGFILE
+ have_global=$?
+
+ if [ "$have_global" = "1" ]
+ then
+ echo "global keys but no 'global {' found, can't edit file"
+ exit 13
+ fi
+fi
+
+if [ "$LOCKING_TYPE" = "2" ] && [ -z "$LOCKINGLIBDIR" ] && [ "$have_dir" = "1" ]; then
+ echo "no library_dir specified in $CONFIGFILE"
+ exit 16
+fi
+
+# So if we don't have "global {" we need to create one and
+# populate it
+
+if [ "$have_global" = "1" ]
+then
+ if [ -z "$LOCKING_TYPE" ]; then
+ LOCKING_TYPE=1
+ fi
+ if [ "$LOCKING_TYPE" = "2" ]; then
+ cat $CONFIGFILE - <<EOF > $TMPFILE
+global {
+ # Enable locking for cluster LVM
+ locking_type = $LOCKING_TYPE
+ library_dir = "$LOCKINGLIBDIR"
+ locking_library = "$LOCKINGLIB"
+}
+EOF
+ fi # if we aren't setting cluster locking, we don't need to create a global section
+
+ if [ $? != 0 ]
+ then
+ echo "failed to create temporary config file, $CONFIGFILE not updated"
+ exit 14
+ fi
+else
+ #
+ # We have a "global {" section, so add or replace the
+ # locking entries as appropriate
+ #
+
+ if [ -n "$LOCKING_TYPE" ]; then
+ if [ "$have_type" = "0" ]
+ then
+ SEDCMD=" s/^[[:blank:]]*locking_type[[:blank:]]*=.*/\ \ \ \ locking_type = $LOCKING_TYPE/g"
+ else
+ SEDCMD=" /global[[:blank:]]*{/a\ \ \ \ locking_type = $LOCKING_TYPE"
+ fi
+ fi
+
+ if [ -n "$LOCKINGLIBDIR" ]; then
+ if [ "$have_dir" = "0" ]
+ then
+ SEDCMD="${SEDCMD}\ns'^[[:blank:]]*library_dir[[:blank:]]*=.*'\ \ \ \ library_dir = \"$LOCKINGLIBDIR\"'g"
+ else
+ SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ library_dir = \"$LOCKINGLIBDIR\""
+ fi
+
+ if [ "$have_library" = "0" ]
+ then
+ SEDCMD="${SEDCMD}\ns/^[[:blank:]]*locking_library[[:blank:]]*=.*/\ \ \ \ locking_library = \"$LOCKINGLIB\"/g"
+ else
+ SEDCMD="${SEDCMD}\n/global[[:blank:]]*{/a\ \ \ \ locking_library = \"$LOCKINGLIB\""
+ fi
+ fi
+
+ if [ "$LOCKING_TYPE" = "1" ]; then
+ # if we're not using cluster locking, remove the library dir and locking library name
+ if [ "$have_dir" = "0" ]
+ then
+ SEDCMD="${SEDCMD}\n/^[[:blank:]]*library_dir[[:blank:]]*=.*/d"
+ fi
+
+ if [ "$have_library" = "0" ]
+ then
+ SEDCMD="${SEDCMD}\n/^[[:blank:]]*locking_library[[:blank:]]*=.*/d"
+ fi
+ fi
+
+ echo -e $SEDCMD > $SCRIPTFILE
+ sed <$CONFIGFILE >$TMPFILE -f $SCRIPTFILE
+ if [ $? != 0 ]
+ then
+ echo "sed failed, $CONFIGFILE not updated"
+ exit 15
+ fi
+fi
+
+# Now we have a suitably editted config file in a temp place,
+# backup the original and copy our new one into place.
+
+cp $CONFIGFILE $CONFIGFILE.lvmconfold
+if [ $? != 0 ]
+ then
+ echo "failed to backup old config file, $CONFIGFILE not updated"
+ exit 2
+fi
+
+cp $TMPFILE $CONFIGFILE
+if [ $? != 0 ]
+ then
+ echo "failed to copy new config file into place, check $CONFIGFILE is still OK"
+ exit 3
+fi
+
+rm -f $SCRIPTFILE $TMPFILE
--- /dev/null
+#!/bin/bash
+# We use some bash-isms (getopts?)
+
+# Copyright (C) 2007-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# lvm_dump: This script is used to collect pertinent information for
+# the debugging of lvm issues.
+
+# following external commands are used throughout the script
+# echo and test are internal in bash at least
+MKDIR=mkdir # need -p
+TAR=tar # need czf
+RM=rm # need -rf
+CP=cp
+TAIL=tail # we need -n
+LS=ls # need -la
+PS=ps # need alx
+SED=sed
+DD=dd
+CUT=cut
+DATE=date
+BASENAME=basename
+UNAME=uname
+
+# user may override lvm and dmsetup location by setting LVM_BINARY
+# and DMSETUP_BINARY respectively
+LVM=${LVM_BINARY-lvm}
+DMSETUP=${DMSETUP_BINARY-dmsetup}
+
+die() {
+ code=$1; shift
+ echo "$@" 1>&2
+ exit $code
+}
+
+"$LVM" version >& /dev/null || die 2 "Could not run lvm binary '$LVM'"
+"$DMSETUP" version >& /dev/null || DMSETUP=:
+
+function usage {
+ echo "$0 [options]"
+ echo " -h print this message"
+ echo " -a advanced collection - warning: if lvm is already hung,"
+ echo " then this script may hang as well if -a is used"
+ echo " -m gather LVM metadata from the PVs"
+ echo " -d <directory> dump into a directory instead of tarball"
+ echo " -c if running clvmd, gather cluster data as well"
+ echo ""
+
+ exit 1
+}
+
+advanced=0
+clustered=0
+metadata=0
+while getopts :acd:hm opt; do
+ case $opt in
+ s) sysreport=1 ;;
+ a) advanced=1 ;;
+ c) clustered=1 ;;
+ d) userdir=$OPTARG ;;
+ h) usage ;;
+ m) metadata=1 ;;
+ :) echo "$0: $OPTARG requires a value:"; usage ;;
+ \?) echo "$0: unknown option $OPTARG"; usage ;;
+ *) usage ;;
+ esac
+done
+
+NOW=`$DATE -u +%G%m%d%k%M%S | /usr/bin/tr -d ' '`
+if test -n "$userdir"; then
+ dir="$userdir"
+else
+ dirbase="lvmdump-$HOSTNAME-$NOW"
+ dir="$HOME/$dirbase"
+fi
+
+test -e $dir && die 3 "Fatal: $dir already exists"
+$MKDIR -p $dir || die 4 "Fatal: could not create $dir"
+
+log="$dir/lvmdump.log"
+
+myecho() {
+ echo "$@"
+ echo "$@" >> "$log"
+}
+
+log() {
+ echo "$@" >> "$log"
+ eval "$@"
+}
+
+warnings() {
+ if test "$UID" != "0" && test "$EUID" != "0"; then
+ myecho "WARNING! Running as non-privileged user, dump is likely incomplete!"
+ elif test "$DMSETUP" = ":"; then
+ myecho "WARNING! Could not run dmsetup, dump is likely incomplete."
+ fi
+}
+
+warnings
+
+myecho "Creating dump directory: $dir"
+echo " "
+
+if (( $advanced )); then
+ myecho "Gathering LVM volume info..."
+
+ myecho " vgscan..."
+ log "\"$LVM\" vgscan -vvvv > \"$dir/vgscan\" 2>&1"
+
+ myecho " pvscan..."
+ log "\"$LVM\" pvscan -v >> \"$dir/pvscan\" 2>> \"$log\""
+
+ myecho " lvs..."
+ log "\"$LVM\" lvs -a -o +devices >> \"$dir/lvs\" 2>> \"$log\""
+
+ myecho " pvs..."
+ log "\"$LVM\" pvs -a -v > \"$dir/pvs\" 2>> \"$log\""
+
+ myecho " vgs..."
+ log "\"$LVM\" vgs -v > \"$dir/vgs\" 2>> \"$log\""
+fi
+
+if (( $clustered )); then
+ myecho "Gathering cluster info..."
+
+ {
+ for i in nodes status services; do
+ cap_i=$(echo $i|tr a-z A-Z)
+ printf "$cap_i:\n----------------------------------\n"
+ log "cman_tool $i 2>> \"$log\""
+ echo
+ done
+
+ echo "LOCKS:"
+ echo "----------------------------------"
+ if [ -f /proc/cluster/dlm_locks ]
+ then
+ echo clvmd > /proc/cluster/dlm_locks
+ cat /proc/cluster/dlm_locks
+ echo
+ echo "RESOURCE DIR:"
+ cat /proc/cluster/dlm_dir
+ echo
+ echo "DEBUG LOG:"
+ cat /proc/cluster/dlm_debug
+ echo
+ fi
+ if [ -f /debug/dlm/clvmd ]
+ then
+ cat /debug/dlm/clvmd
+ echo
+ echo "WAITERS:"
+ cat /debug/dlm/clvmd_waiters
+ echo
+ echo "MASTER:"
+ cat /debug/dlm/clvmd_master
+ fi
+ } > $dir/cluster_info
+fi
+
+myecho "Gathering LVM & device-mapper version info..."
+echo "LVM VERSION:" > "$dir/versions"
+"$LVM" lvs --version >> "$dir/versions" 2>> "$log"
+echo "DEVICE MAPPER VERSION:" >> "$dir/versions"
+"$DMSETUP" --version >> "$dir/versions" 2>> "$log"
+echo "KERNEL VERSION:" >> "$dir/versions"
+"$UNAME" -a >> "$dir/versions" 2>> "$log"
+echo "DM TARGETS VERSIONS:" >> "$dir/versions"
+"$DMSETUP" targets >> "$dir/versions" 2>> "$log"
+
+myecho "Gathering dmsetup info..."
+log "\"$DMSETUP\" info -c > \"$dir/dmsetup_info\" 2>> \"$log\""
+log "\"$DMSETUP\" table > \"$dir/dmsetup_table\" 2>> \"$log\""
+log "\"$DMSETUP\" status > \"$dir/dmsetup_status\" 2>> \"$log\""
+
+myecho "Gathering process info..."
+log "$PS alx > \"$dir/ps_info\" 2>> \"$log\""
+
+myecho "Gathering console messages..."
+log "$TAIL -n 75 /var/log/messages > \"$dir/messages\" 2>> \"$log\""
+
+myecho "Gathering /etc/lvm info..."
+log "$CP -a /etc/lvm \"$dir/lvm\" 2>> \"$log\""
+
+myecho "Gathering /dev listing..."
+log "$LS -laR /dev > \"$dir/dev_listing\" 2>> \"$log\""
+
+myecho "Gathering /sys/block listing..."
+log "$LS -laR /sys/block > \"$dir/sysblock_listing\" 2>> \"$log\""
+log "$LS -laR /sys/devices/virtual/block >> \"$dir/sysblock_listing\" 2>> \"$log\""
+
+if (( $metadata )); then
+ myecho "Gathering LVM metadata from Physical Volumes..."
+
+ log "$MKDIR -p \"$dir/metadata\""
+
+ pvs="$("$LVM" pvs --separator , --noheadings --units s --nosuffix -o \
+ name,pe_start 2>> "$log" | $SED -e 's/^ *//')"
+ for line in $pvs
+ do
+ test -z "$line" && continue
+ pv="$(echo $line | $CUT -d, -f1)"
+ pe_start="$(echo $line | $CUT -d, -f2)"
+ name="$($BASENAME "$pv")"
+ myecho " $pv"
+ log "$DD if=$pv \"of=$dir/metadata/$name\" bs=512 count=$pe_start 2>> \"$log\""
+ done
+fi
+
+if test -z "$userdir"; then
+ lvm_dump="$dirbase.tgz"
+ myecho "Creating report tarball in $HOME/$lvm_dump..."
+fi
+
+warnings
+
+if test -z "$userdir"; then
+ cd "$HOME"
+ "$TAR" czf "$lvm_dump" "$dirbase" 2>/dev/null
+ "$RM" -rf "$dir"
+fi
+
+exit 0
+
--- /dev/null
+#!/usr/bin/awk -f
+#
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# relpath.awk: Script is used to calculate relative path
+# between two real absolute paths.
+#
+# echo /a/b/c/d /a/b/e/f | relpath.awk
+# -> ../../e/f/
+
+{
+ length_from = split($1, from, "/");
+ length_to = split($2, to, "/") ;
+ l = 1;
+ while (l <= length_from && l <= length_to && from[l] == to[l])
+ l++;
+ for (i = l; i <= length_from && length(from[i]); i++) {
+ if (i > l)
+ p = sprintf("%s/", p);
+ p = sprintf("%s..", p);
+ }
+ for (i = l; i <= length_to && length(to[i]); i++) {
+ if (length(p) > 0)
+ p = sprintf("%s/", p);
+ p = sprintf("%s%s", p, to[i]);
+ }
+ if (length(p))
+ p = sprintf("%s/", p);
+ print p
+}
--- /dev/null
+#!/bin/sh -x
+
+# Original script used to convert a VG from LVM1 to LVM2 metadata format.
+# Superceded by 'vgconvert', but left here to show how to do it step-by-step.
+
+# Takes vgname as parameter. No error checking. Uses temp file 'lvmbackup'.
+
+echo "Please use the 'vgconvert' tool instead"
+exit 1
+
+./vgcfgbackup $1 || exit 1
+./vgcfgbackup --file lvmbackup $1 || exit 1
+
+CMDS=`./pvscan -u | sed -ne "s/.*PV \(.*\) with UUID \(.*\) VG $1 .*/.\/pvcreate -ff -y -M lvm2 --restorefile lvmbackup -u \2 \1 ; /p"`
+
+sh -x -c "$CMDS" || exit 1
+
+./vgcfgrestore --file lvmbackup -M lvm2 $1 || exit 1
--- /dev/null
+#!/bin/bash
+
+# Copyright (C) 2009 Chris Procter All rights reserved.
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# vgimportclone: This script is used to rename the VG and change the associated
+# VG and PV UUIDs (primary application being HW snapshot restore)
+
+# following external commands are used throughout the script
+# echo and test are internal in bash at least
+RM=rm
+BASENAME=basename
+MKTEMP=mktemp
+AWK=awk
+CUT=cut
+TR=tr
+READLINK=readlink
+GREP=grep
+GETOPT=getopt
+
+# user may override lvm location by setting LVM_BINARY
+LVM="${LVM_BINARY:-lvm}"
+
+die() {
+ code=$1; shift
+ echo "Fatal: $@" 1>&2
+ exit $code
+}
+
+"$LVM" version >& /dev/null || die 2 "Could not run lvm binary '$LVM'"
+
+
+function getvgname {
+### get a unique vg name
+### $1 = list of exists VGs
+### $2 = the name we want
+ VGLIST=$1
+ VG=$2
+ NEWVG=$3
+
+ BNAME="${NEWVG:-${VG}}"
+ NAME="${BNAME}"
+ I=0
+
+ while [[ "${VGLIST}" =~ "${NAME}" ]]
+ do
+ I=$(($I+1))
+ NAME="${BNAME}$I"
+ done
+ echo "${NAME}"
+}
+
+
+function checkvalue {
+### check return value and error if non zero
+ if [ $1 -ne 0 ]
+ then
+ die $1 "$2, error: $1"
+ fi
+}
+
+
+function usage {
+### display usage message
+ echo "Usage: ${SCRIPTNAME} [options] PhysicalVolume [PhysicalVolume...]"
+ echo " -n|--basevgname - Base name for the new volume group(s)"
+ echo " -i|--import - Import any exported volume groups found"
+ echo " -t|--test - Run in test mode"
+ echo " --quiet - Suppress output"
+ echo " -v|--verbose - Set verbose level"
+ echo " -d|--debug - Set debug level"
+ echo " --version - Display version information"
+ echo " -h|--help - Display this help message"
+ echo ""
+ exit 1
+}
+
+
+function cleanup {
+ #set to use old lvm.conf
+ LVM_SYSTEM_DIR=${ORIG_LVM_SYS_DIR}
+
+ if [ $KEEP_TMP_LVM_SYSTEM_DIR -eq 1 ]; then
+ echo "${SCRIPTNAME}: LVM_SYSTEM_DIR (${TMP_LVM_SYSTEM_DIR}) must be cleaned up manually."
+ else
+ "$RM" -rf -- "${TMP_LVM_SYSTEM_DIR}"
+ fi
+}
+
+SCRIPTNAME=`"$BASENAME" $0`
+
+
+if [ "$UID" != "0" -a "$EUID" != "0" ]
+then
+ die 3 "${SCRIPTNAME} must be run as root."
+fi
+
+LVM_OPTS=""
+TEST_OPT=""
+DISKS=""
+# for compatibility: using mktemp -t rather than --tmpdir
+TMP_LVM_SYSTEM_DIR=`"$MKTEMP" -d -t snap.XXXXXXXX`
+KEEP_TMP_LVM_SYSTEM_DIR=0
+CHANGES_MADE=0
+IMPORT=0
+DEBUG=""
+VERBOSE=""
+VERBOSE_COUNT=0
+DEVNO=0
+
+if [ -n "${LVM_SYSTEM_DIR}" ]; then
+ export ORIG_LVM_SYS_DIR="${LVM_SYSTEM_DIR}"
+fi
+
+trap cleanup 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
+
+#####################################################################
+### Get and check arguments
+#####################################################################
+OPTIONS=`"$GETOPT" -o n:dhitv \
+ -l basevgname:,debug,help,import,quiet,test,verbose,version \
+ -n "${SCRIPTNAME}" -- "$@"`
+[ $? -ne 0 ] && usage
+eval set -- "$OPTIONS"
+
+while true
+do
+ case $1 in
+ -n|--basevgname)
+ NEWVG="$2"; shift; shift
+ ;;
+ -i|--import)
+ IMPORT=1; shift
+ ;;
+ -t|--test)
+ TEST_OPT="-t"
+ shift
+ ;;
+ --quiet)
+ LVM_OPTS="--quiet ${LVM_OPTS}"
+ shift
+ ;;
+ -v|--verbose)
+ let VERBOSE_COUNT=VERBOSE_COUNT+1
+ if [ -z "$VERBOSE" ]
+ then
+ VERBOSE="-v"
+ else
+ VERBOSE="${VERBOSE}v"
+ fi
+ shift
+ ;;
+ -d|--debug)
+ if [ -z "$DEBUG" ]
+ then
+ DEBUG="-d"
+ set -x
+ else
+ DEBUG="${DEBUG}d"
+ fi
+ shift
+ ;;
+ --version)
+ "$LVM" version
+ shift
+ exit 0
+ ;;
+ -h|--help)
+ usage; shift
+ ;;
+ --)
+ shift; break
+ ;;
+ *)
+ usage
+ ;;
+ esac
+done
+
+# turn on DEBUG (special case associated with -v use)
+if [ -z "$DEBUG" -a $VERBOSE_COUNT -gt 3 ]; then
+ DEBUG="-d"
+ set -x
+fi
+
+# setup LVM_OPTS
+if [ -n "${DEBUG}" -o -n "${VERBOSE}" ]
+then
+ LVM_OPTS="${LVM_OPTS} ${DEBUG} ${VERBOSE}"
+fi
+
+# process remaining arguments (which should be disks)
+for ARG
+do
+ if [ -b "$ARG" ]
+ then
+ PVS_OUT=`"${LVM}" pvs ${LVM_OPTS} --noheadings -o vg_name "$ARG" 2>/dev/null`
+ checkvalue $? "$ARG is not a PV."
+ PV_VGNAME=$(echo $PVS_OUT | $GREP -v '[[:space:]]+$')
+ [ -z "$PV_VGNAME" ] && die 3 "$ARG is not in a VG."
+
+ ln -s "$ARG" ${TMP_LVM_SYSTEM_DIR}/vgimport${DEVNO}
+ DISKS="${DISKS} ${TMP_LVM_SYSTEM_DIR}/vgimport${DEVNO}"
+ DEVNO=$((${DEVNO}+1))
+ else
+ die 3 "$ARG is not a block device."
+ fi
+done
+
+### check we have suitable values for important variables
+if [ -z "${DISKS}" ]
+then
+ usage
+fi
+
+#####################################################################
+### Get the existing state so we can use it later
+#####################################################################
+
+OLDVGS=`"${LVM}" vgs ${LVM_OPTS} -o name --noheadings 2>/dev/null`
+checkvalue $? "Current VG names could not be collected without errors"
+
+#####################################################################
+### Prepare the temporary lvm environment
+#####################################################################
+
+for BLOCK in ${DISKS}
+do
+ FILTER="\"a|^${BLOCK}$|\", ${FILTER}"
+done
+export FILTER="filter=[ ${FILTER} \"r|.*|\" ]"
+
+LVMCONF=${TMP_LVM_SYSTEM_DIR}/lvm.conf
+
+"$LVM" dumpconfig ${LVM_OPTS} | \
+"$AWK" -v DEV=${TMP_LVM_SYSTEM_DIR} -v CACHE=${TMP_LVM_SYSTEM_DIR}/.cache \
+ -v CACHE_DIR=${TMP_LVM_SYSTEM_DIR}/cache \
+ '/^[[:space:]]*filter[[:space:]]*=/{print ENVIRON["FILTER"];next} \
+ /^[[:space:]]*scan[[:space:]]*=/{print "scan = [ \"" DEV "\" ]";next} \
+ /^[[:space:]]*cache[[:space:]]*=/{print "cache = \"" CACHE "\"";next} \
+ /^[[:space:]]*cache_dir[[:space:]]*=/{print "cache_dir = \"" CACHE_DIR "\"";next} \
+ {print $0}' > ${LVMCONF}
+
+checkvalue $? "Failed to generate ${LVMCONF}"
+# Only keep TMP_LVM_SYSTEM_DIR if it contains something worth keeping
+[ -n "${DEBUG}" ] && KEEP_TMP_LVM_SYSTEM_DIR=1
+
+# verify the config contains the filter, scan and cache_dir (or cache) config keywords
+"$GREP" -q '^[[:space:]]*filter[[:space:]]*=' ${LVMCONF} || \
+ die 5 "Temporary lvm.conf must contain 'filter' config."
+"$GREP" -q '^[[:space:]]*scan[[:space:]]*=' ${LVMCONF} || \
+ die 6 "Temporary lvm.conf must contain 'scan' config."
+
+# check for either 'cache' or 'cache_dir' config values
+"$GREP" -q '[[:space:]]*cache[[:space:]]*=' ${LVMCONF}
+CACHE_RET=$?
+"$GREP" -q '^[[:space:]]*cache_dir' ${LVMCONF}
+CACHE_DIR_RET=$?
+[ $CACHE_RET -eq 0 -o $CACHE_DIR_RET -eq 0 ] || \
+ die 7 "Temporary lvm.conf must contain 'cache' or 'cache_dir' config."
+
+### set to use new lvm.conf
+export LVM_SYSTEM_DIR=${TMP_LVM_SYSTEM_DIR}
+
+
+#####################################################################
+### Rename the VG(s) and change the VG and PV UUIDs.
+#####################################################################
+
+PVINFO=`"${LVM}" pvs ${LVM_OPTS} -o pv_name,vg_name,vg_attr --noheadings --separator : 2>/dev/null`
+checkvalue $? "PV info could not be collected without errors"
+
+# output VG info so each line looks like: name:exported?:disk1,disk2,...
+VGINFO=`echo "${PVINFO}" | \
+ "$AWK" -F : '{{sub(/^[[:space:]]*/,"")} \
+ {sub(/unknown device/,"unknown_device")} \
+ {vg[$2]=$1","vg[$2]} if($3 ~ /^..x/){x[$2]="x"}} \
+ END{for(k in vg){printf("%s:%s:%s\n", k, x[k], vg[k])}}'`
+checkvalue $? "PV info could not be parsed without errors"
+
+for VG in ${VGINFO}
+do
+ VGNAME=`echo "${VG}" | "$CUT" -d: -f1`
+ EXPORTED=`echo "${VG}" | "$CUT" -d: -f2`
+ PVLIST=`echo "${VG}" | "$CUT" -d: -f3- | "$TR" , ' '`
+
+ if [ -z "${VGNAME}" ]
+ then
+ FOLLOWLIST=""
+ for DEV in $PVLIST; do
+ FOLLOW=`"$READLINK" $DEV`
+ FOLLOWLIST="$FOLLOW $FOLLOWLIST"
+ done
+ die 8 "Specified PV(s) ($FOLLOWLIST) don't belong to a VG."
+ fi
+
+ if [ -n "${EXPORTED}" ]
+ then
+ if [ ${IMPORT} -eq 1 ]
+ then
+ "$LVM" vgimport ${LVM_OPTS} ${TEST_OPT} "${VGNAME}"
+ checkvalue $? "Volume Group ${VGNAME} could not be imported"
+ else
+ echo "Volume Group ${VGNAME} exported, skipping."
+ continue
+ fi
+ fi
+
+ ### change the pv uuids
+ if [[ "${PVLIST}" =~ "unknown" ]]
+ then
+ echo "Volume Group ${VGNAME} has unknown PV(s), skipping."
+ echo "- Were all associated PV(s) supplied as arguments?"
+ continue
+ fi
+
+ for BLOCKDEV in ${PVLIST}
+ do
+ "$LVM" pvchange ${LVM_OPTS} ${TEST_OPT} --uuid ${BLOCKDEV} --config 'global{activation=0}'
+ checkvalue $? "Unable to change PV uuid for ${BLOCKDEV}"
+ done
+
+ NEWVGNAME=`getvgname "${OLDVGS}" "${VGNAME}" "${NEWVG}"`
+
+ "$LVM" vgchange ${LVM_OPTS} ${TEST_OPT} --uuid "${VGNAME}" --config 'global{activation=0}'
+ checkvalue $? "Unable to change VG uuid for ${VGNAME}"
+
+ ## if the name isn't going to get changed dont even try.
+ if [ "${VGNAME}" != "${NEWVGNAME}" ]
+ then
+ "$LVM" vgrename ${LVM_OPTS} ${TEST_OPT} "${VGNAME}" "${NEWVGNAME}"
+ checkvalue $? "Unable to rename ${VGNAME} to ${NEWVGNAME}"
+ fi
+
+ CHANGES_MADE=1
+done
+
+#####################################################################
+### Restore the old environment
+#####################################################################
+### set to use old lvm.conf
+if [ -z "${ORIG_LVM_SYS_DIR}" ]
+then
+ unset LVM_SYSTEM_DIR
+else
+ LVM_SYSTEM_DIR=${ORIG_LVM_SYS_DIR}
+fi
+
+### update the device cache and make sure all
+### the device nodes we need are straight
+if [ ${CHANGES_MADE} -eq 1 ]
+then
+ "$LVM" vgscan ${LVM_OPTS} --mknodes
+fi
+
+exit 0
--- /dev/null
+.bin-dir-stamp
+Makefile
+bin
+init.sh
--- /dev/null
+# Copyright (C) 2007-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#TEST_OPTS=--verbose --debug
+SHELL_PATH ?= $(SHELL)
+TAR ?= $(TAR)
+RM ?= rm -f
+
+subdir := $(shell pwd|sed 's,.*/,,')
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+abs_srcdir = @abs_srcdir@
+abs_builddir = @abs_builddir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+
+T ?= .
+S ?= @ # never match anything by default
+VERBOSE ?= 0
+RUN = $(shell find $(srcdir) -regextype posix-egrep \( -name t-\*.sh -or -path */api/\*.sh \) -and -regex "$(srcdir)/.*($(T)).*" -and -not -regex "$(srcdir)/.*($(S)).*" | sort)
+RUN_BASE = $(shell echo $(RUN) | xargs -n 1 echo | sed -e s,^$(srcdir)/,,)
+
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
+SUPPORT := $(srcdir)/test-utils.sh \
+ $(srcdir)/lvm-utils.sh
+
+ifeq ("@UDEV_SYNC@", "yes")
+dm_udev_synchronisation = 1
+endif
+
+all check: init.sh
+ make -C api tests
+ @echo Testing with locking_type 1
+ VERBOSE=$(VERBOSE) ./bin/harness $(RUN_BASE)
+ @echo Testing with locking_type 3
+ VERBOSE=$(VERBOSE) LVM_TEST_LOCKING=3 ./bin/harness $(RUN_BASE)
+
+check_cluster: init.sh
+ make -C api tests
+ @echo Testing with locking_type 3
+ VERBOSE=$(VERBOSE) LVM_TEST_LOCKING=3 ./bin/harness $(RUN_BASE)
+
+check_local: init.sh
+ make -C api tests
+ @echo Testing with locking_type 1
+ VERBOSE=$(VERBOSE) LVM_TEST_LOCKING=1 ./bin/harness $(RUN_BASE)
+
+bin/not: $(srcdir)/not.c .bin-dir-stamp
+ $(CC) -o bin/not $<
+ ln -sf not bin/should
+
+bin/harness: $(srcdir)/harness.c .bin-dir-stamp
+ $(CC) -o bin/harness $<
+
+bin/check: $(srcdir)/check.sh .bin-dir-stamp
+ cp $< bin/check
+ chmod +x bin/check
+
+init.sh: $(srcdir)/Makefile.in .bin-dir-stamp bin/not bin/check bin/harness $(RUN) $(SUPPORT) $(UNIT)
+ rm -f $@-t $@
+ echo 'top_srcdir=$(top_srcdir)' >> $@-t
+ echo 'abs_top_builddir=$(abs_top_builddir)' >> $@-t
+ echo 'abs_top_srcdir=$(abs_top_builddir)' >> $@-t
+ echo 'PATH=$$abs_top_builddir/test/bin:$$PATH' >> $@-t
+ LDLPATH="\$$abs_top_builddir/libdm"; \
+ LDLPATH="$$LDLPATH:\$$abs_top_builddir/tools"; \
+ LDLPATH="$$LDLPATH:\$$abs_top_builddir/liblvm"; \
+ LDLPATH="$$LDLPATH:\$$abs_top_builddir/daemons/dmeventd"; \
+ LDLPATH="$$LDLPATH:\$$abs_top_builddir/daemons/dmeventd/plugins/lvm2"; \
+ LDLPATH="$$LDLPATH:\$$abs_top_builddir/daemons/dmeventd/plugins/mirror"; \
+ LDLPATH="$$LDLPATH:\$$abs_top_builddir/daemons/dmeventd/plugins/snapshot"; \
+ echo "export LD_LIBRARY_PATH=\"$$LDLPATH\"" >> $@-t
+ echo 'top_srcdir=$(top_srcdir)' >> $@-t
+ echo 'abs_srcdir=$(abs_srcdir)' >> $@-t
+ echo 'abs_builddir=$(abs_builddir)' >> $@-t
+ echo 'export PATH' >> $@-t
+ echo 'export DM_UDEV_SYNCHRONISATION=$(dm_udev_synchronisation)' >> $@-t
+ chmod a-w $@-t
+ mv $@-t $@
+ @if test "$(srcdir)" != . ; then \
+ echo "Copying tests to builddir."; \
+ cp $(SUPPORT) .; \
+ for f in $(RUN); do cp $$f `echo $$f | sed -e s,^$(srcdir)/,,`; done; \
+ fi
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+.bin-dir-stamp: lvm-wrapper
+ rm -rf bin
+ mkdir bin
+ for i in lvm $$(cat ../tools/.commands); do \
+ ln -s ../lvm-wrapper bin/$$i; \
+ done
+ ln -s "$(abs_top_builddir)/tools/dmsetup" bin/dmsetup
+ ln -s "$(abs_top_builddir)/daemons/clvmd/clvmd" bin/clvmd
+ ln -s "$(abs_top_builddir)/daemons/dmeventd/dmeventd" bin/dmeventd
+ touch $@
+
+lvm-wrapper: Makefile
+ rm -f $@-t $@
+ echo '#!/bin/sh' > $@-t
+ echo 'cmd=$$(echo ./$$0|sed "s,.*/,,")' >> $@-t
+ echo 'test "$$cmd" = lvm &&' >> $@-t
+ echo 'exec "$(abs_top_builddir)/tools/lvm" "$$@"' >> $@-t
+ echo 'exec "$(abs_top_builddir)/tools/lvm" "$$cmd" "$$@"' >> $@-t
+ chmod a-w,a+x $@-t
+ mv $@-t $@
+
+clean:
+ rm -rf init.sh lvm-wrapper bin .bin-dir-stamp
+ if test "$(srcdir)" != . ; then rm -f $(subst $(srcdir)/, ,$(RUN)) lvm2app.sh ; fi
+
+distclean: clean
+ rm -f Makefile
+
+.NOTPARALLEL:
--- /dev/null
+#
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+ifeq ("@DEBUG@", "yes")
+ DEFS += -DDEBUG
+endif
+
+TARGETS =
+test_SOURCES = test.c
+wrapper_SOURCES = test.c
+INCLUDES += -I../../include
+
+UNIT = vgtest.t percent.t
+
+LVMLIBS = @LVM2APP_LIB@ -ldevmapper
+DEPLIBS = $(top_builddir)/liblvm/liblvm2app.so $(top_builddir)/libdm/libdevmapper.so
+
+DEFS += -D_REENTRANT
+
+include $(top_builddir)/make.tmpl
+
+LDFLAGS = -L$(top_builddir)/libdm -L$(top_builddir)/liblvm
+
+ifeq ("@DMEVENTD@", "yes")
+ LVMLIBS += -ldevmapper-event
+ LDFLAGS += -L$(top_builddir)/daemons/dmeventd
+endif
+
+test_OBJECTS = $(test_SOURCES:.c=.o)
+wrapper_OBJECTS = $(wrapper_SOURCES:.c=.o)
+OBJECTS = $(test_OBJECTS)
+
+all: tests test
+
+tests: $(UNIT)
+
+test: $(test_OBJECTS) $(DEPLIBS)
+ $(CC) -o test $(test_OBJECTS) $(CFLAGS) $(LDFLAGS) $(LVMLIBS) $(LIBS) $(READLINE_LIBS)
+
+%.t: %.o $(DEPLIBS)
+ $(CC) -o $@ $(<) $(CFLAGS) $(LDFLAGS) $(LVMLIBS) $(LIBS)
+
+wrapper: $(wrapper_OBJECTS) $(DEPLIBS)
+ $(CC) -o wrapper $(wrapper_OBJECTS) $(CFLAGS) $(LDFLAGS) $(LVMLIBS) $(LIBS) -lreadline
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) && $(SHELL) ./config.status test/api/Makefile
--- /dev/null
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lvm2app.h"
+
+#define assert(x) do { if (!(x)) goto bad; } while (0)
+
+int main(int argc, char *argv[])
+{
+ lvm_t handle;
+ vg_t vg = NULL;
+ lv_t lv;
+ struct lvm_property_value v;
+
+ handle = lvm_init(NULL);
+ assert(handle);
+
+ vg = lvm_vg_open(handle, argv[1], "r", 0);
+ assert(vg);
+
+ lv = lvm_lv_from_name(vg, "snap");
+ assert(lv);
+
+ v = lvm_lv_get_property(lv, "snap_percent");
+ assert(v.is_valid);
+ assert(v.value.integer == PERCENT_0);
+
+ lv = lvm_lv_from_name(vg, "mirr");
+ assert(lv);
+
+ v = lvm_lv_get_property(lv, "copy_percent");
+ assert(v.is_valid);
+ assert(v.value.integer == PERCENT_100);
+
+ lv = lvm_lv_from_name(vg, "snap2");
+ assert(lv);
+
+ v = lvm_lv_get_property(lv, "snap_percent");
+ assert(v.is_valid);
+ assert(v.value.integer == 50 * PERCENT_1);
+
+ lvm_vg_close(vg);
+ return 0;
+
+bad:
+ if (handle && lvm_errno(handle))
+ fprintf(stderr, "LVM Error: %s\n", lvm_errmsg(handle));
+ if (vg)
+ lvm_vg_close(vg);
+ if (handle)
+ lvm_quit(handle);
+ return 1;
+}
--- /dev/null
+#
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+aux prepare_devs 2
+vgcreate -c n -s 4k $vg $devs
+lvcreate -n foo $vg -l 5
+lvcreate -s -n snap $vg/foo -l 2 -c 4k
+lvcreate -s -n snap2 $vg/foo -l 6 -c 4k
+dd if=/dev/urandom of=$DM_DEV_DIR/$vg/snap2 count=1 bs=1024
+lvcreate -m 1 -n mirr $vg -l 1 --mirrorlog core
+lvs
+apitest percent $vg
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <readline/readline.h>
+
+#include "lvm2app.h"
+
+#define MAX_ARGS 64
+
+static int lvm_split(char *str, int *argc, char **argv, int max)
+{
+ char *b = str, *e;
+ *argc = 0;
+
+ while (*b) {
+ while (*b && isspace(*b))
+ b++;
+
+ if ((!*b) || ((*argc == 0)&&(*b == '#')))
+ break;
+
+ e = b;
+ while (*e && !isspace(*e))
+ e++;
+
+ argv[(*argc)++] = b;
+ if (!*e)
+ break;
+ *e++ = '\0';
+ b = e;
+ if (*argc == max)
+ break;
+ }
+
+ return *argc;
+}
+
+static void _show_help(void)
+{
+ printf("'lv_activate vgname lvname: "
+ "Activate an LV\n");
+ printf("'lv_deactivate vgname lvname: "
+ "Deactivate an LV\n");
+ printf("'vg_remove_lv vgname lvname': "
+ "Remove a LV\n");
+ printf("'vg_create_lv_linear vgname lvname size_in_bytes': "
+ "Create a linear LV\n");
+ printf("'scan_vgs': "
+ "Scan the system for LVM metadata\n");
+ printf("'list_vg_names': "
+ "List the names of the VGs that exist in the system\n");
+ printf("'list_vg_ids': "
+ "List the uuids of the VGs that exist in the system\n");
+ printf("'vg_list_pvs vgname': "
+ "List the PVs that exist in VG vgname\n");
+ printf("'pv_list_pvsegs pvname': "
+ "List the PV segments that exist in PV pvname\n");
+ printf("'vg_list_lvs vgname': "
+ "List the LVs that exist in VG vgname\n");
+ printf("'lv_list_lvsegs vgname lvname': "
+ "List the LV segments that exist in LV vgname/lvname\n");
+ printf("'vgs_open': "
+ "List the VGs that are currently open\n");
+ printf("'vgs': "
+ "List all VGs known to the system\n");
+ printf("'vg_extend vgname device: "
+ "Issue a lvm_vg_extend() API call on VG 'vgname'\n");
+ printf("'vg_reduce vgname device: "
+ "Issue a lvm_vg_reduce() API call on VG 'vgname'\n");
+ printf("'vg_open vgname ['r' | 'w']': "
+ "Issue a lvm_vg_open() API call on VG 'vgname'\n");
+ printf("'vg_close vgname': "
+ "Issue a lvm_vg_close() API call on VG 'vgname'\n");
+ printf("'vg_create vgname: "
+ "Issue a lvm_vg_create() to create VG 'vgname'\n");
+ printf("'vg_remove vgname: "
+ "Issue a lvm_vg_remove() to remove VG 'vgname'\n");
+ printf("'config_reload': "
+ "Issue a lvm_config_reload() API to reload LVM config\n");
+ printf("'config_override' device: "
+ "Issue a lvm_config_override() with accept device filter\n");
+ printf("'vg_get_tags vgname': "
+ "List the tags of a VG\n");
+ printf("'lv_get_property vgname lvname property_name': "
+ "Display the value of LV property\n");
+ printf("'vg_get_property vgname property_name': "
+ "Display the value of VG property\n");
+ printf("'pv_get_property pvname property_name': "
+ "Display the value of PV property\n");
+ printf("'vg_set_property vgname property_name': "
+ "Set the value of VG property\n");
+ printf("'lv_get_tags vgname lvname': "
+ "List the tags of a LV\n");
+ printf("'vg_{add|remove}_tag vgname tag': "
+ "Add/remove a tag from a VG\n");
+ printf("'lv_{add|remove}_tag vgname lvname tag': "
+ "Add/remove a tag from a LV\n");
+ printf("'vgname_from_devname device': "
+ "Lookup a vgname from a device name\n");
+ printf("'vgname_from_pvid pvid': "
+ "Lookup a vgname from a pvid\n");
+ printf("'lv_from_uuid vgname lvuuid': "
+ "Lookup an LV from an LV uuid\n");
+ printf("'lv_from_name vgname lvname': "
+ "Lookup an LV from an LV name\n");
+ printf("'pv_from_uuid vgname pvuuid': "
+ "Lookup an LV from an LV uuid\n");
+ printf("'pv_from_name vgname pvname': "
+ "Lookup an LV from an LV name\n");
+ printf("'quit': exit the program\n");
+}
+
+static struct dm_hash_table *_vgid_hash = NULL;
+static struct dm_hash_table *_vgname_hash = NULL;
+static struct dm_hash_table *_pvname_hash = NULL;
+static struct dm_hash_table *_lvname_hash = NULL;
+
+static void _hash_destroy_single(struct dm_hash_table **htable)
+{
+ if (htable && *htable) {
+ dm_hash_destroy(*htable);
+ *htable = NULL;
+ }
+}
+
+static void _hash_destroy(void)
+{
+ _hash_destroy_single(&_vgname_hash);
+ _hash_destroy_single(&_vgid_hash);
+ _hash_destroy_single(&_pvname_hash);
+ _hash_destroy_single(&_lvname_hash);
+}
+
+static int _hash_create(void)
+{
+ if (!(_vgname_hash = dm_hash_create(128)))
+ return 0;
+ if (!(_pvname_hash = dm_hash_create(128))) {
+ _hash_destroy_single(&_vgname_hash);
+ return 0;
+ }
+ if (!(_lvname_hash = dm_hash_create(128))) {
+ _hash_destroy_single(&_vgname_hash);
+ _hash_destroy_single(&_pvname_hash);
+ return 0;
+ }
+ if (!(_vgid_hash = dm_hash_create(128))) {
+ _hash_destroy_single(&_vgname_hash);
+ _hash_destroy_single(&_pvname_hash);
+ _hash_destroy_single(&_lvname_hash);
+ return 0;
+ }
+ return 1;
+}
+
+/* FIXME: this should be per vg */
+static lv_t _lookup_lv_by_name(const char *name)
+{
+ lv_t lv;
+
+ if (!name) {
+ printf ("Invalid LV name\n");
+ return NULL;
+ }
+ if (!(lv = dm_hash_lookup(_lvname_hash, name))) {
+ printf ("Can't find %s in LVs - run vg_create_lv first\n",
+ name);
+ return NULL;
+ }
+ return lv;
+}
+
+static vg_t _lookup_vg_by_name(char **argv, int argc)
+{
+ vg_t vg;
+
+ if (argc < 2) {
+ printf ("Please enter vg_name\n");
+ return NULL;
+ }
+ if (!(vg = dm_hash_lookup(_vgid_hash, argv[1])) &&
+ !(vg = dm_hash_lookup(_vgname_hash, argv[1]))) {
+ printf ("Can't find %s in open VGs - run vg_open first\n",
+ argv[1]);
+ return NULL;
+ }
+ return vg;
+}
+
+static pv_t _lookup_pv_by_name(const char *name)
+{
+ pv_t pv;
+
+ if (!(pv = dm_hash_lookup(_pvname_hash, name))) {
+ printf ("Can't find %s in open PVs - run vg_open first\n",
+ name);
+ return NULL;
+ }
+ return pv;
+}
+
+static void _add_lvs_to_lvname_hash(struct dm_list *lvs)
+{
+ struct lvm_lv_list *lvl;
+ dm_list_iterate_items(lvl, lvs) {
+ /* Concatenate VG name with LV name */
+ dm_hash_insert(_lvname_hash, lvm_lv_get_name(lvl->lv), lvl->lv);
+ }
+}
+
+static void _add_pvs_to_pvname_hash(struct dm_list *pvs)
+{
+ struct lvm_pv_list *pvl;
+ dm_list_iterate_items(pvl, pvs) {
+ dm_hash_insert(_pvname_hash, lvm_pv_get_name(pvl->pv), pvl->pv);
+ }
+}
+
+static void _remove_device_from_pvname_hash(struct dm_list *pvs, const char *name)
+{
+ struct lvm_pv_list *pvl;
+ dm_list_iterate_items(pvl, pvs) {
+ if (!strncmp(lvm_pv_get_name(pvl->pv), name, strlen(name)))
+ dm_hash_remove(_pvname_hash, name);
+ }
+}
+static void _add_device_to_pvname_hash(struct dm_list *pvs, const char *name)
+{
+ struct lvm_pv_list *pvl;
+ dm_list_iterate_items(pvl, pvs) {
+ if (!strncmp(lvm_pv_get_name(pvl->pv), name, strlen(name)))
+ dm_hash_insert(_pvname_hash, name, pvl->pv);
+ }
+}
+
+static void _vg_reduce(char **argv, int argc, lvm_t libh)
+{
+ vg_t vg;
+ struct dm_list *pvs;
+
+ if (argc < 2) {
+ printf ("Please enter vg_name\n");
+ return;
+ }
+ if (!(vg = dm_hash_lookup(_vgid_hash, argv[1])) &&
+ !(vg = dm_hash_lookup(_vgname_hash, argv[1]))) {
+ printf ("VG not open\n");
+ return;
+ }
+ if (lvm_vg_reduce(vg, argv[2])) {
+ printf("Error reducing %s by %s\n", argv[1], argv[2]);
+ return;
+ }
+
+ printf("Success reducing vg %s by %s\n", argv[1], argv[2]);
+
+ /*
+ * Add the device into the hashes for lookups
+ */
+ pvs = lvm_vg_list_pvs(vg);
+ if (pvs && !dm_list_empty(pvs))
+ _remove_device_from_pvname_hash(pvs, argv[2]);
+}
+
+/* Print "Error" or "Success" depending on lvm status */
+static int _lvm_status_to_pass_fail(int rc)
+{
+ if (rc)
+ printf("Error ");
+ else
+ printf("Success ");
+ return rc;
+}
+static void _config_override(char **argv, int argc, lvm_t libh)
+{
+ int rc;
+ char tmp[64];
+
+ if (argc < 2) {
+ printf ("Please enter device\n");
+ return;
+ }
+ snprintf(tmp, 63, "devices{filter=[\"a|%s|\", \"r|.*|\"]}", argv[1]);
+ rc = lvm_config_override(libh, tmp);
+ _lvm_status_to_pass_fail(rc);
+ printf("overriding LVM configuration\n");
+}
+
+static void _config_reload(char **argv, int argc, lvm_t libh)
+{
+ int rc;
+ rc = lvm_config_reload(libh);
+ _lvm_status_to_pass_fail(rc);
+ printf("reloading LVM configuration\n");
+}
+
+static void _vg_extend(char **argv, int argc, lvm_t libh)
+{
+ vg_t vg;
+ struct dm_list *pvs;
+
+ if (argc < 2) {
+ printf ("Please enter vg_name\n");
+ return;
+ }
+ if (!(vg = dm_hash_lookup(_vgid_hash, argv[1])) &&
+ !(vg = dm_hash_lookup(_vgname_hash, argv[1]))) {
+ printf ("VG not open\n");
+ return;
+ }
+ if (lvm_vg_extend(vg, argv[2])) {
+ printf("Error extending %s with %s\n", argv[1], argv[2]);
+ return;
+ }
+
+ printf("Success extending vg %s with %s\n", argv[1], argv[2]);
+
+ /*
+ * Add the device into the hashes for lookups
+ */
+ pvs = lvm_vg_list_pvs(vg);
+ if (pvs && !dm_list_empty(pvs))
+ _add_device_to_pvname_hash(pvs, argv[2]);
+}
+
+static void _vg_open(char **argv, int argc, lvm_t libh)
+{
+ vg_t vg;
+ struct dm_list *lvs;
+ struct dm_list *pvs;
+
+ if (argc < 2) {
+ printf ("Please enter vg_name\n");
+ return;
+ }
+ if ((vg = dm_hash_lookup(_vgid_hash, argv[1])) ||
+ (vg = dm_hash_lookup(_vgname_hash, argv[1]))) {
+ printf ("VG already open\n");
+ return;
+ }
+ if (argc < 3)
+ vg = lvm_vg_open(libh, argv[1], "r", 0);
+ else
+ vg = lvm_vg_open(libh, argv[1], argv[2], 0);
+ if (!vg || !lvm_vg_get_name(vg)) {
+ printf("Error opening %s\n", argv[1]);
+ return;
+ }
+
+ printf("Success opening vg %s\n", argv[1]);
+ dm_hash_insert(_vgname_hash, lvm_vg_get_name(vg), vg);
+ dm_hash_insert(_vgid_hash, lvm_vg_get_uuid(vg), vg);
+
+ /*
+ * Add the LVs and PVs into the hashes for lookups
+ */
+ lvs = lvm_vg_list_lvs(vg);
+ if (lvs && !dm_list_empty(lvs))
+ _add_lvs_to_lvname_hash(lvs);
+ pvs = lvm_vg_list_pvs(vg);
+ if (pvs && !dm_list_empty(pvs))
+ _add_pvs_to_pvname_hash(pvs);
+}
+/* Lookup the vg and remove it from the vgname and vgid hashes */
+static vg_t _lookup_and_remove_vg(const char *vgname)
+{
+ vg_t vg=NULL;
+
+ if ((vg = dm_hash_lookup(_vgname_hash, vgname))) {
+ dm_hash_remove(_vgid_hash, lvm_vg_get_uuid(vg));
+ dm_hash_remove(_vgname_hash, lvm_vg_get_name(vg));
+ }
+ if (!vg && (vg = dm_hash_lookup(_vgid_hash, vgname))) {
+ dm_hash_remove(_vgid_hash, lvm_vg_get_uuid(vg));
+ dm_hash_remove(_vgname_hash, lvm_vg_get_name(vg));
+ }
+ return vg;
+}
+
+static void _vg_write(char **argv, int argc)
+{
+ vg_t vg;
+ int rc = 0;
+
+ if (argc < 2) {
+ printf ("Please enter vg_name\n");
+ return;
+ }
+ vg = _lookup_vg_by_name(argv, argc);
+ if (!vg) {
+ printf("Can't find vg_name %s\n", argv[1]);
+ return;
+ }
+ rc = lvm_vg_write(vg);
+ _lvm_status_to_pass_fail(rc);
+ printf("writing VG %s\n", lvm_vg_get_name(vg));
+}
+
+static void _vg_create(char **argv, int argc, lvm_t libh)
+{
+ vg_t vg;
+
+ if (argc < 2) {
+ printf ("Please enter vg_name\n");
+ return;
+ }
+ vg = lvm_vg_create(libh, argv[1]);
+ if (!vg || !lvm_vg_get_name(vg)) {
+ printf("Error creating %s\n", argv[1]);
+ return;
+ }
+
+ printf("Success creating vg %s\n", argv[1]);
+ dm_hash_insert(_vgname_hash, lvm_vg_get_name(vg), vg);
+ dm_hash_insert(_vgid_hash, lvm_vg_get_uuid(vg), vg);
+}
+
+static void _vg_remove(char **argv, int argc)
+{
+ vg_t vg;
+ int rc = 0;
+
+ if (argc < 2) {
+ printf ("Please enter vg_name\n");
+ return;
+ }
+ vg = _lookup_vg_by_name(argv, argc);
+ if (!vg) {
+ printf("Can't find vg_name %s\n", argv[1]);
+ return;
+ }
+ rc = lvm_vg_remove(vg);
+ _lvm_status_to_pass_fail(rc);
+ printf("removing VG\n");
+}
+
+static void _vg_close(char **argv, int argc)
+{
+ vg_t vg;
+ int rc = 0;
+
+ if (argc < 2) {
+ printf ("Please enter vg_name\n");
+ return;
+ }
+ vg = _lookup_and_remove_vg(argv[1]);
+ if (!vg) {
+ printf("Can't find vg_name %s\n", argv[1]);
+ return;
+ }
+ rc = lvm_vg_close(vg);
+ _lvm_status_to_pass_fail(rc);
+ printf("closing VG\n");
+}
+
+static void _show_one_vg(vg_t vg)
+{
+ printf("%s (%s): sz=%"PRIu64", free=%"PRIu64", #pv=%"PRIu64
+ ", seq#=%"PRIu64"\n",
+ lvm_vg_get_name(vg), lvm_vg_get_uuid(vg),
+ lvm_vg_get_size(vg), lvm_vg_get_free_size(vg),
+ lvm_vg_get_pv_count(vg), lvm_vg_get_seqno(vg));
+}
+
+static void _print_pv(pv_t pv)
+{
+ if (!pv)
+ return;
+ printf("%s (%s): size=%"PRIu64", free=%"PRIu64
+ ", dev_size=%"PRIu64", mda_count=%"PRIu64"\n",
+ lvm_pv_get_name(pv), lvm_pv_get_uuid(pv),
+ lvm_pv_get_size(pv), lvm_pv_get_free(pv),
+ lvm_pv_get_dev_size(pv),
+ lvm_pv_get_mda_count(pv));
+}
+
+static void _print_lv(vg_t vg, lv_t lv)
+{
+ if (!lv)
+ return;
+ printf("%s/%s (%s): size=%"PRIu64", %sACTIVE / %sSUSPENDED\n",
+ lvm_vg_get_name(vg),
+ lvm_lv_get_name(lv), lvm_lv_get_uuid(lv),
+ lvm_lv_get_size(lv),
+ lvm_lv_is_active(lv) ? "" : "IN",
+ lvm_lv_is_suspended(lv) ? "" : "NOT ");
+}
+
+static void _list_open_vgs(void)
+{
+ dm_hash_iter(_vgid_hash, (dm_hash_iterate_fn) _show_one_vg);
+}
+
+static void _pvs_in_vg(char **argv, int argc)
+{
+ struct dm_list *pvs;
+ struct lvm_pv_list *pvl;
+ vg_t vg;
+
+ if (!(vg = _lookup_vg_by_name(argv, argc)))
+ return;
+ pvs = lvm_vg_list_pvs(vg);
+ if (!pvs || dm_list_empty(pvs)) {
+ printf("No PVs in VG %s\n", lvm_vg_get_name(vg));
+ return;
+ }
+ printf("PVs in VG %s:\n", lvm_vg_get_name(vg));
+ dm_list_iterate_items(pvl, pvs) {
+ _print_pv(pvl->pv);
+ }
+}
+
+static void _print_property_value(const char *name,
+ struct lvm_property_value v)
+{
+ if (!v.is_valid)
+ printf("%s = INVALID\n", name);
+ else if (v.is_string)
+ printf("%s = %s\n", name, v.value.string);
+ else
+ printf("%s = %"PRIu64"\n", name, v.value.integer);
+}
+
+static void _pvsegs_in_pv(char **argv, int argc)
+{
+ struct dm_list *pvsegs;
+ struct lvm_pvseg_list *pvl;
+ pv_t pv;
+
+ if (!(pv = _lookup_pv_by_name(argv[1])))
+ return;
+ pvsegs = lvm_pv_list_pvsegs(pv);
+ if (!pvsegs || dm_list_empty(pvsegs)) {
+ printf("No PV segments in pv %s\n", argv[1]);
+ return;
+ }
+ printf("PV segments in pv %s:\n", argv[1]);
+ dm_list_iterate_items(pvl, pvsegs) {
+ struct lvm_property_value v;
+ v = lvm_pvseg_get_property(pvl->pvseg, "pvseg_start");
+ _print_property_value("pvseg_start", v);
+ v = lvm_pvseg_get_property(pvl->pvseg, "pvseg_size");
+ _print_property_value("pvseg_size", v);
+ }
+}
+
+static void _scan_vgs(lvm_t libh)
+{
+ lvm_scan(libh);
+}
+
+static void _list_vg_names(lvm_t libh)
+{
+ struct dm_list *list;
+ struct lvm_str_list *strl;
+
+ list = lvm_list_vg_names(libh);
+ printf("VG names:\n");
+ dm_list_iterate_items(strl, list) {
+ printf("%s\n", strl->str);
+ }
+}
+
+static void _list_vg_ids(lvm_t libh)
+{
+ struct dm_list *list;
+ struct lvm_str_list *strl;
+
+ list = lvm_list_vg_uuids(libh);
+ printf("VG uuids:\n");
+ dm_list_iterate_items(strl, list) {
+ printf("%s\n", strl->str);
+ }
+}
+
+static void _display_tags(struct dm_list *list)
+{
+ struct lvm_str_list *strl;
+ if (dm_list_empty(list)) {
+ printf("No tags exist\n");
+ return;
+ } else if (!list) {
+ printf("Error obtaining tags\n");
+ return;
+ }
+ dm_list_iterate_items(strl, list) {
+ printf("%s\n", strl->str);
+ }
+}
+
+static void _vg_get_tags(char **argv, int argc)
+{
+ vg_t vg;
+
+ if (!(vg = _lookup_vg_by_name(argv, argc)))
+ return;
+ printf("VG tags:\n");
+ _display_tags(lvm_vg_get_tags(vg));
+}
+
+static void _vg_tag(char **argv, int argc, int add)
+{
+ vg_t vg;
+
+ if (argc < 3) {
+ printf("Please enter vgname, tag\n");
+ return;
+ }
+ if (!(vg = _lookup_vg_by_name(argv, argc)))
+ return;
+ if (add && lvm_vg_add_tag(vg, argv[2]))
+ printf("Error ");
+ else if (!add && lvm_vg_remove_tag(vg, argv[2])){
+ printf("Error ");
+ } else {
+ printf("Success ");
+ }
+ printf("%s tag %s to VG %s\n",
+ add ? "adding":"removing", argv[2], argv[1]);
+}
+
+static void _pv_get_property(char **argv, int argc)
+{
+ pv_t pv;
+ struct lvm_property_value v;
+
+ if (argc < 3) {
+ printf("Please enter pvname, field_id\n");
+ return;
+ }
+ if (!(pv = _lookup_pv_by_name(argv[1])))
+ return;
+ v = lvm_pv_get_property(pv, argv[2]);
+ _print_property_value(argv[2], v);
+}
+
+static void _vg_get_property(char **argv, int argc)
+{
+ vg_t vg;
+ struct lvm_property_value v;
+
+ if (argc < 3) {
+ printf("Please enter vgname, field_id\n");
+ return;
+ }
+ if (!(vg = _lookup_vg_by_name(argv, argc)))
+ return;
+ v = lvm_vg_get_property(vg, argv[2]);
+ _print_property_value(argv[2], v);
+}
+
+static void _lv_get_property(char **argv, int argc)
+{
+ lv_t lv;
+ struct lvm_property_value v;
+
+ if (argc < 4) {
+ printf("Please enter vgname, lvname, field_id\n");
+ return;
+ }
+ if (!(lv = _lookup_lv_by_name(argv[2])))
+ return;
+ v = lvm_lv_get_property(lv, argv[3]);
+ _print_property_value(argv[3], v);
+}
+
+static void _vg_set_property(char **argv, int argc)
+{
+ vg_t vg;
+ struct lvm_property_value value;
+ int rc;
+
+ if (argc < 4) {
+ printf("Please enter vgname, field_id, value\n");
+ return;
+ }
+ if (!(vg = _lookup_vg_by_name(argv, argc)))
+ return;
+ value = lvm_vg_get_property(vg, argv[2]);
+ if (!value.is_valid) {
+ printf("Error obtaining property value\n");
+ return;
+ }
+ if (value.is_string)
+ value.value.string = argv[3];
+ else
+ value.value.integer = atoi(argv[3]);
+ rc = lvm_vg_set_property(vg, argv[2], &value);
+ if (rc)
+ printf("Error ");
+ else
+ printf("Success ");
+ printf("setting value of property %s in VG %s\n",
+ argv[2], argv[1]);
+}
+
+static void _lv_get_tags(char **argv, int argc)
+{
+ lv_t lv;
+
+ if (argc < 3) {
+ printf("Please enter vgname, lvname\n");
+ return;
+ }
+ if (!(lv = _lookup_lv_by_name(argv[2])))
+ return;
+ printf("LV tags:\n");
+ _display_tags(lvm_lv_get_tags(lv));
+}
+
+static void _lv_tag(char **argv, int argc, int add)
+{
+ lv_t lv;
+
+ if (argc < 3) {
+ printf("Please enter vgname, lvname\n");
+ return;
+ }
+ if (!(lv = _lookup_lv_by_name(argv[2])))
+ return;
+ if (add && lvm_lv_add_tag(lv, argv[3]))
+ printf("Error ");
+ else if (!add && lvm_lv_remove_tag(lv, argv[3])){
+ printf("Error ");
+ } else {
+ printf("Success ");
+ }
+ printf("%s tag %s to LV %s\n",
+ add ? "adding":"removing", argv[3], argv[2]);
+}
+
+static void _lv_from_uuid(char **argv, int argc)
+{
+ vg_t vg;
+
+ if (argc < 3) {
+ printf("Please enter vgname, lv_uuid\n");
+ return;
+ }
+ if (!(vg = _lookup_vg_by_name(argv, argc)))
+ return;
+ _print_lv(vg, lvm_lv_from_uuid(vg, argv[2]));
+}
+
+static void _lv_from_name(char **argv, int argc)
+{
+ vg_t vg;
+
+ if (argc < 3) {
+ printf("Please enter vgname, lv_uuid\n");
+ return;
+ }
+ if (!(vg = _lookup_vg_by_name(argv, argc)))
+ return;
+ _print_lv(vg, lvm_lv_from_name(vg, argv[2]));
+}
+
+static void _pv_from_uuid(char **argv, int argc)
+{
+ vg_t vg;
+
+ if (argc < 3) {
+ printf("Please enter vgname, pv_uuid\n");
+ return;
+ }
+ if (!(vg = _lookup_vg_by_name(argv, argc)))
+ return;
+ _print_pv(lvm_pv_from_uuid(vg, argv[2]));
+}
+
+static void _pv_from_name(char **argv, int argc)
+{
+ vg_t vg;
+
+ if (argc < 3) {
+ printf("Please enter vgname, pv_uuid\n");
+ return;
+ }
+ if (!(vg = _lookup_vg_by_name(argv, argc)))
+ return;
+ _print_pv(lvm_pv_from_name(vg, argv[2]));
+}
+
+static void _vgname_from_pvid(char **argv, int argc, lvm_t libh)
+{
+ const char *vgname;
+
+ if (argc < 1) {
+ printf("Please enter pvid\n");
+ return;
+ }
+ if (!(vgname = lvm_vgname_from_pvid(libh, argv[1]))) {
+ printf("Error ");
+ } else {
+ printf("Success ");
+ }
+ printf("looking up vgname=%s from PVID=%s\n",
+ vgname, argv[1]);
+}
+static void _vgname_from_devname(char **argv, int argc, lvm_t libh)
+{
+ const char *vgname;
+
+ if (argc < 1) {
+ printf("Please enter device\n");
+ return;
+ }
+ if (!(vgname = lvm_vgname_from_device(libh, argv[1]))) {
+ printf("Error ");
+ } else {
+ printf("Success ");
+ }
+ printf("looking up vgname=%s from device name=%s\n",
+ vgname, argv[1]);
+}
+static void _lvs_in_vg(char **argv, int argc)
+{
+ struct dm_list *lvs;
+ struct lvm_lv_list *lvl;
+ vg_t vg;
+
+ if (!(vg = _lookup_vg_by_name(argv, argc)))
+ return;
+ lvs = lvm_vg_list_lvs(vg);
+ if (!lvs || dm_list_empty(lvs)) {
+ printf("No LVs in VG %s\n", lvm_vg_get_name(vg));
+ return;
+ }
+ printf("LVs in VG %s:\n", lvm_vg_get_name(vg));
+ dm_list_iterate_items(lvl, lvs) {
+ _print_lv(vg, lvl->lv);
+ }
+}
+
+static void _lvsegs_in_lv(char **argv, int argc)
+{
+ struct dm_list *lvsegs;
+ struct lvm_lvseg_list *lvl;
+ lv_t lv;
+
+ if (!(lv = _lookup_lv_by_name(argv[2])))
+ return;
+ lvsegs = lvm_lv_list_lvsegs(lv);
+ if (!lvsegs || dm_list_empty(lvsegs)) {
+ printf("No LV segments in lv %s\n", lvm_lv_get_name(lv));
+ return;
+ }
+ printf("LV segments in lv %s:\n", lvm_lv_get_name(lv));
+ dm_list_iterate_items(lvl, lvsegs) {
+ struct lvm_property_value v;
+ v = lvm_lvseg_get_property(lvl->lvseg, "segtype");
+ _print_property_value("segtype", v);
+ v = lvm_lvseg_get_property(lvl->lvseg, "seg_start_pe");
+ _print_property_value("seg_start_pe", v);
+ v = lvm_lvseg_get_property(lvl->lvseg, "seg_size");
+ _print_property_value("seg_size", v);
+ }
+}
+
+static void _lv_deactivate(char **argv, int argc)
+{
+ lv_t lv;
+ int rc=0;
+
+ if (argc < 3) {
+ printf("Please enter vgname, lvname\n");
+ return;
+ }
+ if (!(lv = _lookup_lv_by_name(argv[2])))
+ return;
+ rc = lvm_lv_deactivate(lv);
+ _lvm_status_to_pass_fail(rc);
+ printf("De-activating LV %s in VG %s\n",
+ argv[2], argv[1]);
+}
+static void _lv_activate(char **argv, int argc)
+{
+ lv_t lv;
+ int rc=0;
+
+ if (argc < 3) {
+ printf("Please enter vgname, lvname\n");
+ return;
+ }
+ if (!(lv = _lookup_lv_by_name(argv[2])))
+ return;
+ rc = lvm_lv_activate(lv);
+ _lvm_status_to_pass_fail(rc);
+ printf("activating LV %s in VG %s\n",
+ argv[2], argv[1]);
+}
+
+static void _vg_remove_lv(char **argv, int argc)
+{
+ lv_t lv;
+
+ if (argc < 3) {
+ printf("Please enter vgname, lvname\n");
+ return;
+ }
+ if (!(lv = _lookup_lv_by_name(argv[2])))
+ return;
+ if (lvm_vg_remove_lv(lv))
+ printf("Error ");
+ else {
+ printf("Success ");
+ dm_hash_remove(_lvname_hash, argv[2]);
+ }
+ printf("removing LV %s in VG %s\n",
+ argv[2], argv[1]);
+}
+
+static void _vg_create_lv_linear(char **argv, int argc)
+{
+ vg_t vg;
+ lv_t lv;
+
+ if (argc < 4) {
+ printf("Please enter vgname, lvname, and size\n");
+ return;
+ }
+ if (!(vg = _lookup_vg_by_name(argv, argc)))
+ return;
+ lv = lvm_vg_create_lv_linear(vg, argv[2], atol(argv[3]));
+ if (!lv)
+ printf("Error ");
+ else {
+ printf("Success ");
+ dm_hash_insert(_lvname_hash, argv[2], lv);
+ }
+ printf("creating LV %s in VG %s\n",
+ argv[2], argv[1]);
+}
+
+static int lvmapi_test_shell(lvm_t libh)
+{
+ int argc;
+ char *input = NULL, *args[MAX_ARGS], **argv;
+
+ _hash_create();
+ argc=0;
+ while (1) {
+ free(input);
+ input = readline("liblvm> ");
+
+ /* EOF */
+ if (!input) {
+ printf("\n");
+ break;
+ }
+
+ /* empty line */
+ if (!*input)
+ continue;
+
+ argv = args;
+
+ if (lvm_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) {
+ printf("Too many arguments, sorry.");
+ continue;
+ }
+
+ if (!strcmp(argv[0], "lvm")) {
+ argv++;
+ argc--;
+ }
+
+ if (!argc)
+ continue;
+
+ if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) {
+ printf("Exiting.\n");
+ break;
+ } else if (!strcmp(argv[0], "?") || !strcmp(argv[0], "help")) {
+ _show_help();
+ } else if (!strcmp(argv[0], "config_reload")) {
+ _config_reload(argv, argc, libh);
+ } else if (!strcmp(argv[0], "config_override")) {
+ _config_override(argv, argc, libh);
+ } else if (!strcmp(argv[0], "vg_extend")) {
+ _vg_extend(argv, argc, libh);
+ } else if (!strcmp(argv[0], "vg_reduce")) {
+ _vg_reduce(argv, argc, libh);
+ } else if (!strcmp(argv[0], "vg_write")) {
+ _vg_write(argv, argc);
+ } else if (!strcmp(argv[0], "vg_open")) {
+ _vg_open(argv, argc, libh);
+ } else if (!strcmp(argv[0], "vg_close")) {
+ _vg_close(argv, argc);
+ } else if (!strcmp(argv[0], "vg_create")) {
+ _vg_create(argv, argc, libh);
+ } else if (!strcmp(argv[0], "vg_remove")) {
+ _vg_remove(argv, argc);
+ } else if (!strcmp(argv[0], "lv_activate")) {
+ _lv_activate(argv, argc);
+ } else if (!strcmp(argv[0], "lv_deactivate")) {
+ _lv_deactivate(argv, argc);
+ } else if (!strcmp(argv[0], "vg_remove_lv")) {
+ _vg_remove_lv(argv, argc);
+ } else if (!strcmp(argv[0], "vgs_open")) {
+ _list_open_vgs();
+ } else if (!strcmp(argv[0], "vg_list_pvs")) {
+ _pvs_in_vg(argv, argc);
+ } else if (!strcmp(argv[0], "pv_list_pvsegs")) {
+ _pvsegs_in_pv(argv, argc);
+ } else if (!strcmp(argv[0], "vg_list_lvs")) {
+ _lvs_in_vg(argv, argc);
+ } else if (!strcmp(argv[0], "lv_list_lvsegs")) {
+ _lvsegs_in_lv(argv, argc);
+ } else if (!strcmp(argv[0], "list_vg_names")) {
+ _list_vg_names(libh);
+ } else if (!strcmp(argv[0], "list_vg_ids")) {
+ _list_vg_ids(libh);
+ } else if (!strcmp(argv[0], "scan_vgs")) {
+ _scan_vgs(libh);
+ } else if (!strcmp(argv[0], "vg_create_lv_linear")) {
+ _vg_create_lv_linear(argv, argc);
+ } else if (!strcmp(argv[0], "vg_add_tag")) {
+ _vg_tag(argv, argc, 1);
+ } else if (!strcmp(argv[0], "vg_remove_tag")) {
+ _vg_tag(argv, argc, 0);
+ } else if (!strcmp(argv[0], "vg_get_tags")) {
+ _vg_get_tags(argv, argc);
+ } else if (!strcmp(argv[0], "lv_get_property")) {
+ _lv_get_property(argv, argc);
+ } else if (!strcmp(argv[0], "vg_get_property")) {
+ _vg_get_property(argv, argc);
+ } else if (!strcmp(argv[0], "pv_get_property")) {
+ _pv_get_property(argv, argc);
+ } else if (!strcmp(argv[0], "vg_set_property")) {
+ _vg_set_property(argv, argc);
+ } else if (!strcmp(argv[0], "lv_add_tag")) {
+ _lv_tag(argv, argc, 1);
+ } else if (!strcmp(argv[0], "lv_remove_tag")) {
+ _lv_tag(argv, argc, 0);
+ } else if (!strcmp(argv[0], "lv_get_tags")) {
+ _lv_get_tags(argv, argc);
+ } else if (!strcmp(argv[0], "vgname_from_devname")) {
+ _vgname_from_devname(argv, argc, libh);
+ } else if (!strcmp(argv[0], "vgname_from_pvid")) {
+ _vgname_from_pvid(argv, argc, libh);
+ } else if (!strcmp(argv[0], "lv_from_uuid")) {
+ _lv_from_uuid(argv, argc);
+ } else if (!strcmp(argv[0], "lv_from_name")) {
+ _lv_from_name(argv, argc);
+ } else if (!strcmp(argv[0], "pv_from_uuid")) {
+ _pv_from_uuid(argv, argc);
+ } else if (!strcmp(argv[0], "pv_from_name")) {
+ _pv_from_name(argv, argc);
+ } else {
+ printf ("Unrecognized command %s\n", argv[0]);
+ }
+ }
+
+ dm_hash_iter(_vgname_hash, (dm_hash_iterate_fn) lvm_vg_close);
+ _hash_destroy();
+ free(input);
+ return 0;
+}
+
+int main (int argc, char *argv[])
+{
+ lvm_t libh;
+
+ libh = lvm_init(NULL);
+ if (!libh) {
+ printf("Unable to open lvm library instance\n");
+ return 1;
+ }
+
+ printf("Library version: %s\n", lvm_library_get_version());
+ lvmapi_test_shell(libh);
+
+ lvm_quit(libh);
+ return 0;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * Unit test case for vgcreate and related APIs.
+ * # gcc -g vgcreate.c -I../../liblvm -I../../include -L../../liblvm \
+ * -L../../libdm -ldevmapper -llvm2app
+ * # export LD_LIBRARY_PATH=`pwd`/../../libdm:`pwd`/../../liblvm
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "lvm2app.h"
+
+lvm_t handle;
+vg_t vg;
+const char *vg_name;
+#define MAX_DEVICES 16
+const char *device[MAX_DEVICES];
+uint64_t size = 1024;
+
+#define vg_create(vg_name) \
+ printf("Creating VG %s\n", vg_name); \
+ vg = lvm_vg_create(handle, vg_name); \
+ if (!vg) { \
+ fprintf(stderr, "Error creating volume group %s\n", vg_name); \
+ goto bad; \
+ }
+#define vg_extend(vg, dev) \
+ printf("Extending VG %s by %s\n", vg_name, dev); \
+ status = lvm_vg_extend(vg, dev); \
+ if (status) { \
+ fprintf(stderr, "Error extending volume group %s " \
+ "with device %s\n", vg_name, dev); \
+ goto bad; \
+ }
+#define vg_commit(vg) \
+ printf("Committing VG %s to disk\n", vg_name); \
+ status = lvm_vg_write(vg); \
+ if (status) { \
+ fprintf(stderr, "Commit of volume group '%s' failed\n", \
+ lvm_vg_get_name(vg)); \
+ goto bad; \
+ }
+#define vg_open(vg_name, mode) \
+ printf("Opening VG %s %s\n", vg_name, mode); \
+ vg = lvm_vg_open(handle, vg_name, mode, 0); \
+ if (!vg) { \
+ fprintf(stderr, "Error opening volume group %s\n", vg_name); \
+ goto bad; \
+ }
+#define vg_close(vg) \
+ printf("Closing VG %s\n", vg_name); \
+ if (lvm_vg_close(vg)) { \
+ fprintf(stderr, "Error closing volume group %s\n", vg_name); \
+ goto bad; \
+ }
+#define vg_reduce(vg, dev) \
+ printf("Reducing VG %s by %s\n", vg_name, dev); \
+ status = lvm_vg_reduce(vg, dev); \
+ if (status) { \
+ fprintf(stderr, "Error reducing volume group %s " \
+ "by device %s\n", vg_name, dev); \
+ goto bad; \
+ }
+#define vg_remove(vg) \
+ printf("Removing VG %s from system\n", vg_name); \
+ status = lvm_vg_remove(vg); \
+ if (status) { \
+ fprintf(stderr, "Revmoval of volume group '%s' failed\n", \
+ vg_name); \
+ goto bad; \
+ }
+
+static int init_vgtest(int argc, char *argv[])
+{
+ int i;
+
+ if (argc < 4) {
+ fprintf(stderr, "Usage: %s <vgname> <pv1> <pv2> [... <pvN> ]",
+ argv[0]);
+ return -1;
+ }
+ vg_name = argv[1];
+ for(i=2; i<MAX_DEVICES && i < argc; i++) {
+ device[i-2] = argv[i];
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int status;
+
+ if (init_vgtest(argc, argv) < 0)
+ goto bad;
+
+ /* FIXME: make the below messages verbose-only and print PASS/FAIL*/
+ printf("Opening LVM\n");
+ handle = lvm_init(NULL);
+ if (!handle) {
+ fprintf(stderr, "Unable to lvm_init\n");
+ goto bad;
+ }
+
+ printf("Library version: %s\n", lvm_library_get_version());
+ vg_create(vg_name);
+ vg_extend(vg, device[0]);
+
+ printf("Setting VG %s extent_size to %"PRIu64"\n", vg_name, size);
+ status = lvm_vg_set_extent_size(vg, size);
+ if (status) {
+ fprintf(stderr, "Can not set physical extent "
+ "size '%"PRIu64"' for '%s'\n",
+ size, vg_name);
+ goto bad;
+ }
+
+ vg_commit(vg);
+ vg_close(vg);
+
+ vg_open(vg_name, "r");
+ vg_close(vg);
+
+ vg_open(vg_name, "w");
+ vg_extend(vg, device[1]);
+ vg_reduce(vg, device[0]);
+ vg_commit(vg);
+ vg_close(vg);
+
+ vg_open(vg_name, "w");
+ vg_extend(vg, device[0]);
+ vg_commit(vg);
+ vg_close(vg);
+
+ vg_open(vg_name, "w");
+ vg_remove(vg);
+ vg_commit(vg);
+ vg_close(vg);
+
+ lvm_quit(handle);
+ printf("liblvm vgcreate unit test PASS\n");
+ _exit(0);
+bad:
+ printf("liblvm vgcreate unit test FAIL\n");
+ if (handle && lvm_errno(handle))
+ fprintf(stderr, "LVM Error: %s\n", lvm_errmsg(handle));
+ if (vg)
+ lvm_vg_close(vg);
+ if (handle)
+ lvm_quit(handle);
+ _exit(-1);
+}
--- /dev/null
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#
+# tests lvm2app library
+#
+
+. ./test-utils.sh
+aux prepare_devs 2
+pvcreate $dev1 $dev2
+apitest vgtest $vg1 $dev1 $dev2
--- /dev/null
+#!/bin/bash
+
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# check.sh: assert various things about volumes
+
+# USAGE
+# check linear VG LV
+# check lv_on VG LV PV
+
+# check mirror VG LV [LOGDEV|core]
+# check mirror_nonredundant VG LV
+# check mirror_legs VG LV N
+# check mirror_images_on VG LV DEV [DEV...]
+
+# ...
+
+set -e -o pipefail
+
+lvl() {
+ lvs -a --noheadings "$@"
+}
+
+lvdevices() {
+ lvl -odevices "$@" | sed 's/([^)]*)//g; s/,/ /g'
+}
+
+mirror_images_redundant()
+{
+ vg=$1
+ lv=$vg/$2
+
+ lvs -a $vg -o+devices
+ for i in `lvdevices $lv`; do
+ echo "# $i:"
+ lvdevices $vg/$i | sort | uniq
+ done > check.tmp.all
+
+ (grep -v ^# check.tmp.all || true) | sort | uniq -d > check.tmp
+
+ test "`cat check.tmp | wc -l`" -eq 0 || {
+ echo "mirror images of $lv expected redundant, but are not:"
+ cat check.tmp.all
+ exit 1
+ }
+}
+
+mirror_images_on() {
+ vg=$1
+ lv=$2
+
+ shift 2
+
+ for i in `lvdevices $lv`; do
+ lv_on $vg $lv $1
+ shift
+ done
+}
+
+lv_on()
+{
+ lv="$1/$2"
+ lvdevices $lv | grep -F "$3" || {
+ echo "LV $lv expected on $3 but is not:" >&2
+ lvdevices $lv >&2
+ exit 1
+ }
+ test `lvdevices $lv | grep -vF "$3" | wc -l` -eq 0 || {
+ echo "LV $lv contains unexpected devices:" >&2
+ lvdevices $lv >&2
+ exit 1
+ }
+}
+
+mirror_log_on()
+{
+ vg="$1"
+ lv="$2"
+ where="$3"
+ if test "$where" = "core"; then
+ lvl -omirror_log "$vg/$lv" | not grep mlog
+ else
+ lv_on $vg "${lv}_mlog" "$where"
+ fi
+}
+
+lv_is_contiguous()
+{
+ test `lvl --segments $1 | wc -l` -eq 1 || {
+ echo "LV $1 expected to be contiguous, but is not:"
+ lvl --segments $1
+ exit 1
+ }
+}
+
+lv_is_clung()
+{
+ test `lvdevices $1 | sort | uniq | wc -l` -eq 1 || {
+ echo "LV $1 expected to be clung, but is not:"
+ lvdevices $! | sort | uniq
+ exit 1
+ }
+}
+
+mirror_images_contiguous()
+{
+ for i in `lvdevices $1/$2`; do
+ lv_is_contiguous $1/$i
+ done
+}
+
+mirror_images_clung()
+{
+ for i in `lvdevices $1/$2`; do
+ lv_is_clung $1/$i
+ done
+}
+
+mirror() {
+ mirror_nonredundant "$@"
+ mirror_images_redundant "$1" "$2"
+}
+
+mirror_nonredundant() {
+ lv="$1/$2"
+ lvs -oattr "$lv" | grep -q "^ *m.....$" || {
+ echo "$lv expected a mirror, but is not:"
+ lvs -a $lv
+ exit 1
+ }
+ if test -n "$3"; then mirror_log_on "$1" "$2" "$3"; fi
+}
+
+mirror_legs() {
+ lv="$1/$2"
+ expect="$3"
+ lvdevices "$lv"
+ real=`lvdevices "$lv" | wc -w`
+ test "$expect" = "$real"
+}
+
+mirror_no_temporaries()
+{
+ vg=$1
+ lv=$2
+ lvl -oname $vg | grep $lv | not grep "tmp" || {
+ echo "$lv has temporary mirror images unexpectedly:"
+ lvl $vg | grep $lv
+ exit 1
+ }
+}
+
+linear() {
+ lv="$1/$2"
+ lvl -ostripes "$lv" | grep -q "1" || {
+ echo "$lv expected linear, but is not:"
+ lvl "$lv" -o+devices
+ exit 1
+ }
+}
+
+active() {
+ lv="$1/$2"
+ lvl -oattr "$lv" 2> /dev/null | grep -q "^ *....a.$" || {
+ echo "$lv expected active, but lvs says it's not:"
+ lvl "$lv" -o+devices 2>/dev/null
+ exit 1
+ }
+ dmsetup table | egrep -q "$1-$2: *[^ ]+" || {
+ echo "$lv expected active, lvs thinks it is but there are no mappings!"
+ dmsetup table | grep $1-$2:
+ exit 1
+ }
+}
+
+inactive() {
+ lv="$1/$2"
+ lvl -oattr "$lv" 2> /dev/null | grep -q '^ *....[-isd].$' || {
+ echo "$lv expected inactive, but lvs says it's not:"
+ lvl "$lv" -o+devices 2>/dev/null
+ exit 1
+ }
+ dmsetup table | not egrep -q "$1-$2: *[^ ]+" || {
+ echo "$lv expected inactive, lvs thinks it is but there are mappings!"
+ dmsetup table | grep $1-$2:
+ exit 1
+ }
+}
+
+"$@"
--- /dev/null
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+pid_t pid;
+int fds[2];
+
+#define MAX 1024
+
+struct stats {
+ int nfailed;
+ int nskipped;
+ int npassed;
+ int nwarned;
+ int status[MAX];
+};
+
+struct stats s;
+
+char *readbuf = NULL;
+int readbuf_sz = 0, readbuf_used = 0;
+
+int die = 0;
+int verbose = 0;
+
+#define PASSED 0
+#define SKIPPED 1
+#define FAILED 2
+#define WARNED 3
+
+void handler( int s ) {
+ signal( s, SIG_DFL );
+ kill( pid, s );
+ die = s;
+}
+
+void dump() {
+ fwrite(readbuf, 1, readbuf_used, stdout);
+}
+
+void clear() {
+ readbuf_used = 0;
+}
+
+void drain() {
+ int sz;
+ char buf[2048];
+ while (1) {
+ sz = read(fds[1], buf, 2048);
+ if (verbose)
+ write(1, buf, sz);
+ if (sz <= 0)
+ return;
+ if (readbuf_used + sz >= readbuf_sz) {
+ readbuf_sz = readbuf_sz ? 2 * readbuf_sz : 4096;
+ readbuf = realloc(readbuf, readbuf_sz);
+ }
+ if (!readbuf)
+ exit(205);
+ memcpy(readbuf + readbuf_used, buf, sz);
+ readbuf_used += sz;
+ readbuf[readbuf_used] = 0;
+ }
+}
+
+void passed(int i, char *f) {
+ if (strstr(readbuf, "TEST WARNING")) {
+ ++s.nwarned;
+ s.status[i] = WARNED;
+ printf("warnings\n");
+ } else {
+ ++ s.npassed;
+ s.status[i] = PASSED;
+ printf("passed.\n");
+ }
+}
+
+void skipped(int i, char *f) {
+ ++ s.nskipped;
+ s.status[i] = SKIPPED;
+ printf("skipped.\n");
+}
+
+void failed(int i, char *f, int st) {
+ ++ s.nfailed;
+ s.status[i] = FAILED;
+ if(die == 2) {
+ printf("interrupted.\n");
+ return;
+ }
+ printf("FAILED.\n");
+ printf("-- FAILED %s ------------------------------------\n", f);
+ dump();
+ printf("-- FAILED %s (end) ------------------------------\n", f);
+}
+
+void run(int i, char *f) {
+ pid = fork();
+ if (pid < 0) {
+ perror("Fork failed.");
+ exit(201);
+ } else if (pid == 0) {
+ close(0);
+ dup2(fds[0], 1);
+ dup2(fds[0], 2);
+ execlp("bash", "bash", f, NULL);
+ perror("execlp");
+ fflush(stderr);
+ _exit(202);
+ } else {
+ char buf[128];
+ snprintf(buf, 128, "%s ...", f);
+ buf[127] = 0;
+ printf("Running %-40s ", buf);
+ fflush(stdout);
+ int st, w;
+ while ((w = waitpid(pid, &st, WNOHANG)) == 0) {
+ drain();
+ usleep(20000);
+ }
+ if (w != pid) {
+ perror("waitpid");
+ exit(206);
+ }
+ drain();
+ if (WIFEXITED(st)) {
+ if (WEXITSTATUS(st) == 0) {
+ passed(i, f);
+ } else if (WEXITSTATUS(st) == 200) {
+ skipped(i, f);
+ } else {
+ failed(i, f, st);
+ }
+ } else {
+ failed(i, f, st);
+ }
+ clear();
+ }
+}
+
+int main(int argc, char **argv) {
+ int i;
+
+ if (argc >= MAX) {
+ fprintf(stderr, "Sorry, my head exploded. Please increase MAX.\n");
+ exit(1);
+ }
+
+ s.nwarned = s.nfailed = s.npassed = s.nskipped = 0;
+
+ char *config = getenv("LVM_TEST_CONFIG"),
+ *config_debug,
+ *be_verbose = getenv("VERBOSE");
+ if (be_verbose && atoi(be_verbose))
+ verbose = 1; // XXX
+ config = config ? config : "";
+ asprintf(&config_debug, "%s\n%s\n", config, "log { verbose=4 }");
+
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, fds)) {
+ perror("socketpair");
+ return 201;
+ }
+
+ if ( fcntl( fds[1], F_SETFL, O_NONBLOCK ) == -1 ) {
+ perror("fcntl on socket");
+ return 202;
+ }
+
+ /* set up signal handlers */
+ for (i = 0; i <= 32; ++i) {
+ if (i == SIGCHLD || i == SIGWINCH || i == SIGURG)
+ continue;
+ signal(i, handler);
+ }
+
+ /* run the tests */
+ for (i = 1; i < argc; ++ i) {
+ run(i, argv[i]);
+ if (die)
+ break;
+ }
+
+ printf("\n## %d tests: %d OK, %d warnings, %d failures; %d skipped\n",
+ s.nwarned + s.npassed + s.nfailed + s.nskipped,
+ s.npassed, s.nwarned, s.nfailed, s.nskipped);
+
+ /* print out a summary */
+ if (s.nfailed || s.nskipped) {
+ for (i = 1; i < argc; ++ i) {
+ switch (s.status[i]) {
+ case FAILED:
+ printf("FAILED: %s\n", argv[i]);
+ break;
+ case SKIPPED:
+ printf("skipped: %s\n", argv[i]);
+ break;
+ }
+ }
+ printf("\n");
+ return s.nfailed > 0 || die;
+ }
+ return die;
+}
--- /dev/null
+#!/bin/sh
+
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+tests="$@"
+test -z "$tests" && tests=`echo t-*.sh`
+
+for t in $tests; do
+ printf "Running %-40s" "$t ..."
+ out=`bash ./$t 2>&1`
+ ret=$?
+ if test $ret = 0; then
+ echo " passed."
+ elif test $ret = 200; then
+ skipped="$skipped $t"
+ echo " skipped."
+ else
+ echo " FAILED!"
+ len=`echo $t | wc -c`
+ # fancy formatting...
+ printf -- "--- Output: $t -"
+ for i in `seq $(($len + 14)) 78`; do echo -n "-"; done; echo
+ printf "%s\n" "$out"
+ printf -- "--- End: $t ----"
+ for i in `seq $(($len + 14)) 78`; do echo -n "-"; done; echo
+ failed="$failed $t"
+ fi
+done
+
+if test -n "$failed"; then
+ echo "Tests skipped:"
+ for t in $skipped; do
+ printf "\t%s\n" $t
+ done
+ echo "TESTS FAILED:"
+ for t in $failed; do
+ printf "\t%s\n" $t
+ done
+ exit 1
+else
+ echo "All tests passed."
+fi
--- /dev/null
+# Put lvm-related utilities here.
+# This file is sourced from test-lib.sh.
+
+# Copyright (C) 2007, 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+export LVM_SUPPRESS_FD_WARNINGS=1
+
+ME=$(basename "$0")
+warn() { echo >&2 "$ME: $@"; }
+
+trim()
+{
+ trimmed=${1%% }
+ trimmed=${trimmed## }
+
+ echo "$trimmed"
+}
+
+compare_two_fields_()
+{
+ local cmd1=$1;
+ local obj1=$2;
+ local field1=$3;
+ local cmd2=$4;
+ local obj2=$5;
+ local field2=$6;
+ local val1;
+ local val2;
+
+ val1=$($cmd1 --noheadings -o $field1 $obj1)
+ val2=$($cmd2 --noheadings -o $field2 $obj2)
+if test "$verbose" = "t"
+then
+ echo "compare_two_fields_ $obj1($field1): $val1 $obj2($field2): $val2"
+fi
+ test "$val1" = "$val2"
+}
+
+compare_vg_field_()
+{
+ local vg1=$1;
+ local vg2=$2;
+ local field=$3;
+ local val1;
+ local val2;
+
+ val1=$(vgs --noheadings -o $field $vg1)
+ val2=$(vgs --noheadings -o $field $vg2)
+if test "$verbose" = "t"
+then
+ echo "compare_vg_field_ VG1: $val1 VG2: $val2"
+fi
+ test "$val1" = "$val2"
+}
+
+
+get_pv_field() {
+ local pv=$1
+ local field=$2
+ local value
+ pvs --noheading -o $field $pv | sed 's/^ *//'
+}
+
+get_vg_field() {
+ local vg=$1
+ local field=$2
+ local value
+ vgs --noheading -o $field $vg | sed 's/^ *//'
+}
+
+get_lv_field() {
+ local lv=$1
+ local field=$2
+ local value
+ lvs --noheading -o $field $lv | sed 's/^ *//'
+}
+
+check_vg_field_()
+{
+ local vg=$1;
+ local field=$2;
+ local expected=$3;
+ local actual;
+
+ actual=$(trim $(vgs --noheadings -o $field $vg))
+if test "$verbose" = "t"
+then
+ echo "check_vg_field_ VG=$vg, field=$field, actual=$actual, expected=$expected"
+fi
+ test "$actual" = "$expected"
+}
+
+check_pv_field_()
+{
+ local pv=$1;
+ local field=$2;
+ local expected=$3;
+ local pvs_args=$4; # optional
+ local actual;
+
+ actual=$(trim $(pvs --noheadings $pvs_args -o $field $pv))
+if test "$verbose" = "t"
+then
+ echo "check_pv_field_ PV=$pv, field=$field, actual=$actual, expected=$expected"
+fi
+ test "$actual" = "$expected"
+}
+
+check_lv_field_()
+{
+ local lv=$1;
+ local field=$2;
+ local expected=$3;
+ local actual;
+
+ actual=$(trim $(lvs --noheadings -o $field $lv))
+if test "$verbose" = "t"
+then
+ echo "check_lv_field_ LV=$lv, field=$field, actual=$actual, expected=$expected"
+fi
+ test "$actual" = "$expected"
+}
+
+vg_validate_pvlv_counts_()
+{
+ local local_vg=$1
+ local num_pvs=$2
+ local num_lvs=$3
+ local num_snaps=$4
+
+ lvs -a -o+devices $local_vg
+
+ check_vg_field_ $local_vg pv_count $num_pvs && \
+ check_vg_field_ $local_vg lv_count $num_lvs && \
+ check_vg_field_ $local_vg snap_count $num_snaps
+}
+
+dmsetup_has_dm_devdir_support_()
+{
+ # Detect support for the envvar. If it's supported, the
+ # following command will fail with the expected diagnostic.
+ out=$(DM_DEV_DIR=j dmsetup version 2>&1)
+ test "$?:$out" = "1:Invalid DM_DEV_DIR envvar value." -o \
+ "$?:$out" = "1:Invalid DM_DEV_DIR environment variable value."
+}
--- /dev/null
+#!/bin/sh
+# Create a temporary directory, sort of like mktemp -d does.
+
+# Copyright (C) 2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Written by Jim Meyering.
+
+# Usage: mkdtemp /tmp phoey.XXXXXXXXXX
+
+# First, try to use the mktemp program.
+# Failing that, we'll roll our own mktemp-like function:
+# - try to get random bytes from /dev/urandom
+# - failing that, generate output from a combination of quickly-varying
+# sources and gzip. Ignore non-varying gzip header, and extract
+# "random" bits from there.
+# - given those bits, map to file-name bytes using tr, and try to create
+# the desired directory.
+# - make only $MAX_TRIES attempts
+
+ME=$(basename "$0")
+die() { echo >&2 "$ME: $@"; exit 1; }
+
+MAX_TRIES=4
+
+rand_bytes()
+{
+ n=$1
+
+ chars=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
+
+ dev_rand=/dev/urandom
+ if test -r "$dev_rand"; then
+ # Note: 256-length($chars) == 194; 3 copies of $chars is 186 + 8 = 194.
+ head -c$n "$dev_rand" | tr -c $chars 01234567$chars$chars$chars
+ return
+ fi
+
+ cmds='date; date +%N; free; who -a; w; ps auxww; ps ef; netstat -n'
+ data=$( (eval "$cmds") 2>&1 | gzip )
+
+ n_plus_50=$(expr $n + 50)
+
+ # Ensure that $data has length at least 50+$n
+ while :; do
+ len=$(echo "$data"|wc -c)
+ test $n_plus_50 -le $len && break;
+ data=$( (echo "$data"; eval "$cmds") 2>&1 | gzip )
+ done
+
+ echo "$data" \
+ | dd bs=1 skip=50 count=$n 2>/dev/null \
+ | tr -c $chars 01234567$chars$chars$chars
+}
+
+mkdtemp()
+{
+ case $# in
+ 2);;
+ *) die "Usage: $ME DIR TEMPLATE";;
+ esac
+
+ destdir=$1
+ template=$2
+
+ case $template in
+ *XXXX) ;;
+ *) die "invalid template: $template (must have a suffix of at least 4 X's)";;
+ esac
+
+ fail=0
+
+ # First, try to use mktemp.
+ d=$(env -u TMPDIR mktemp -d -t -p "$destdir" "$template" 2>/dev/null) \
+ || fail=1
+
+ # The resulting name must be in the specified directory.
+ case $d in "$destdir"*);; *) fail=1;; esac
+
+ # It must have created the directory.
+ test -d "$d" || fail=1
+
+ # It must have 0700 permissions.
+ perms=$(ls -dgo "$d" 2>/dev/null) || fail=1
+ case $perms in drwx------*) ;; *) fail=1;; esac
+
+ test $fail = 0 && {
+ echo "$d"
+ return
+ }
+
+ # If we reach this point, we'll have to create a directory manually.
+
+ # Get a copy of the template without its suffix of X's.
+ base_template=$(echo "$template"|sed 's/XX*$//')
+
+ # Calculate how many X's we've just removed.
+ nx=$(expr length "$template" - length "$base_template")
+
+ err=
+ i=1
+ while :; do
+ X=$(rand_bytes $nx)
+ candidate_dir="$destdir/$base_template$X"
+ err=$(mkdir -m 0700 "$candidate_dir" 2>&1) \
+ && { echo "$candidate_dir"; return; }
+ test $MAX_TRIES -le $i && break;
+ i=$(expr $i + 1)
+ done
+ die "$err"
+}
+
+mkdtemp "$@"
--- /dev/null
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+int finished(const char *cmd, int status) {
+ if (!strcmp(cmd, "not"))
+ return !status;
+ if (!strcmp(cmd, "should")) {
+ if (status)
+ fprintf(stderr, "TEST WARNING: Ignoring command failure.\n");
+ return 0;
+ }
+ return 6;
+}
+
+int main(int args, char **argv) {
+ pid_t pid;
+ int status;
+ int FAILURE = 6;
+
+ if (args < 2) {
+ fprintf(stderr, "Need args\n");
+ return FAILURE;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ fprintf(stderr, "Could not fork\n");
+ return FAILURE;
+ } else if (pid == 0) { /* child */
+ execvp(argv[1], &argv[1]);
+ /* should not be accessible */
+ return FAILURE;
+ } else { /* parent */
+ waitpid(pid, &status, 0);
+ if (!WIFEXITED(status)) {
+ if (WIFSIGNALED(status))
+ fprintf(stderr,
+ "Process %d died of signal %d.\n",
+ pid, WTERMSIG(status));
+ /* did not exit correctly */
+ return FAILURE;
+ }
+
+ return finished(argv[0], WEXITSTATUS(status));
+ }
+ /* not accessible */
+ return FAILURE;
+}
--- /dev/null
+# Copyright (C) 2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+lvm version
+
+v=$abs_top_srcdir/lib/misc/lvm-version.h
+sed -n "/#define LVM_VERSION ./s///p" "$v" | sed "s/ .*//" > expected
+
+lvm pvmove --version|sed -n "1s/.*: *\([0-9][^ ]*\) .*/\1/p" > actual
+
+# ensure they are the same
+diff -u actual expected
+
+mknod $DM_DEV_DIR/null c 1 3 || \
+ error "Can't create nodes on filesystem"
+echo >$DM_DEV_DIR/null || \
+ error "Filesystem for tests does not allow using device nodes (check nodev)"
+
+# ensure we can create devices (uses dmsetup, etc)
+aux prepare_devs 5
+
--- /dev/null
+#!/bin/bash
+
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Test activation behaviour with devices missing.
+# - snapshots and their origins are only activated together; if one fails, both
+# fail
+# - partial mirrors are not activated (but maybe they should? maybe we should
+# instead lvconvert --repair them?)
+# - linear LVs with bits missing are not activated
+
+. ./test-utils.sh
+
+prepare_vg 4
+
+lvcreate -l1 -n linear1 $vg $dev1
+lvcreate -l1 -n linear2 $vg $dev2
+lvcreate -l2 -n linear12 $vg $dev1:4 $dev2:4
+
+lvcreate -l1 -n origin1 $vg $dev1
+lvcreate -s $vg/origin1 -l1 -n s_napshot2 $dev2
+
+lvcreate -l1 -m1 -n mirror12 --mirrorlog core $vg $dev1 $dev2
+lvcreate -l1 -m1 -n mirror123 $vg $dev1 $dev2 $dev3
+
+vgchange -a n $vg
+disable_dev $dev1
+not vgchange -a y $vg
+not vgck $vg
+
+check inactive $vg linear1
+check active $vg linear2
+check inactive $vg origin1
+check inactive $vg s_napshot2
+check inactive $vg linear12
+check inactive $vg mirror12
+check inactive $vg mirror123
+
+vgchange -a n $vg
+enable_dev $dev1
+disable_dev $dev2
+not vgchange -a y $vg
+not vgck $vg
+
+check active $vg linear1
+check inactive $vg linear2
+check inactive $vg linear12
+check inactive $vg origin1
+check inactive $vg s_napshot2
+check inactive $vg mirror12
+check inactive $vg mirror123
+
+vgchange -a n $vg
+enable_dev $dev2
+disable_dev $dev3
+not vgchange -a y $vg
+not vgck $vg
+
+check active $vg origin1
+check active $vg s_napshot2
+check active $vg linear1
+check active $vg linear2
+check active $vg linear12
+check inactive $vg mirror123
+check active $vg mirror12
+
+vgchange -a n $vg
+enable_dev $dev3
+disable_dev $dev4
+vgchange -a y $vg
+not vgck $vg
+
+check active $vg origin1
+check active $vg s_napshot2
+check active $vg linear1
+check active $vg linear2
+check active $vg linear12
+check active $vg mirror12
+check active $vg mirror123
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_vg 3
+
+lvcreate -m 1 -l 1 -n mirror $vg
+lvchange -a n $vg/mirror
+disable_dev $dev1
+
+not vgreduce --removemissing $vg
+not lvchange -v -a y $vg/mirror
+lvchange -v --partial -a y $vg/mirror
+not lvchange -v --refresh $vg/mirror
+lvchange -v --refresh --partial $vg/mirror
+
+# also check that vgchange works
+vgchange -a n --partial $vg
+vgchange -a y --partial $vg
+
+# check vgremove
+vgremove -f $vg
--- /dev/null
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#
+# tests basic functionality of read-ahead and ra regressions
+#
+
+. ./test-utils.sh
+
+TEST_UUID="aaaaaa-aaaa-aaaa-aaaa-aaaa-aaaa-aaaaaa"
+
+get_lvs_()
+{
+ case $(lvs --units s --nosuffix --noheadings -o $1_read_ahead "$vg"/"$lv") in
+ *$2) true ;;
+ *) false ;;
+ esac
+}
+
+aux prepare_devs 5
+
+pvcreate $dev1
+pvcreate --metadatacopies 0 $dev2
+pvcreate --metadatacopies 0 $dev3
+pvcreate $dev4
+pvcreate --norestorefile -u $TEST_UUID --metadatacopies 0 $dev5
+vgcreate -c n $vg $devs
+lvcreate -n $lv -l 5 -i5 -I256 $vg
+
+# test *scan and *display tools
+pvscan
+vgscan
+lvscan
+lvmdiskscan
+vgdisplay --units k
+lvdisplay --units g
+for i in h b s k m g t p e H B S K M G T P E ; do
+ pvdisplay --units "$i" "$dev1"
+done
+
+# test vgexport vgimport tools
+vgchange -an $vg
+vgexport $vg
+vgimport $vg
+vgchange -ay $vg
+
+# "-persistent y --major 254 --minor 20"
+# "-persistent n"
+# test various lvm utils
+for i in dumpconfig formats segtypes; do
+ lvm "$i"
+done
+
+for i in pr "p rw" an ay "-monitor y" "-monitor n" \
+ -resync -refresh "-addtag MYTAG" "-deltag MYETAG"; do
+ lvchange -$i "$vg"/"$lv"
+done
+
+pvck "$dev1"
+vgck "$vg"
+lvrename "$vg" "$lv" "$lv-rename"
+vgcfgbackup -f "$(pwd)/backup.$$" "$vg"
+vgchange -an "$vg"
+vgcfgrestore -f "$(pwd)/backup.$$" "$vg"
+pvremove -y -ff $dev5
+not vgcfgrestore -f "$(pwd)/backup.$$" "$vg"
+pvcreate -u $TEST_UUID --restorefile "$(pwd)/backup.$$" $dev5
+vgremove -f "$vg"
+pvresize --setphysicalvolumesize 10M "$dev1"
+
+# test various errors and obsoleted tools
+not lvmchange
+not lvrename "$vg"
+not lvrename "$vg-xxx"
+not lvrename "$vg" "$vg"/"$lv-rename" "$vg"/"$lv"
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+prepare_vg 5
+prepare_dmeventd
+
+which mkfs.ext2 || exit 200
+
+lvcreate -m 3 --ig -L 1 -n 4way $vg
+lvchange --monitor y $vg/4way
+lvcreate -m 2 --ig -L 1 -n 3way $vg
+lvchange --monitor y $vg/3way
+
+dmeventd -R -f &
+LOCAL_DMEVENTD="$!"
+
+sleep 1 # wait a bit, so we talk to the new dmeventd later
+
+lvchange --monitor y --verbose $vg/3way 2>&1 | tee lvchange.out
+grep 'already monitored' lvchange.out
+lvchange --monitor y --verbose $vg/4way 2>&1 | tee lvchange.out
+grep 'already monitored' lvchange.out
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2008-2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+test_description='Exercise fsadm filesystem resize'
+exit 200
+
+. ./test-utils.sh
+
+aux prepare_vg 1 100
+
+# set to "skip" to avoid testing given fs and test warning result
+# i.e. check_reiserfs=skip
+check_ext3=
+check_xfs=
+check_reiserfs=
+
+which mkfs.ext3 || check_ext3=${check_ext3:=mkfs.ext3}
+which fsck.ext3 || check_ext3=${check_ext3:=fsck.ext3}
+which mkfs.xfs || check_xfs=${check_xfs:=mkfs.xfs}
+which xfs_check || check_xfs=${check_xfs:=xfs_check}
+which mkfs.reiserfs || check_reiserfs=${check_reiserfs:=mkfs.reiserfs}
+which reiserfsck || check_reiserfs=${check_reiserfs:=reiserfsck}
+
+vg_lv="$vg/$lv1"
+dev_vg_lv="$DM_DEV_DIR/$vg_lv"
+mount_dir="$TESTDIR/mnt"
+
+test ! -d $mount_dir && mkdir $mount_dir
+
+cleanup_mounted_and_teardown()
+{
+ umount $mount_dir || true
+ teardown
+}
+
+fscheck_ext3()
+{
+ fsck.ext3 -p -F -f $dev_vg_lv
+}
+
+fscheck_xfs()
+{
+ xfs_check $dev_vg_lv
+}
+
+fscheck_reiserfs()
+{
+ reiserfsck --check -p -f $dev_vg_lv </dev/null
+}
+
+check_missing()
+{
+ eval local t=$\check_$1
+ test -z "$t" && return 0
+ test "$t" = skip && return 1
+ # trick for warning test
+ echo "TEST ""WARNING: fsadm skips $1 tests, $t tool is missing"
+ return 1
+}
+
+# Test for block sizes != 1024 (rhbz #480022)
+lvcreate -n $lv1 -L20M $vg
+trap 'aux cleanup_mounted_and_teardown' EXIT
+
+if check_missing ext3; then
+ mkfs.ext3 -b4096 -j $dev_vg_lv
+
+ fsadm --lvresize resize $vg_lv 30M
+ # Fails - not enough space for 4M fs
+ not fsadm --lvresize resize $dev_vg_lv 4M
+ lvresize -L+10M -r $vg_lv
+ lvreduce -L10M -r $vg_lv
+
+ fscheck_ext3
+ mount $dev_vg_lv $mount_dir
+ not fsadm --lvresize resize $vg_lv 9M
+ lvresize -L+20M -r -n $vg_lv
+ umount $mount_dir
+ fscheck_ext3
+
+ lvresize -f -L20M $vg_lv
+fi
+
+if check_missing xfs; then
+ mkfs.xfs -l internal,size=1000b -f $dev_vg_lv
+
+ fsadm --lvresize resize $vg_lv 30M
+ # Fails - not enough space for 4M fs
+ lvresize -L+10M -r $vg_lv
+ not lvreduce -L10M -r $vg_lv
+
+ fscheck_xfs
+ mount $dev_vg_lv $mount_dir
+ lvresize -L+10M -r -n $vg_lv
+ umount $mount_dir
+ fscheck_xfs
+
+ lvresize -f -L20M $vg_lv
+fi
+
+if check_missing reiserfs; then
+ mkfs.reiserfs -s 513 -f $dev_vg_lv
+
+ fsadm --lvresize resize $vg_lv 30M
+ lvresize -L+10M -r $vg_lv
+ fsadm --lvresize -y resize $vg_lv 10M
+
+ fscheck_reiserfs
+ mount $dev_vg_lv $mount_dir
+
+ not fsadm -y --lvresize resize $vg_lv 20M
+ umount $mount_dir
+
+ lvresize -f -L20M $vg_lv
+fi
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_vg 3
+
+lvcreate -m 1 -l 1 -n mirror $vg
+lvcreate -l 1 -n resized $vg
+lvchange -a n $vg/mirror
+
+backup_dev $devs
+
+init() {
+ restore_dev $devs
+ lvs -o lv_name,lv_size --units k $vg | tee lvs.out
+ grep resized lvs.out | not grep 8192
+ lvresize -L 8192K $vg/resized
+ restore_dev $dev1
+}
+
+check() {
+ lvs -o lv_name,lv_size --units k $vg | tee lvs.out
+ grep resized lvs.out | grep 8192
+}
+
+# vgscan fixes up metadata
+init
+vgscan 2>&1 | tee cmd.out
+grep "Inconsistent metadata found for VG $vg" cmd.out
+vgscan 2>&1 | tee cmd.out
+not grep "Inconsistent metadata found for VG $vg" cmd.out
+check
+
+# vgdisplay fixes
+init
+vgdisplay 2>&1 | tee cmd.out
+grep "Inconsistent metadata found for VG $vg" cmd.out
+vgdisplay 2>&1 | tee cmd.out
+not grep "Inconsistent metadata found for VG $vg" cmd.out
+check
+
+# lvs fixes up
+init
+lvs 2>&1 | tee cmd.out
+grep "Inconsistent metadata found for VG $vg" cmd.out
+vgdisplay 2>&1 | tee cmd.out
+not grep "Inconsistent metadata found for VG $vg" cmd.out
+check
+
+# vgs fixes up as well
+init
+vgs 2>&1 | tee cmd.out
+grep "Inconsistent metadata found for VG $vg" cmd.out
+vgs 2>&1 | tee cmd.out
+not grep "Inconsistent metadata found for VG $vg" cmd.out
+check
+
+echo Check auto-repair of failed vgextend - metadata written to original pv but not new pv
+vgremove -f $vg
+pvremove -ff $devs
+pvcreate $devs
+backup_dev $dev2
+vgcreate $vg $dev1
+vgextend $vg $dev2
+restore_dev $dev2
+should compare_two_fields_ vgs $vg vg_mda_count pvs $dev2 vg_mda_count
--- /dev/null
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#
+# tests functionality of lvs, pvs, vgs, *display tools
+#
+
+. ./test-utils.sh
+
+get_lvs_()
+{
+ case $(lvs --units s --nosuffix --noheadings -o $1_read_ahead "$vg"/"$lv") in
+ *$2) true ;;
+ *) false ;;
+ esac
+}
+
+aux prepare_devs 5
+
+pvcreate $dev1
+pvcreate --metadatacopies 0 $dev2
+pvcreate --metadatacopies 0 $dev3
+pvcreate $dev4
+pvcreate --metadatacopies 0 $dev5
+
+#COMM bz195276 -- pvs doesn't show PVs until a VG is created
+pvs --noheadings|tee out
+test $(wc -l <out) -eq 5
+
+#COMM pvs with segment attributes works even for orphans
+pvs --noheadings -o seg_all,pv_all,lv_all,vg_all | tee out
+test $(wc -l <out) -eq 5
+
+vgcreate -c n $vg $devs
+
+#COMM pvs and vgs report mda_count, mda_free (bz202886, bz247444)
+pvs -o +pv_mda_count,pv_mda_free $devs
+for I in $dev2 $dev3 $dev5; do
+ aux check_pv_field_ $I pv_mda_count 0
+ aux check_pv_field_ $I pv_mda_free 0
+done
+vgs -o +vg_mda_count,vg_mda_free $vg
+aux check_vg_field_ $vg vg_mda_count 2
+
+#COMM pvs doesn't display --metadatacopies 0 PVs as orphans (bz409061)
+pvdisplay $dev2|grep "VG Name.*$vg"
+test $(pvs -o vg_name --noheadings $dev2) = $vg
+
+#COMM lvs displays snapshots (bz171215)
+lvcreate -l4 -n $lv1 $vg
+lvcreate -l4 -s -n $lv2 $vg/$lv1
+lvs $vg --noheadings|tee out
+test $(wc -l <out) -eq 2
+lvs -a --noheadings|tee out
+# should lvs -a display cow && real devices? (it doesn't)
+test $(wc -l <out) -eq 2
+dmsetup ls|grep $PREFIX|grep -v "LVMTEST.*pv."
+lvremove -f $vg/$lv2
+
+#COMM lvs -a displays mirror legs and log
+lvcreate -l4 -m2 -n$lv3 $vg
+lvs $vg --noheadings|tee out
+test $(wc -l <out) -eq 2
+lvs -a --noheadings|tee out
+test $(wc -l <out) -eq 6
+dmsetup ls|grep $PREFIX|grep -v "LVMTEST.*pv."
+
+#COMM vgs with options from pvs still treats arguments as VGs (bz193543)
+vgs -o pv_name,vg_name $vg
+# would complain if not
+
+#COMM pvdisplay --maps feature (bz149814)
+pvdisplay $devs >out
+pvdisplay --maps $devs >out2
+not diff out out2
+
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+test_description='test some blocking / non-blocking multi-vg operations'
+
+. ./test-utils.sh
+
+aux prepare_devs 3
+test -n "$LOCAL_CLVMD" && exit 200
+pvcreate $dev1 $dev2
+vgcreate $vg $dev1 $dev2
+
+# if wait_for_locks set, vgremove should wait for orphan lock
+# flock process should have exited by the time first vgremove completes
+flock -w 5 $TESTDIR/var/lock/lvm/P_orphans -c "sleep 10" &
+flock_pid=`jobs -p`
+vgremove --config 'global { wait_for_locks = 1 }' $vg
+not vgremove --config 'global { wait_for_locks = 1 }' $vg
+not ps $flock_pid # finished
+
+# if wait_for_locks not set, vgremove should fail on non-blocking lock
+# we must wait for flock process at the end - vgremove won't wait
+vgcreate $vg $dev1 $dev2
+flock -w 5 $TESTDIR/var/lock/lvm/P_orphans -c "sleep 10" &
+flock_pid=`jobs -p`
+not vgremove --config 'global { wait_for_locks = 0 }' $vg
+ps $flock_pid # still running
+kill $flock_pid
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+aux prepare_vg 3
+
+# force resync 2-way active mirror
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+check mirror $vg $lv1 $dev3
+echo y | lvchange --resync $vg/$lv1
+check mirror $vg $lv1 $dev3
+lvremove -ff $vg
+
+# force resync 2-way inactive mirror
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvchange -an $vg/$lv1
+check mirror $vg $lv1 $dev3
+lvchange --resync $vg/$lv1
+check mirror $vg $lv1 $dev3
+lvremove -ff $vg
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./t-lvconvert-mirror-basic.sh
+test_many 0
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./t-lvconvert-mirror-basic.sh
+test_many 1
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./t-lvconvert-mirror-basic.sh
+test_many 2
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./t-lvconvert-mirror-basic.sh
+test_many 3
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+log_name_to_count()
+{
+ if [ "$1" = "mirrored" ]; then
+ echo 2
+ elif [ "$1" = "disk" ]; then
+ echo 1
+ else
+ echo 0
+ fi
+}
+
+# FIXME: For test_[up|down]convert, I'd still like to be able
+# to specifiy devices - especially if I can do partial PV
+# specification for down-converts. It may even be wise to
+# do one round through these tests without specifying the PVs
+# to use and one round where we do.
+
+# test_lvconvert
+# start_mirror_count: The '-m' argument to create with
+# start_log_type: core|disk|mirrored
+# final_mirror_count: The '-m' argument to convert to
+# final_log_type: core|disk|mirrored
+# active: Whether the LV should be active when the convert happens
+#
+# Exmaple: Convert 3-way disk-log mirror to
+# 2-way disk-log mirror while not active
+# -> test_lvconvert 2 disk 3 disk 0
+
+test_lvconvert()
+{
+ local start_count=$1
+ local start_count_p1=$(($start_count + 1))
+ local start_log_type=$2
+ local finish_count=$3
+ local finish_count_p1=$(($finish_count + 1))
+ local finish_log_type=$4
+ local dev_array=($dev1 $dev2 $dev3 $dev4 $dev5)
+ local start_log_count
+ local finish_log_count
+ local max_log_count
+ local alloc=""
+ local active=true
+ local i
+
+ if [ "$start_log_type" = "disk" ] &&
+ [ "$finish_log_type" = "mirrored" ]; then
+ echo "FIXME: disk -> mirrored log conversion not yet supported by LVM"
+ return 0
+ fi
+
+ test "$5" = "active" && active=false
+ #test $finish_count -gt $start_count && up=true
+
+ # Do we have enough devices for the mirror images?
+ if [ $start_count_p1 -gt ${#dev_array[@]} ]; then
+ echo "Action requires too many devices"
+ return 1
+ fi
+
+ # Do we have enough devices for the mirror images?
+ if [ $finish_count_p1 -gt ${#dev_array[@]} ]; then
+ echo "Action requires too many devices"
+ return 1
+ fi
+
+ start_log_count=`log_name_to_count $start_log_type`
+ finish_log_count=`log_name_to_count $finish_log_type`
+ if [ $finish_log_count -gt $start_log_count ]; then
+ max_log_count=$finish_log_count
+ else
+ max_log_count=$start_log_count
+ fi
+
+ prepare_vg 5
+
+ if [ $start_count -gt 0 ]; then
+ # Are there extra devices for the log or do we overlap
+ if [ $(($start_count_p1 + $start_log_count)) -gt ${#dev_array[@]} ]; then
+ alloc="--alloc anywhere"
+ fi
+
+ lvcreate -l2 -m $start_count --mirrorlog $start_log_type \
+ -n $lv1 $vg $alloc
+ check mirror_legs $vg $lv1 $start_count_p1
+ # FIXME: check mirror log
+ else
+ lvcreate -l2 -n $lv1 $vg
+ fi
+
+ lvs -a -o name,copy_percent,devices $vg
+ if ! $active; then
+ lvchange -an $vg/$lv1
+ fi
+
+ # Are there extra devices for the log or do we overlap
+ if [ $(($finish_count_p1 + $finish_log_count)) -gt ${#dev_array[@]} ]; then
+ alloc="--alloc anywhere"
+ fi
+
+ echo y | lvconvert -m $finish_count --mirrorlog $finish_log_type \
+ $vg/$lv1 $alloc
+
+ if ! $active; then
+ lvchange -ay $vg/$lv1
+ fi
+
+ check mirror_no_temporaries $vg $lv1
+ if [ "$finish_count_p1" -eq 1 ]; then
+ check linear $vg $lv1
+ else
+ if test -n "$alloc"; then
+ check mirror_nonredundant $vg $lv1
+ else
+ check mirror $vg $lv1
+ fi
+ check mirror_legs $vg $lv1 $finish_count_p1
+ fi
+}
+
+aux prepare_vg 5
+
+test_many() {
+ i=$1
+ for j in $(seq 0 3); do
+ for k in core disk mirrored; do
+ for l in core disk mirrored; do
+ if test "$i" -eq "$j" && test "$k" = "$l"; then continue; fi
+ : ----------------------------------------------------
+ : "Testing mirror conversion -m$i/$k -> -m$j/$l"
+ : ----------------------------------------------------
+ test_lvconvert $i $k $j $l 0
+ test_lvconvert $i $k $j $l 1
+ done
+ done
+ done
+}
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+# convert from linear to 2-way mirror
+aux prepare_vg 5
+lvcreate -l2 -n $lv1 $vg $dev1
+lvconvert -i1 -m+1 $vg/$lv1 $dev2 $dev3:0-1
+check mirror $vg $lv1 $dev3
+
+# convert from 2-way mirror to linear
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvconvert -m-1 $vg/$lv1
+check linear $vg $lv1
+lvremove -ff $vg
+# and now try removing a specific leg (bz453643)
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvconvert -m0 $vg/$lv1 $dev2
+check lv_on $vg $lv1 $dev1
+lvremove -ff $vg
+
+# convert from disklog to corelog, active
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvconvert -f --mirrorlog core $vg/$lv1
+check mirror $vg $lv1 core
+lvremove -ff $vg
+
+# convert from corelog to disklog, active
+aux prepare_vg 5
+lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $dev1 $dev2
+lvconvert --mirrorlog disk $vg/$lv1 $dev3:0-1
+check mirror $vg $lv1 $dev3
+lvremove -ff $vg
+
+# bz192865: lvconvert log of an inactive mirror lv
+# convert from disklog to corelog, inactive
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvchange -an $vg/$lv1
+echo y | lvconvert -f --mirrorlog core $vg/$lv1
+check mirror $vg $lv1 core
+lvremove -ff $vg
+
+# convert from corelog to disklog, inactive
+aux prepare_vg 5
+lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $dev1 $dev2
+lvchange -an $vg/$lv1
+lvconvert --mirrorlog disk $vg/$lv1 $dev3:0-1
+check mirror $vg $lv1 $dev3
+lvremove -ff $vg
+
+# convert linear to 2-way mirror with 1 PV
+aux prepare_vg 5
+lvcreate -l2 -n $lv1 $vg $dev1
+not lvconvert -m+1 --mirrorlog core $vg/$lv1 $dev1
+lvremove -ff $vg
+
+# Start w/ 3-way mirror
+# Test pulling primary image before mirror in-sync (should fail)
+# Test pulling primary image after mirror in-sync (should work)
+# Test that the correct devices remain in the mirror
+lvcreate -l2 -m2 -n $lv1 $vg $dev1 $dev2 $dev4 $dev3:0
+# FIXME:
+# This is somewhat timing dependent - sync /could/ finish before
+# we get a chance to have this command fail
+should not lvconvert -m-1 $vg/$lv1 $dev1
+
+lvconvert $vg/$lv1 # wait
+lvconvert -m2 $vg/$lv1 $dev1 $dev2 $dev4 $dev3:0 # If the above "should" failed...
+
+lvconvert -m-1 $vg/$lv1 $dev1
+check mirror_images_on $lv1 $dev2 $dev4
+lvconvert -m-1 $vg/$lv1 $dev2
+check linear $vg $lv1
+check lv_on $vg $lv1 $dev4
+
+# No parallel lvconverts on a single LV please
+
+aux prepare_vg 5
+lvcreate -l5 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+check mirror $vg $lv1
+check mirror_legs $vg $lv1 2
+lvconvert -m+1 -b $vg/$lv1 $dev4
+
+# Next convert should fail b/c we can't have 2 at once
+should not lvconvert -m+1 $vg/$lv1 $dev5
+lvconvert $vg/$lv1 # wait
+lvconvert -m2 $vg/$lv1 # In case the above "should" actually failed
+
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 3
+
+# add 1 mirror to core log mirror, but
+# implicitly keep log as 'core'
+aux prepare_vg 5
+lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $dev1 $dev2
+lvconvert -m +1 -i1 $vg/$lv1
+
+check mirror $vg $lv1 core
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 3
+
+# remove 1 mirror from corelog'ed mirror; should retain 'core' log type
+aux prepare_vg 5
+lvcreate -l2 -m2 --corelog -n $lv1 $vg
+lvconvert -m -1 -i1 $vg/$lv1
+
+check mirror $vg $lv1 core
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 2
+
+# add 1 mirror then add 1 more mirror during conversion
+# FIXME this has been explicitly forbidden?
+#aux prepare_vg 5
+#lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+#lvconvert -m+1 -b $vg/$lv1 $dev4
+#lvconvert -m+1 $vg/$lv1 $dev5
+#
+#check mirror $vg $lv1 $dev3
+#check mirror_no_temporaries $vg $lv1
+#check mirror_legs $vg $lv1 4
+
+# Linear to mirror with mirrored log using --alloc anywhere
+aux prepare_vg 5
+lvcreate -l2 -n $lv1 $vg $dev1
+lvconvert -m +1 --mirrorlog mirrored $vg/$lv1 $dev1 $dev2 --alloc anywhere
+should check mirror $vg $lv1
+
+# convert inactive mirror and start polling
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+lvchange -an $vg/$lv1
+lvconvert -m+1 $vg/$lv1 $dev4
+lvchange -ay $vg/$lv1
+lvconvert $vg/$lv1 # wait
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+
+# ---------------------------------------------------------------------
+# removal during conversion
+
+# "remove newly added mirror"
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+lvconvert -m+1 -b $vg/$lv1 $dev4
+lvconvert -m-1 $vg/$lv1 $dev4
+lvconvert $vg/$lv1 # wait
+
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 2
+
+# "remove one of newly added mirrors"
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+lvconvert -m+2 -b $vg/$lv1 $dev4 $dev5
+lvconvert -m-1 $vg/$lv1 $dev4
+lvconvert $vg/$lv1 # wait
+
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 3
+
+# "remove from original mirror (the original is still mirror)"
+aux prepare_vg 5
+lvcreate -l2 -m2 -n $lv1 $vg $dev1 $dev2 $dev5 $dev3:0
+lvconvert -m+1 -b $vg/$lv1 $dev4
+lvconvert -m-1 $vg/$lv1 $dev2
+lvconvert $vg/$lv1
+
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 3
+
+# "remove from original mirror (the original becomes linear)"
+aux prepare_vg 5
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+lvconvert -m+1 -b $vg/$lv1 $dev4
+lvconvert -m-1 $vg/$lv1 $dev2
+lvconvert $vg/$lv1
+
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 2
+
+# ---------------------------------------------------------------------
+
+# "rhbz440405: lvconvert -m0 incorrectly fails if all PEs allocated"
+aux prepare_vg 5
+lvcreate -l`pvs --noheadings -ope_count $dev1` -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+while [ `lvs --noheadings -o copy_percent $vg/$lv1` != "100.00" ]; do sleep 1; done
+lvconvert -m0 $vg/$lv1 $dev1
+check linear $vg $lv1
+
+# "rhbz264241: lvm mirror doesn't lose it's "M" --nosync attribute after being down and the up converted"
+aux prepare_vg 5
+lvcreate -l2 -m1 -n$lv1 --nosync $vg
+lvconvert -m0 $vg/$lv1
+lvconvert -m1 $vg/$lv1
+lvs --noheadings -o attr $vg/$lv1 | grep '^ *m'
+
+# lvconvert from linear (on multiple PVs) to mirror
+aux prepare_vg 5
+lvcreate -l 8 -n $lv1 $vg $dev1:0-3 $dev2:0-3
+lvconvert -m1 $vg/$lv1
+
+should check mirror $vg $lv1
+check mirror_legs $vg $lv1 2
+
+# BZ 463272: disk log mirror convert option is lost if downconvert option is also given
+aux prepare_vg 5
+lvcreate -l1 -m2 --corelog -n $lv1 $vg $dev1 $dev2 $dev3
+while [ `lvs --noheadings -o copy_percent $vg/$lv1` != "100.00" ]; do sleep 1; done
+lvconvert -m1 --mirrorlog disk $vg/$lv1
+check mirror $vg $lv1
+not check mirror $vg $lv1 core
+
+# ---
+# add mirror and disk log
+
+# "add 1 mirror and disk log"
+aux prepare_vg 5
+lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $dev1 $dev2
+
+# FIXME on next line, specifying $dev3:0 $dev4 (i.e log device first) fails (!)
+lvconvert -m+1 --mirrorlog disk -i1 $vg/$lv1 $dev4 $dev3:0
+
+check mirror $vg $lv1 $dev3
+check mirror_no_temporaries $vg $lv1
+check mirror_legs $vg $lv1 3
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+prepare_vg 5
+prepare_dmeventd
+
+which mkfs.ext2 || exit 200
+
+lvcreate -m 3 --ig -L 1 -n 4way $vg
+lvchange --monitor y $vg/4way
+disable_dev $dev2 $dev4
+mkfs.ext2 $DM_DEV_DIR/$vg/4way
+sleep 10 # FIXME: need a "poll" utility, akin to "check"
+enable_dev $dev2 $dev4
+check mirror $vg 4way
+check mirror_legs $vg 4way 2
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+prepare_vg 4
+
+# Clean-up and create a 2-way mirror, where the the
+# leg devices are always on $dev[12] and the log
+# is always on $dev3. ($dev4 behaves as a spare)
+cleanup() {
+ vgreduce --removemissing $vg
+ for d in "$@"; do enable_dev $d; done
+ for d in "$@"; do vgextend $vg $d; done
+ lvremove -ff $vg/mirror
+ lvcreate -m 1 --ig -l 2 -n mirror $vg $dev1 $dev2 $dev3:0
+}
+
+repair() {
+ lvconvert --repair --use-policies --config "$1" $vg/mirror
+}
+
+lvcreate -m 1 -L 1 -n mirror $vg
+lvchange -a n $vg/mirror
+
+# Fail a leg of a mirror.
+aux disable_dev $dev1
+lvchange --partial -a y $vg/mirror
+repair 'activation { mirror_image_fault_policy = "remove" }'
+check linear $vg mirror
+aux cleanup $dev1
+
+# Fail a leg of a mirror.
+# Expected result: Mirror (leg replaced)
+aux disable_dev $dev1
+repair 'activation { mirror_image_fault_policy = "replace" }'
+check mirror $vg mirror
+lvs | grep mirror_mlog
+aux cleanup $dev1
+
+# Fail a leg of a mirror (use old name for policy specification)
+# Expected result: Mirror (leg replaced)
+aux disable_dev $dev1
+repair 'activation { mirror_device_fault_policy = "replace" }'
+check mirror $vg mirror
+lvs | grep mirror_mlog
+aux cleanup $dev1
+
+# Fail a leg of a mirror w/ no available spare
+# Expected result: 2-way with corelog
+aux disable_dev $dev2 $dev4
+repair 'activation { mirror_image_fault_policy = "replace" }'
+check mirror $vg mirror
+lvs | not grep mirror_mlog
+aux cleanup $dev2 $dev4
+
+# Fail the log device of a mirror w/ no available spare
+# Expected result: mirror w/ corelog
+aux disable_dev $dev3 $dev4
+repair 'activation { mirror_image_fault_policy = "replace" }' $vg/mirror
+check mirror $vg mirror
+lvs | not grep mirror_mlog
+aux cleanup $dev3 $dev4
+
+# Fail the log device with a remove policy
+# Expected result: mirror w/ corelog
+lvchange -a y $vg/mirror
+aux disable_dev $dev3 $dev4
+repair 'activation { mirror_log_fault_policy = "remove" }'
+check mirror $vg mirror core
+lvs | not grep mirror_mlog
+cleanup $dev3 $dev4
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+prepare_vg 6
+
+# multiple failures, full replace
+lvcreate --mirrorlog disk -m 2 --ig -L 1 -n 3way $vg $dev1 $dev2 $dev3 $dev4:0-1
+disable_dev $dev1 $dev2
+echo y | lvconvert --repair $vg/3way 2>&1 | tee 3way.out
+lvs -a -o +devices | not grep unknown
+not grep "WARNING: Failed" 3way.out
+vgreduce --removemissing $vg
+check mirror $vg 3way
+enable_dev $dev1 $dev2
+
+vgremove -ff $vg; vgcreate -c n $vg $dev1 $dev2 $dev3 $dev4 $dev5
+
+# multiple failures, partial replace
+lvcreate --mirrorlog disk -m 2 --ig -L 1 -n 3way $vg $dev1 $dev2 $dev3 $dev4
+disable_dev $dev1 $dev2
+echo y | lvconvert --repair $vg/3way 2>&1 | tee 3way.out
+grep "WARNING: Failed" 3way.out
+lvs -a -o +devices | not grep unknown
+vgreduce --removemissing $vg
+check mirror $vg 3way
+enable_dev $dev1 $dev2
+lvchange -a n $vg/3way
+
+vgremove -ff $vg; vgcreate -c n $vg $dev1 $dev2 $dev3
+
+lvcreate --mirrorlog disk -m 1 --ig -L 1 -n 2way $vg $dev1 $dev2 $dev3
+disable_dev $dev1
+echo y | lvconvert --repair $vg/2way 2>&1 | tee 2way.out
+grep "WARNING: Failed" 2way.out
+lvs -a -o +devices | not grep unknown
+vgreduce --removemissing $vg
+check mirror $vg 2way
+enable_dev $dev1 $dev2
+lvchange -a n $vg/2way
+
+vgremove -ff $vg; vgcreate -c n $vg $dev1 $dev2 $dev3 $dev4
+
+# Test repair of inactive mirror with log failure
+# Replacement should fail, but covert should succeed (switch to corelog)
+lvcreate -m 2 --ig -l 2 -n mirror2 $vg $dev1 $dev2 $dev3 $dev4:0
+vgchange -a n $vg
+pvremove -ff -y $dev4
+echo 'y' | lvconvert -y --repair $vg/mirror2
+check mirror $vg mirror2
+vgs
+
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+prepare_vg 5
+
+exit 200 # this breaks upstream .33 and RHEL6 kernel :(
+
+# fail multiple devices
+
+lvcreate -m 3 --ig -L 1 -n 4way $vg
+disable_dev $dev2 $dev4
+mkfs.ext3 $DM_DEV_DIR/$vg/4way
+enable_dev $dev2 $dev4
+echo n | lvconvert --repair $vg/4way 2>&1 | tee 4way.out
+lvs -a -o +devices | not grep unknown
+vgreduce --removemissing $vg
+check mirror $vg 4way
+lvchange -a n $vg/4way
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+
+# fail multiple devices
+
+aux prepare_vg 5
+lvcreate -m 3 --ig -L 1 -n 4way $vg $dev1 $dev2 $dev3 $dev4 $dev5:0
+disable_dev $dev2 $dev4
+echo n | lvconvert --repair $vg/4way 2>&1 | tee 4way.out
+lvs -a -o +devices | not grep unknown
+vgreduce --removemissing $vg
+enable_dev $dev2 $dev4
+check mirror $vg 4way $dev5
+
+aux prepare_vg 5
+lvcreate -m 2 --ig -L 1 -n 3way $vg
+disable_dev $dev1 $dev2
+echo n | lvconvert --repair $vg/3way
+check linear $vg 3way
+lvs -a -o +devices | not grep unknown
+lvs -a -o +devices | not grep mlog
+dmsetup ls | grep $PREFIX | not grep mlog
+vgreduce --removemissing $vg
+enable_dev $dev1 $dev2
+check linear $vg 3way
+
+# fail just log and get it removed
+
+aux prepare_vg 5
+lvcreate -m 2 --ig -L 1 -n 3way $vg $dev1 $dev2 $dev3 $dev4:0
+disable_dev $dev4
+echo n | lvconvert --repair $vg/3way
+check mirror $vg 3way core
+lvs -a -o +devices | not grep unknown
+lvs -a -o +devices | not grep mlog
+dmsetup ls | grep $PREFIX | not grep mlog
+vgreduce --removemissing $vg
+enable_dev $dev4
+
+aux prepare_vg 5
+lvcreate -m 1 --ig -L 1 -n 2way $vg $dev1 $dev2 $dev3:0
+disable_dev $dev3
+echo n | lvconvert --repair $vg/2way
+check mirror $vg 2way core
+lvs -a -o +devices | not grep unknown
+lvs -a -o +devices | not grep mlog
+vgreduce --removemissing $vg
+enable_dev $dev3
+
+# fail single devices
+
+aux prepare_vg 5
+vgreduce $vg $dev4
+
+lvcreate -m 1 --ig -L 1 -n mirror $vg
+lvchange -a n $vg/mirror
+vgextend $vg $dev4
+disable_dev $dev1
+lvchange --partial -a y $vg/mirror
+
+not vgreduce -v --removemissing $vg
+lvconvert -y --repair $vg/mirror
+vgreduce --removemissing $vg
+
+enable_dev $dev1
+vgextend $vg $dev1
+disable_dev $dev2
+lvconvert -y --repair $vg/mirror
+vgreduce --removemissing $vg
+
+enable_dev $dev2
+vgextend $vg $dev2
+disable_dev $dev3
+lvconvert -y --repair $vg/mirror
+vgreduce --removemissing $vg
+enable_dev $dev3
+vgextend $vg $dev3
+lvremove -ff $vg
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_vg 4
+lvcreate -m 1 --mirrorlog disk --ig -L 1 -n mirror $vg
+not lvconvert -m 2 --mirrorlog core $vg/mirror $dev3 2>&1 | tee errs
+grep "two steps" errs
+lvconvert -m 2 $vg/mirror $dev3
+lvconvert --mirrorlog core $vg/mirror
+not lvconvert -m 1 --mirrorlog disk $vg/mirror $dev3 2>&1 | tee errs
+grep "two steps" errs
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+aux prepare_vg 5 80
+
+# 2-way mirror with corelog, 2 PVs
+lvcreate -l2 -m1 --mirrorlog core -n $lv1 $vg $dev1 $dev2
+check mirror_images_redundant $vg $lv1
+lvremove -ff $vg
+
+# 2-way mirror with disklog, 3 PVs
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+check mirror_images_redundant $vg $lv1
+check mirror_log_on $vg $lv1 $dev3
+lvremove -ff $vg
+
+# 3-way mirror with disklog, 4 PVs
+lvcreate -l2 -m2 --mirrorlog disk -n $lv1 $vg $dev1 $dev2 $dev4 $dev3:0-1
+check mirror_images_redundant $vg $lv1
+check mirror_log_on $vg $lv1 $dev3
+lvremove -ff $vg
+
+# lvcreate --nosync is in 100% sync after creation (bz429342)
+lvcreate -l2 -m1 --nosync -n $lv1 $vg $dev1 $dev2 $dev3:0-1 2>out
+grep "New mirror won't be synchronised." out
+lvs -o copy_percent --noheadings $vg/$lv1 | grep 100.00
+lvremove -ff $vg
+
+# creating 2-way mirror with disklog from 2 PVs fails
+not lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# 'Exercise some lvcreate diagnostics'
+
+. ./test-utils.sh
+
+cleanup_lvs() {
+ lvremove -ff $vg
+ if dmsetup table|grep $vg; then
+ echo "ERROR: lvremove did leave some some mappings in DM behind!"
+ return 1
+ fi
+}
+
+prepare_pvs 2
+aux pvcreate --metadatacopies 0 $dev1
+aux vgcreate -c n $vg $devs
+
+# ---
+# Create snapshots of LVs on --metadatacopies 0 PV (bz450651)
+lvcreate -n$lv1 -l4 $vg $dev1
+lvcreate -n$lv2 -l4 -s $vg/$lv1
+cleanup_lvs
+
+# ---
+# Create mirror on two devices with mirrored log using --alloc anywhere
+lvcreate -m 1 -l4 -n $lv1 --mirrorlog mirrored $vg --alloc anywhere $dev1 $dev2
+cleanup_lvs
+
+# --
+# Create mirror on one dev with mirrored log using --alloc anywhere, should fail
+not lvcreate -m 1 -l4 -n $lv1 --mirrorlog mirrored $vg --alloc anywhere $dev1
+cleanup_lvs
--- /dev/null
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_pvs 3
+# not required, just testing
+aux pvcreate --metadatacopies 0 $dev1
+
+vgcreate -c n $vg $devs
+pvchange --addtag fast $devs
+
+# 3 stripes with 3 PVs (selected by tag, @fast) is fine
+lvcreate -l3 -i3 $vg @fast
+
+# too many stripes(4) for 3 PVs
+not lvcreate -l4 -i4 $vg @fast
+
+# 2 stripes is too many with just one PV
+not lvcreate -l2 -i2 $vg $DM_DEV_DIR/mapper/pv1
+
+# lvcreate mirror
+lvcreate -l1 -m1 $vg @fast
+
+# lvcreate mirror w/corelog
+lvcreate -l1 -m2 --corelog $vg @fast
+
+# lvcreate mirror w/no free PVs
+not lvcreate -l1 -m2 $vg @fast
+
+# lvcreate mirror (corelog, w/no free PVs)
+not lvcreate -l1 -m3 --corelog $vg @fast
+
+# lvcreate mirror with a single PV arg
+not lvcreate -l1 -m1 --corelog $vg $dev1
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_pvs 3
+
+vgcreate -c n -s 1k $vg $devs
+
+lvcreate -n one -l 10 $vg
+lvcreate -s -l 8 -n snapA $vg/one
+lvcreate -s -c 4k -l 8 -n snapX1 $vg/one
+lvcreate -s -c 8k -l 16 -n snapX2 $vg/one
+
+# Check that snapshots that are too small are caught with correct error.
+not lvcreate -s -c 8k -l 8 -n snapX3 $vg/one 2>&1 | tee lvcreate.out
+not grep "suspend origin one" lvcreate.out
+grep "Unable to create a snapshot" lvcreate.out
+
+not lvcreate -s -l 4 -n snapB $vg/one 2>&1 | tee lvcreate.out
+not grep "suspend origin one" lvcreate.out
+grep "Unable to create a snapshot" lvcreate.out
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# 'Exercise some lvcreate diagnostics'
+
+. ./test-utils.sh
+
+aux prepare_pvs 4
+aux pvcreate --metadatacopies 0 $dev1
+vgcreate -cn $vg $devs
+
+# "lvcreate rejects repeated invocation (run 2 times) (bz178216)"
+lvcreate -n $lv -l 4 $vg
+not lvcreate -n $lv -l 4 $vg
+lvremove -ff $vg/$lv
+# try to remove it again - should fail (but not segfault)
+not lvremove -ff $vg/$lv
+
+# "lvcreate rejects a negative stripe_size"
+not lvcreate -L 64m -n $lv -i2 --stripesize -4 $vg 2>err;
+grep "^ Negative stripesize is invalid\$" err
+
+# 'lvcreate rejects a too-large stripesize'
+not lvcreate -L 64m -n $lv -i2 --stripesize 4294967291 $vg 2>err
+grep "^ Stripe size cannot be larger than" err
+
+# 'lvcreate w/single stripe succeeds with diagnostics to stdout'
+lvcreate -L 64m -n $lv -i1 --stripesize 4 $vg 2> err | tee out
+grep "^ Ignoring stripesize argument with single stripe\$" out
+lvdisplay $vg
+lvremove -ff $vg
+
+# 'lvcreate w/default (64KB) stripe size succeeds with diagnostics to stdout'
+lvcreate -L 64m -n $lv -i2 $vg > out
+grep "^ Using default stripesize" out
+lvdisplay $vg
+check_lv_field_ $vg/$lv stripesize "64.00k"
+lvremove -ff $vg
+
+# 'lvcreate rejects an invalid number of stripes'
+not lvcreate -L 64m -n $lv -i129 $vg 2>err
+grep "^ Number of stripes (129) must be between 1 and 128\$" err
+
+# The case on lvdisplay output is to verify that the LV was not created.
+# 'lvcreate rejects an invalid stripe size'
+not lvcreate -L 64m -n $lv -i2 --stripesize 3 $vg 2>err
+grep "^ Invalid stripe size" err
+case $(lvdisplay $vg) in "") true ;; *) false ;; esac
+
+# Setting max_lv works. (bz490298)
+lvremove -ff $vg
+vgchange -l 3 $vg
+lvcreate -l1 -n $lv1 $vg
+lvcreate -l1 -s -n $lv2 $vg/$lv1
+lvcreate -l1 -n $lv3 $vg
+not lvcreate -l1 -n $lv4 $vg
+
+lvremove -ff $vg/$lv3
+lvcreate -l1 -s -n $lv3 $vg/$lv1
+not lvcreate -l1 -n $lv4 $vg
+not lvcreate -l1 -m1 -n $lv4 $vg
+
+lvremove -ff $vg/$lv3
+lvcreate -l1 -m1 -n $lv3 $vg
+lvs
+vgs -o +max_lv
+not lvcreate -l1 -n $lv4 $vg
+not lvcreate -l1 -m1 -n $lv4 $vg
+
+lvconvert -m0 $vg/$lv3
+lvconvert -m2 -i 1 $vg/$lv3
+lvconvert -m1 $vg/$lv3
+
+not vgchange -l 2
+vgchange -l 4
+vgs $vg
+
+lvremove -ff $vg
+vgchange -l 0 $vg
+
+# lvcreate rejects invalid chunksize, accepts between 4K and 512K
+# validate origin_size
+vgremove -ff $vg
+vgcreate -cn $vg $devs
+lvcreate -L 32m -n $lv1 $vg
+not lvcreate -L 8m -n $lv2 -s --chunksize 3k $vg/$lv1
+not lvcreate -L 8m -n $lv2 -s --chunksize 1024k $vg/$lv1
+lvcreate -L 8m -n $lv2 -s --chunksize 4k $vg/$lv1
+check_lv_field_ $vg/$lv2 chunk_size 4.00k
+check_lv_field_ $vg/$lv2 origin_size 32.00m
+lvcreate -L 8m -n $lv3 -s --chunksize 512k $vg/$lv1
+check_lv_field_ $vg/$lv3 chunk_size 512.00k
+check_lv_field_ $vg/$lv3 origin_size 32.00m
+lvremove -ff $vg
+vgchange -l 0 $vg
+
+# regionsize must be
+# - nonzero (bz186013)
+# - a power of 2 and a multiple of page size
+# - <= size of LV
+not lvcreate -L 32m -n $lv -R0 $vg 2>err
+grep "Non-zero region size must be supplied." err
+not lvcreate -L 32m -n $lv -R 11k $vg
+not lvcreate -L 32m -n $lv -R 1k $vg
+lvcreate -L 32m -n $lv --regionsize 128m -m 1 $vg
+check_lv_field_ $vg/$lv regionsize "32.00m"
+lvremove -ff $vg
+lvcreate -L 32m -n $lv --regionsize 4m -m 1 $vg
+check_lv_field_ $vg/$lv regionsize "4.00m"
+lvremove -ff $vg
+
+# snapshot with virtual origin works
+lvcreate -s --virtualoriginsize 64m -L 32m -n $lv1 $vg
+lvrename $vg/$lv1 $vg/$lv2
+lvcreate -s --virtualoriginsize 64m -L 32m -n $lv1 $vg
+lvchange -a n $vg/$lv1
+lvremove $vg/$lv1
+lvremove -ff $vg
+
+# readahead default (auto), none, #, auto
+lvcreate -L 32m -n $lv $vg
+check_lv_field_ $vg/$lv lv_read_ahead "auto"
+lvremove -ff $vg
+lvcreate -L 32m -n $lv --readahead none $vg
+check_lv_field_ $vg/$lv lv_read_ahead "0"
+lvremove -ff $vg
+lvcreate -L 32m -n $lv --readahead 8k $vg
+check_lv_field_ $vg/$lv lv_read_ahead "8.00k"
+lvremove -ff $vg
+lvcreate -L 32m -n $lv --readahead auto $vg
+check_lv_field_ $vg/$lv lv_read_ahead "auto"
+lvremove -ff $vg
+
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# 'Check extents percentage arguments'
+
+. ./test-utils.sh
+
+aux prepare_vg 2 128
+
+lvcreate -L 64m -n $lv $vg
+
+# 'lvextend rejects both size and extents without PVs'
+not lvextend -l 10 -L 64m $vg/$lv 2>err
+grep "^ Please specify either size or extents but not both.\$" err
+
+# 'lvextend rejects both size and extents with PVs'
+not lvextend -l 10 -L 64m $vg/$lv $dev1 2>err
+grep "^ Please specify either size or extents but not both.\$" err
+
+# 'lvextend accepts no size or extents but one PV - bz154691'
+lvextend $vg/$lv $dev1 >out
+grep "^ Logical volume $lv successfully resized\$" out
+check_pv_field_ $dev1 pv_free "0"
+
+lvremove -f $vg/$lv
+
+# 'lvextend computes necessary free space correctly - bz213552'
+vgsize=$(vgs -o vg_extent_count --noheadings)
+lvcreate -l $vgsize -n $lv $vg
+lvreduce -f -l $(( $vgsize / 2 )) $vg/$lv
+lvextend -l $vgsize $vg/$lv
+
+# 'Reset LV to original size'
+lvremove -f $vg/$lv
+lvcreate -L 64m -n $lv $vg
+
+# 'lvextend accepts no size but extents 100%PVS and two PVs - bz154691'
+lvextend -l +100%PVS $vg/$lv $dev1 $dev2 >out
+grep "^ Logical volume $lv successfully resized\$" out
+check_pv_field_ $dev1 pv_free "0"
+check_pv_field_ $dev2 pv_free "0"
+
+# Exercise the range overlap code. Allocate every 2 extents.
+#
+# Physical Extents
+# 1 2
+#012345678901234567890123
+#
+#aaXXaaXXaaXXaaXXaaXXaaXX - (a)llocated
+#rrrXXXrrrXXXrrrXXXrrrXXX - (r)ange on cmdline
+#ooXXXXXXoXXXooXXXXXXoXXX - (o)verlap of range and allocated
+#
+# Key: a - allocated
+# F - free
+# r - part of a range on the cmdline
+# N - not on cmdline
+#
+# Create the LV with 12 extents, allocated every other 2 extents.
+# Then extend it, with a range of PVs on the cmdline of every other 3 extents.
+# Total number of extents should be 12 + overlap = 12 + 6 = 18.
+# Thus, total size for the LV should be 18 * 4M = 72M
+#
+# 'Reset LV to 12 extents, allocate every other 2 extents'
+create_pvs=`for i in $(seq 0 4 20); do echo -n "\$dev1:$i-$(($i + 1)) "; done`
+lvremove -f $vg/$lv
+lvcreate -l 12 -n $lv $vg $create_pvs
+check_lv_field_ $vg/$lv lv_size "48.00m"
+
+# 'lvextend with partially allocated PVs and extents 100%PVS with PE ranges'
+extend_pvs=`for i in $(seq 0 6 18); do echo -n "\$dev1:$i-$(($i + 2)) "; done`
+lvextend -l +100%PVS $vg/$lv $extend_pvs >out
+grep "^ Logical volume $lv successfully resized\$" out
+check_lv_field_ $vg/$lv lv_size "72.00m"
+
+# Simple seg_count validation; initially create the LV with half the # of
+# extents (should be 1 lv segment), extend it (should go to 2 segments),
+# then reduce (should be back to 1)
+# FIXME: test other segment fields such as seg_size, pvseg_start, pvseg_size
+lvremove -f $vg/$lv
+pe_count=$(pvs -o pv_pe_count --noheadings $dev1)
+pe1=$(( $pe_count / 2 ))
+lvcreate -l $pe1 -n $lv $vg
+pesize=$(lvs -ovg_extent_size --units b --nosuffix --noheadings $vg/$lv)
+segsize=$(( $pe1 * $pesize / 1024 / 1024 ))m
+check_lv_field_ $vg/$lv seg_count 1
+check_lv_field_ $vg/$lv seg_start 0
+check_lv_field_ $vg/$lv seg_start_pe 0
+#check_lv_field_ $vg/$lv seg_size $segsize
+lvextend -l +$(( $pe_count * 1 )) $vg/$lv
+check_lv_field_ $vg/$lv seg_count 2
+lvreduce -f -l -$(( $pe_count * 1 )) $vg/$lv
+check_lv_field_ $vg/$lv seg_count 1
+
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+extend() {
+ lvextend --use-policies --config "activation { snapshot_extend_threshold = $1 }" $vg/snap
+}
+
+write() {
+ mount $DM_DEV_DIR/$vg/snap mnt
+ dd if=/dev/zero of=mnt/file$1 bs=1k count=$2
+ umount mnt
+}
+
+percent() {
+ lvs $vg/snap -o snap_percent --noheadings | cut -c4- | cut -d. -f1
+}
+
+which mkfs.ext2 || exit 200
+
+aux prepare_vg 2
+aux prepare_dmeventd
+
+lvcreate -l 8 -n base $vg
+mkfs.ext2 $DM_DEV_DIR/$vg/base
+
+lvcreate -s -l 4 -n snap $vg/base
+lvchange --monitor y $vg/snap
+
+mkdir mnt
+
+write 1 4096
+pre=`percent`
+sleep 10 # dmeventd only checks every 10 seconds :(
+post=`percent`
+
+test $pre = $post
+write 2 5000
+pre=`percent`
+sleep 10 # dmeventd only checks every 10 seconds :(
+post=`percent`
+test $pre -gt $post
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+extend() {
+ lvextend --use-policies --config "activation { snapshot_extend_threshold = $1 }" $vg/snap
+}
+
+write() {
+ mount $DM_DEV_DIR/$vg/snap mnt
+ dd if=/dev/zero of=mnt/file$1 bs=1k count=$2
+ umount mnt
+}
+
+percent() {
+ lvs $vg/snap -o snap_percent --noheadings | cut -c4- | cut -d. -f1
+}
+
+which mkfs.ext2 || exit 200
+
+aux prepare_vg 2
+lvcreate -l 8 -n base $vg
+mkfs.ext2 $DM_DEV_DIR/$vg/base
+
+lvcreate -s -l 4 -n snap $vg/base
+mkdir mnt
+
+write 1 4096
+pre=`percent`
+extend 50
+post=`percent`
+
+test $pre = $post
+write 2 4096
+pre=`percent`
+extend 50
+post=`percent`
+test $pre -gt $post
--- /dev/null
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#
+# tests lvm initialization, and especially negative tests of error paths
+#
+
+. ./test-utils.sh
+
+aux prepare_devs 5
+
+# invalid units
+not pvs --config 'global { units = "<" }'
+
--- /dev/null
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_pvs 5
+
+vgcreate $vg1 $dev1
+vgcreate $vg2 $dev3
+
+disable_dev $dev1
+pvscan
+vgcreate $vg1 $dev2
+enable_dev $dev1
+pvs
+pvs
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+aux prepare_vg 5 80
+
+# extend 2-way mirror
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvchange -an $vg/$lv1
+lvextend -l+2 $vg/$lv1
+check mirror $vg $lv1 $dev3
+check mirror_images_contiguous $vg $lv1
+lvremove -ff $vg
+
+# reduce 2-way mirror
+lvcreate -l4 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvchange -an $vg/$lv1
+lvreduce -l-2 $vg/$lv1
+check mirror $vg $lv1 $dev3
+lvremove -ff $vg
+
+# extend 2-way mirror (cling if not contiguous)
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0-1
+lvcreate -l1 -n $lv2 $vg $dev1
+lvcreate -l1 -n $lv3 $vg $dev2
+lvchange -an $vg/$lv1
+lvextend -l+2 $vg/$lv1
+check mirror $vg $lv1 $dev3
+check mirror_images_clung $vg $lv1
+lvremove -ff $vg
--- /dev/null
+# Copyright (C) 2007-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_vg 2
+
+lvcreate -L 10M -n lv -i2 $vg
+lvresize -l +4 $vg/lv
+lvremove -ff $vg
+
+lvcreate -L 64M -n $lv -i2 $vg
+not lvresize -v -l +4 xxx/$lv
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# 'Test for proper escaping of strings in metadata (bz431474)'
+
+. ./test-utils.sh
+
+aux prepare_devs 1
+
+pv_ugly="__\"!@#\$%^&*,()|@||'\\\"__pv1"
+
+# 'set up temp files, loopback devices'
+name=$(basename "$dev1")
+dmsetup rename "$name" "$PREFIX$pv_ugly"
+dev1=$(dirname "$dev1")/$PREFIX$pv_ugly
+
+# 'pvcreate, vgcreate on filename with backslashed chars'
+pvcreate "$dev1"
+vgcreate $vg "$dev1"
+
+# 'no parse errors and VG really exists'
+vgs 2>err
+not grep "Parse error" err;
+vgs $vg
+
--- /dev/null
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_devs 6
+
+echo Make sure we can ignore / un-ignore mdas on a per-PV basis
+for pv_in_vg in 1 0; do
+for mdacp in 1 2; do
+ pvcreate --metadatacopies $mdacp $dev1 $dev2
+ pvcreate --metadatacopies 0 $dev3
+ if [ $pv_in_vg = 1 ]; then
+ vgcreate -c n "$vg" $dev1 $dev2 $dev3
+ fi
+ pvchange --metadataignore y $dev1
+ check_pv_field_ $dev1 pv_mda_count $mdacp
+ check_pv_field_ $dev1 pv_mda_used_count 0
+ check_pv_field_ $dev2 pv_mda_count $mdacp
+ check_pv_field_ $dev2 pv_mda_used_count $mdacp
+ if [ $pv_in_vg = 1 ]; then
+ check_vg_field_ $vg vg_mda_count $(($mdacp * 2))
+ check_vg_field_ $vg vg_mda_used_count $mdacp
+ check_vg_field_ $vg vg_mda_copies unmanaged
+ fi
+ pvchange --metadataignore n $dev1
+ check_pv_field_ $dev1 pv_mda_count $mdacp
+ check_pv_field_ $dev1 pv_mda_used_count $mdacp
+ if [ $pv_in_vg = 1 ]; then
+ check_vg_field_ $vg vg_mda_count $(($mdacp * 2))
+ check_vg_field_ $vg vg_mda_used_count $(($mdacp * 2))
+ check_vg_field_ $vg vg_mda_copies unmanaged
+ vgremove -f $vg
+ fi
+done
+done
+
+# Check if a PV has unignored (used) mdas, and if so, ignore
+pvignore_ () {
+ pv_mda_used_count=$(get_pv_field "$1" pv_mda_used_count)
+ if [ $pv_mda_used_count -ne 0 ]; then
+ pvchange --metadataignore y $1
+ fi
+}
+
+# Check if a PV has ignored mdas, and if so, unignore (make used)
+pvunignore_ () {
+ pv_mda_count=$(get_pv_field "$1" pv_mda_count)
+ pv_mda_used_count=$(get_pv_field "$1" pv_mda_used_count)
+ if [ $pv_mda_count -gt $pv_mda_used_count ]; then
+ pvchange --metadataignore n $1
+ fi
+}
+
+echo Test of vgmetadatacopies with vgcreate and vgchange
+for mdacp in 1 2; do
+ pvcreate --metadatacopies $mdacp $dev1 $dev2 $dev4 $dev5
+ check_pv_field_ $dev1 pv_mda_used_count $mdacp
+ check_pv_field_ $dev2 pv_mda_used_count $mdacp
+ check_pv_field_ $dev4 pv_mda_used_count $mdacp
+ check_pv_field_ $dev5 pv_mda_used_count $mdacp
+ pvcreate --metadatacopies 0 $dev3
+ vgcreate -c n "$vg" $dev1 $dev2 $dev3
+ check_vg_field_ $vg vg_mda_copies unmanaged
+ echo ensure both --vgmetadatacopies and --metadatacopies accepted
+ vgchange --metadatacopies $(($mdacp * 1)) $vg
+ echo --vgmetadatacopies is persistent on disk
+ echo --vgmetadatacopies affects underlying pv mda ignore
+ check_vg_field_ $vg vg_mda_copies $(($mdacp * 1))
+ check_vg_field_ $vg vg_mda_used_count $(($mdacp * 1))
+ vgchange --vgmetadatacopies $(($mdacp * 2)) $vg
+ check_vg_field_ $vg vg_mda_copies $(($mdacp * 2))
+ check_vg_field_ $vg vg_mda_used_count $(($mdacp * 2))
+ echo allow setting metadatacopies larger than number of PVs
+ vgchange --vgmetadatacopies $(($mdacp * 5)) $vg
+ check_vg_field_ $vg vg_mda_copies $(($mdacp * 5))
+ check_vg_field_ $vg vg_mda_used_count $(($mdacp * 2))
+ echo setting to 0 disables automatic balancing
+ vgchange --vgmetadatacopies unmanaged $vg
+ check_vg_field_ $vg vg_mda_copies unmanaged
+ vgremove -f $vg
+ echo vgcreate succeeds even when creating a VG w/all ignored mdas
+ pvchange --metadataignore y $dev1 $dev2
+ check_pv_field_ $dev1 pv_mda_count $mdacp
+ check_pv_field_ $dev2 pv_mda_used_count 0
+ vgcreate -c n "$vg" $dev1 $dev2
+ check_vg_field_ $vg vg_mda_copies unmanaged
+ vgremove -f $vg
+ echo vgcreate succeeds with a specific number of metadata copies
+ vgcreate -c n --vgmetadatacopies $(($mdacp * 2)) "$vg" $dev1 $dev2
+ check_vg_field_ $vg vg_mda_copies $(($mdacp * 2))
+ vgremove -f $vg
+ vgcreate -c n --vgmetadatacopies $(($mdacp * 1)) "$vg" $dev1 $dev2
+ check_vg_field_ $vg vg_mda_copies $(($mdacp * 1))
+ vgremove -f $vg
+ echo vgcreate succeeds with a larger value than total metadatacopies
+ vgcreate -c n --vgmetadatacopies $(($mdacp * 5)) "$vg" $dev1 $dev2
+ check_vg_field_ $vg vg_mda_copies $(($mdacp * 5))
+ vgremove -f $vg
+ echo vgcreate succeeds with --vgmetadatacopies unmanaged
+ vgcreate -c n --vgmetadatacopies unmanaged "$vg" $dev1 $dev2
+ check_vg_field_ $vg vg_mda_copies unmanaged
+ vgremove -f $vg
+ pvunignore_ $dev1
+ pvunignore_ $dev2
+ pvunignore_ $dev4
+ pvunignore_ $dev5
+ echo vgcreate succeds with small value of --metadatacopies, ignores mdas
+ vgcreate -c n --vgmetadatacopies 1 "$vg" $dev1 $dev2 $dev4 $dev5
+ check_vg_field_ $vg vg_mda_copies 1
+ check_vg_field_ $vg vg_mda_count $(($mdacp * 4))
+ check_vg_field_ $vg vg_mda_used_count 1
+ echo Setting a larger value should trigger non-ignore of mdas
+ vgchange --metadatacopies 3 $vg
+ check_vg_field_ $vg vg_mda_copies 3
+ check_vg_field_ $vg vg_mda_used_count 3
+ echo Setting all should trigger unignore of all mdas
+ vgchange --vgmetadatacopies all $vg
+ check_vg_field_ $vg vg_mda_count $(($mdacp * 4))
+ check_vg_field_ $vg vg_mda_copies unmanaged
+ check_vg_field_ $vg vg_mda_used_count $(($mdacp * 4))
+ echo --vgmetadatacopies 0 should be unmanaged for vgchange and vgcreate
+ vgchange --vgmetadatacopies 0 $vg
+ check_vg_field_ $vg vg_mda_copies unmanaged
+ vgremove -f $vg
+ vgcreate -c n --vgmetadatacopies 0 "$vg" $dev1 $dev2 $dev4 $dev5
+ check_vg_field_ $vg vg_mda_copies unmanaged
+ vgremove -f $vg
+done
+
+echo Test vgextend / vgreduce with vgmetadatacopies
+for mdacp in 1 2; do
+ pvcreate --metadatacopies $mdacp $dev1 $dev2 $dev4 $dev5
+ pvcreate --metadatacopies 0 $dev3
+ echo Set a large value of vgmetadatacopies
+ vgcreate -c n --vgmetadatacopies $(($mdacp * 5)) "$vg" $dev1 $dev2 $dev3
+ check_vg_field_ $vg vg_mda_copies $(($mdacp * 5))
+ echo Ignore mdas on devices to be used for vgextend
+ echo Large value of vgetadatacopies should automatically un-ignore mdas
+ pvchange --metadataignore y $dev4 $dev5
+ check_pv_field_ $dev4 pv_mda_used_count 0
+ vgextend $vg $dev4 $dev5
+ check_pv_field_ $dev4 pv_mda_used_count $mdacp
+ check_pv_field_ $dev5 pv_mda_used_count $mdacp
+ vgremove -f $vg
+ echo Set a small value of vgmetadatacopies
+ vgcreate -c n --vgmetadatacopies $(($mdacp * 1)) "$vg" $dev1 $dev2 $dev3
+ check_vg_field_ $vg vg_mda_copies $(($mdacp * 1))
+ echo Ignore mdas on devices to be used for vgextend
+ echo Small value of vgetadatacopies should leave mdas as ignored
+ pvchange --metadataignore y $dev4 $dev5
+ check_pv_field_ $dev4 pv_mda_used_count 0
+ vgextend $vg $dev4 $dev5
+ check_pv_field_ $dev4 pv_mda_used_count 0
+ check_pv_field_ $dev5 pv_mda_used_count 0
+ echo vgreduce of ignored pv w/mda should not trigger any change to ignore bits
+ vgreduce $vg $dev4
+ check_pv_field_ $dev4 pv_mda_used_count 0
+ check_pv_field_ $dev5 pv_mda_used_count 0
+ echo vgreduce of un-ignored pv w/mda should trigger un-ignore on an mda
+ vgreduce $vg $dev1 $dev2 $dev3
+ check_pv_field_ $dev5 pv_mda_used_count $mdacp
+ check_vg_field_ $vg vg_mda_copies $(($mdacp * 1))
+ pvunignore_ $dev1
+ pvunignore_ $dev2
+ echo setting vgmetadatacopies to unmanaged should allow vgextend to add w/out balancing
+ vgchange --vgmetadatacopies unmanaged $vg
+ vgextend $vg $dev1 $dev2
+ check_vg_field_ $vg vg_mda_copies unmanaged
+ check_vg_field_ $vg vg_mda_count $(($mdacp * 3))
+ check_vg_field_ $vg vg_mda_used_count $((mdacp * 3))
+ check_pv_field_ $dev1 pv_mda_used_count $mdacp
+ check_pv_field_ $dev2 pv_mda_used_count $mdacp
+ vgremove -f $vg
+done
+
+echo Test special situations, vgsplit, vgmerge, etc
+for mdacp in 1 2; do
+ pvcreate --metadatacopies $mdacp $dev1 $dev2 $dev3 $dev4 $dev5
+ vgcreate -c n --vgmetadatacopies 2 $vg1 $dev1 $dev2 $dev3
+ vgcreate -c n --vgmetadatacopies $(($mdacp * 1)) $vg2 $dev4 $dev5
+ echo vgsplit/vgmerge preserves value of metadata copies
+ check_vg_field_ $vg1 vg_mda_copies 2
+ check_vg_field_ $vg2 vg_mda_copies $(($mdacp * 1))
+ vgsplit $vg1 $vg2 $dev1
+ check_vg_field_ $vg2 vg_mda_copies $(($mdacp * 1))
+ vgmerge $vg1 $vg2
+ check_vg_field_ $vg1 vg_mda_copies 2
+ check_vg_field_ $vg1 vg_mda_count $(($mdacp * 5))
+ echo vgsplit into new vg sets proper value of vgmetadatacopies
+ vgsplit --vgmetadatacopies $(($mdacp * 2)) $vg1 $vg2 $dev1 $dev2
+ check_vg_field_ $vg2 vg_mda_copies $(($mdacp * 2))
+ echo vgchange fails if given both vgmetadatacopies and metadatacopies
+ not vgchange --vgmetadatacopies 5 --metadatacopies 7 $vg2
+ vgremove -f $vg1
+ vgremove -f $vg2
+done
+
+echo Test combination of --vgmetadatacopies and pvchange --metadataignore
+for mdacp in 1 2; do
+ pvcreate --metadatacopies $mdacp $dev1 $dev2 $dev3 $dev4 $dev5
+ vgcreate -c n --vgmetadatacopies $(($mdacp * 1)) $vg1 $dev1 $dev2
+ check_vg_field_ $vg1 vg_mda_copies $(($mdacp * 1))
+ check_vg_field_ $vg1 vg_mda_used_count $(($mdacp * 1))
+ pvignore_ $dev3
+ echo Ensure vgextend of PVs with ignored MDAs does not add to vg_mda_used_count
+ vgextend $vg1 $dev3
+ check_vg_field_ $vg1 vg_mda_used_count $(($mdacp * 1))
+ echo Using pvchange to unignore should update vg_mda_used_count
+ pvchange -f --metadataignore n $dev3
+ check_pv_field_ $dev3 pv_mda_used_count $mdacp
+ check_vg_field_ $vg1 vg_mda_used_count $(($mdacp * 2))
+ echo Set unmanaged on the vg should keep ignore bits the same during vgextend
+ vgchange --vgmetadatacopies unmanaged $vg1
+ check_vg_field_ $vg1 vg_mda_used_count $(($mdacp * 2))
+ pvunignore_ $dev4
+ vgextend $vg1 $dev4
+ check_pv_field_ $dev4 pv_mda_used_count $mdacp
+ check_vg_field_ $vg1 vg_mda_used_count $(($mdacp * 3))
+ echo Using pvchange to ignore should update vg_mda_used_count
+ pvchange -f --metadataignore y $dev4
+ check_pv_field_ $dev4 pv_mda_used_count 0
+ check_vg_field_ $vg1 vg_mda_used_count $(($mdacp * 2))
+ vgremove -f $vg1
+done
--- /dev/null
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_devs 5
+
+pvcreate $dev1
+pvcreate --metadatacopies 0 $dev2
+pvcreate --metadatacopies 0 $dev3
+pvcreate $dev4
+pvcreate --metadatacopies 0 $dev5
+
+vgcreate -c n "$vg" $devs
+lvcreate -n $lv -l 1 -i5 -I256 $vg
+
+pvchange -x n $dev1
+pvchange -x y $dev1
+vgchange -a n $vg
+pvchange --uuid $dev1
+pvchange --uuid $dev2
+vgremove -f $vg
+
+# check that PVs without metadata don't cause too many full device rescans (bz452606)
+for mdacp in 1 0; do
+ pvcreate --metadatacopies $mdacp $devs
+ pvcreate $dev1
+ vgcreate -c n $vg $devs
+ lvcreate -n $lv1 -l 2 -i5 -I256 $vg
+ lvcreate -n $lv2 -m2 -l 2 $vg
+ #lvchange -an $vg
+ lvchange -an $vg/$lv1
+ lvchange -an $vg/$lv2
+ vgchange -ay $vg
+ lvchange -vvvv -an $vg/$lv1 >out$mdacp 2>&1
+ lvchange -vvvv -an $vg/$lv2 >>out$mdacp 2>&1
+ vgremove -f $vg
+done
+not grep "Cached VG .* incorrect PV list" out0
+
+# some M1 metadata tests
+pvcreate -M1 $dev1
+pvcreate -M1 $dev2
+pvcreate -M1 $dev3
+pv3_uuid=$(pvs --noheadings -o pv_uuid $dev3)
+vgcreate -M1 -c n $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
+
+# 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
+
+# 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
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2007-2008 Red Hat, Inc. All rights reserved.
+# Copyright (C) 2007-2008 NEC Corporation
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+test_description="check namings of mirrored LV"
+
+. ./test-utils.sh
+
+# ---------------------------------------------------------------------
+# Utilities
+
+lv_devices_() {
+ local d
+ local lv=$1
+ shift
+ local devices=$*
+
+ local devs=$(lvs -a -odevices --noheadings $lv | sed 's/([0-9]*)//g' |
+ sed 's/ //g' | sed 's/,/ /g')
+
+ for d in $devs; do
+ (echo $devices | grep -q $d) || return 1
+ devices=$(echo $devices | sed "s/$d//")
+ done
+
+ [ "$(echo $devices | sed 's/ //g')" = "" ]
+}
+
+lv_mirror_log_() {
+ local lv=$1
+
+ echo $(lvs -a -omirror_log --noheadings $lv | sed 's/ //g')
+}
+
+lv_convert_lv_() {
+ local lv=$1
+
+ echo $(lvs -a -oconvert_lv --noheadings $lv | sed 's/ //g')
+}
+
+# ---------------------------------------------------------------------
+# Initialize PVs and VGs
+
+aux prepare_vg 5 80
+
+# ---------------------------------------------------------------------
+# Common environment setup/cleanup for each sub testcases
+
+prepare_lvs_() {
+ lvremove -ff $vg
+ if dmsetup table|grep $vg; then
+ echo "ERROR: lvremove did leave some some mappings in DM behind!"
+ return 1
+ fi
+ :
+}
+
+check_and_cleanup_lvs_() {
+ lvs -a -o+devices $vg
+ lvremove -ff $vg
+ if dmsetup table|grep $vg; then
+ echo "ERROR: lvremove did leave some some mappings in DM behind!"
+ return 1
+ fi
+}
+
+prepare_lvs_
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+# basic
+
+#COMM "init: lvcreate"
+prepare_lvs_
+
+#COMM "mirror images are ${lv1}_mimage_x"
+lvcreate -l2 -m1 -n $lv1 $vg
+lv_devices_ $vg/$lv1 "$lv1"_mimage_0 "$lv1"_mimage_1
+
+#COMM "mirror log is ${lv1}_mlog"
+lv_mirror_log_ $vg/$lv1 "$lv1"_mlog
+
+# "cleanup"
+check_and_cleanup_lvs_
+
+#COMM "mirror with name longer than 22 characters (bz221322)"
+name="LVwithanamelogerthan22characters_butidontwonttocounthem"
+lvcreate -m1 -l2 -n"$name" $vg
+lvs $vg/"$name"
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+# lvrename
+
+#COMM "init: lvrename"
+prepare_lvs_
+
+#COMM "renamed mirror names: $lv1 to $lv2"
+lvcreate -l2 -m1 -n $lv1 $vg
+lvrename $vg/$lv1 $vg/$lv2
+lv_devices_ $vg/$lv2 "$lv2"_mimage_0 "$lv2"_mimage_1
+lv_mirror_log_ $vg/$lv2 "$lv2"_mlog
+
+#COMM "cleanup"
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+# lvconvert
+
+#COMM "init: lvconvert"
+prepare_lvs_
+
+#COMM "converting mirror names is ${lv1}_mimagetmp_2"
+lvcreate -l2 -m1 -n $lv1 $vg
+lvconvert -m+1 -i+10 -b $vg/$lv1
+convlv=$(lv_convert_lv_ "$vg/$lv1")
+test "$convlv" = "$lv1"_mimagetmp_2
+lv_devices_ $vg/$lv1 "$convlv" "$lv1"_mimage_2
+lv_devices_ "$vg/$convlv" "$lv1"_mimage_0 "$lv1"_mimage_1
+loglv=$(lv_mirror_log_ "$vg/$convlv")
+test "$loglv" = "$lv1"_mlog
+
+#COMM "mirror log name after re-adding is ${lv1}_mlog" \
+lvconvert --mirrorlog core $vg/$lv1
+lvconvert --mirrorlog disk $vg/$lv1
+convlv=$(lv_convert_lv_ "$vg/$lv1")
+lv_devices_ $vg/$lv1 "$convlv" "$lv1"_mimage_2
+lv_devices_ "$vg/$convlv" "$lv1"_mimage_0 "$lv1"_mimage_1
+loglv=$(lv_mirror_log_ "$vg/$convlv")
+test "$loglv" = "$lv1"_mlog
+
+#COMM "renamed converting mirror names: $lv1 to $lv2" \
+lvrename $vg/$lv1 $vg/$lv2
+convlv=$(lv_convert_lv_ "$vg/$lv2")
+lv_devices_ $vg/$lv2 "$convlv" "$lv2"_mimage_2
+lv_devices_ "$vg/$convlv" "$lv2"_mimage_0 "$lv2"_mimage_1
+loglv=$(lv_mirror_log_ "$vg/$convlv")
+test "$loglv" = "$lv2"_mlog
+
+#COMM "cleanup"
+check_and_cleanup_lvs_
+
+# Temporary mirror log should have "_mlogtmp_<n>" suffix
+# but currently lvconvert doesn't have an option to add the log.
+# If such feature is added in future, a test for that should
+# be added.
+
+# ---------------------------------------------------------------------
--- /dev/null
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+# Copyright (C) 2007 NEC Corporation
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+test_description="ensure that 'vgreduce --removemissing' works on mirrored LV"
+
+. ./test-utils.sh
+
+lv_is_on_ ()
+{
+ local lv=$vg/$1
+ shift
+ local pvs=$*
+
+ echo "Check if $lv is exactly on PVs $pvs"
+ rm -f out1 out2
+ echo $pvs | sed 's/ /\n/g' | sort | uniq > out1
+
+ lvs -a -o+devices $lv
+ lvs -a -odevices --noheadings $lv | \
+ sed 's/([^)]*)//g; s/[ ,]/\n/g' | sort | uniq > out2
+
+ diff --ignore-blank-lines out1 out2
+}
+
+mimages_are_on_ ()
+{
+ local lv=$1
+ shift
+ local pvs="$*"
+ local mimages
+ local i
+
+ echo "Check if mirror images of $lv are on PVs $pvs"
+ rm -f out1 out2
+ echo $pvs | sed 's/ /\n/g' | sort | uniq > out1
+
+ mimages=$(lvs --noheadings -a -o lv_name $vg | grep "${lv}_mimage_" | \
+ sed 's/\[//g; s/\]//g')
+ for i in $mimages; do
+ echo "Checking $vg/$i"
+ lvs -a -o+devices $vg/$i
+ lvs -a -odevices --noheadings $vg/$i | \
+ sed 's/([^)]*)//g; s/ //g; s/,/ /g' | sort | uniq >> out2
+ done
+
+ diff --ignore-blank-lines out1 out2
+}
+
+mirrorlog_is_on_()
+{
+ local lv="$1"_mlog
+ shift
+ lv_is_on_ $lv $*
+}
+
+lv_is_linear_()
+{
+ echo "Check if $1 is linear LV (i.e. not a mirror)"
+ lvs -o stripes,attr --noheadings $vg/$1 | sed 's/ //g'
+ lvs -o stripes,attr --noheadings $vg/$1 | sed 's/ //g' | grep -q '^1-'
+}
+
+rest_pvs_()
+{
+ local index=$1
+ local num=$2
+ local rem=""
+ local n
+
+ for n in $(seq 1 $(($index - 1))) $(seq $(($index + 1)) $num); do
+ eval local dev=$\dev$n
+ rem="$rem $dev"
+ done
+
+ echo "$rem"
+}
+
+# ---------------------------------------------------------------------
+# Initialize PVs and VGs
+
+prepare_vg 5
+
+# ---------------------------------------------------------------------
+# Common environment setup/cleanup for each sub testcases
+
+prepare_lvs_()
+{
+ lvremove -ff $vg;
+ if dmsetup table|grep $vg; then
+ echo "ERROR: lvremove did leave some some mappings in DM behind!"
+ return 1
+ fi
+ :
+}
+
+check_and_cleanup_lvs_()
+{
+ lvs -a -o+devices $vg
+ lvremove -ff $vg
+ if dmsetup table|grep $vg; then
+ echo "ERROR: lvremove did leave some some mappings in DM behind!"
+ return 1
+ fi
+}
+
+recover_vg_()
+{
+ enable_dev $*
+ pvcreate -ff $*
+ vgextend $vg $*
+ check_and_cleanup_lvs_
+}
+
+#COMM "check environment setup/cleanup"
+prepare_lvs_
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+# one of mirror images has failed
+
+#COMM "basic: fail the 2nd mirror image of 2-way mirrored LV"
+prepare_lvs_
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev3:0
+lvchange -an $vg/$lv1
+aux mimages_are_on_ $lv1 $dev1 $dev2
+mirrorlog_is_on_ $lv1 $dev3
+disable_dev $dev2
+vgreduce --removemissing --force $vg
+lv_is_linear_ $lv1
+lv_is_on_ $lv1 $dev1
+
+# "cleanup"
+recover_vg_ $dev2
+
+# ---------------------------------------------------------------------
+# LV has 3 images in flat,
+# 1 out of 3 images fails
+
+#COMM test_3way_mirror_fail_1_ <PV# to fail>
+test_3way_mirror_fail_1_()
+{
+ local index=$1
+
+ lvcreate -l2 -m2 -n $lv1 $vg $dev1 $dev2 $dev3 $dev4:0
+ lvchange -an $vg/$lv1
+ aux mimages_are_on_ $lv1 $dev1 $dev2 $dev3
+ mirrorlog_is_on_ $lv1 $dev4
+ eval disable_dev \$dev$index
+ vgreduce --removemissing --force $vg
+ lvs -a -o+devices $vg
+ mimages_are_on_ $lv1 $(rest_pvs_ $index 3)
+ mirrorlog_is_on_ $lv1 $dev4
+}
+
+for n in $(seq 1 3); do
+ #COMM fail mirror image $(($n - 1)) of 3-way mirrored LV"
+ prepare_lvs_
+ test_3way_mirror_fail_1_ $n
+ eval recover_vg_ \$dev$n
+done
+
+# ---------------------------------------------------------------------
+# LV has 3 images in flat,
+# 2 out of 3 images fail
+
+#COMM test_3way_mirror_fail_2_ <PV# NOT to fail>
+test_3way_mirror_fail_2_()
+{
+ local index=$1
+
+ lvcreate -l2 -m2 -n $lv1 $vg $dev1 $dev2 $dev3 $dev4:0
+ lvchange -an $vg/$lv1
+ mimages_are_on_ $lv1 $dev1 $dev2 $dev3
+ mirrorlog_is_on_ $lv1 $dev4
+ rest_pvs_ $index 3
+ disable_dev $(rest_pvs_ $index 3)
+ vgreduce --force --removemissing $vg
+ lvs -a -o+devices $vg
+ aux lv_is_linear_ $lv1
+ eval lv_is_on_ $lv1 \$dev$n
+}
+
+for n in $(seq 1 3); do
+ #COMM fail mirror images other than mirror image $(($n - 1)) of 3-way mirrored LV
+ prepare_lvs_
+ test_3way_mirror_fail_2_ $n
+ recover_vg_ $(rest_pvs_ $n 3)
+done
+
+# ---------------------------------------------------------------------
+# LV has 4 images, 1 of them is in the temporary mirror for syncing.
+# 1 out of 4 images fails
+
+#COMM test_3way_mirror_plus_1_fail_1_ <PV# to fail>
+test_3way_mirror_plus_1_fail_1_()
+{
+ local index=$1
+
+ lvcreate -l2 -m2 -n $lv1 $vg $dev1 $dev2 $dev3 $dev5:0
+ lvchange -an $vg/$lv1
+ lvconvert -m+1 $vg/$lv1 $dev4
+ mimages_are_on_ $lv1 $dev1 $dev2 $dev3 $dev4
+ mirrorlog_is_on_ $lv1 $dev5
+ eval disable_dev \$dev$n
+ vgreduce --removemissing --force $vg
+ lvs -a -o+devices $vg
+ mimages_are_on_ $lv1 $(rest_pvs_ $index 4)
+ mirrorlog_is_on_ $lv1 $dev5
+}
+
+for n in $(seq 1 4); do
+ #COMM "fail mirror image $(($n - 1)) of 4-way (1 converting) mirrored LV"
+ prepare_lvs_
+ test_3way_mirror_plus_1_fail_1_ $n
+ eval recover_vg_ \$dev$n
+done
+
+# ---------------------------------------------------------------------
+# LV has 4 images, 1 of them is in the temporary mirror for syncing.
+# 3 out of 4 images fail
+
+#COMM test_3way_mirror_plus_1_fail_3_ <PV# NOT to fail>
+test_3way_mirror_plus_1_fail_3_()
+{
+ local index=$1
+
+ lvcreate -l2 -m2 -n $lv1 $vg $dev1 $dev2 $dev3 $dev5:0
+ lvchange -an $vg/$lv1
+ lvconvert -m+1 $vg/$lv1 $dev4
+ mimages_are_on_ $lv1 $dev1 $dev2 $dev3 $dev4
+ mirrorlog_is_on_ $lv1 $dev5
+ disable_dev $(rest_pvs_ $index 4)
+ vgreduce --removemissing --force $vg
+ lvs -a -o+devices $vg
+ eval local dev=\$dev$n
+ mimages_are_on_ $lv1 $dev || lv_is_on_ $lv1 $dev
+ not mirrorlog_is_on_ $lv1 $dev5
+}
+
+for n in $(seq 1 4); do
+ #COMM "fail mirror images other than mirror image $(($n - 1)) of 4-way (1 converting) mirrored LV"
+ prepare_lvs_
+ test_3way_mirror_plus_1_fail_3_ $n
+ recover_vg_ $(rest_pvs_ $n 4)
+done
+
+# ---------------------------------------------------------------------
+# LV has 4 images, 2 of them are in the temporary mirror for syncing.
+# 1 out of 4 images fail
+
+# test_2way_mirror_plus_2_fail_1_ <PV# to fail>
+test_2way_mirror_plus_2_fail_1_()
+{
+ local index=$1
+
+ lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+ lvchange -an $vg/$lv1
+ lvconvert -m+2 $vg/$lv1 $dev3 $dev4
+ mimages_are_on_ $lv1 $dev1 $dev2 $dev3 $dev4
+ mirrorlog_is_on_ $lv1 $dev5
+ eval disable_dev \$dev$n
+ vgreduce --removemissing --force $vg
+ lvs -a -o+devices $vg
+ mimages_are_on_ $lv1 $(rest_pvs_ $index 4)
+ mirrorlog_is_on_ $lv1 $dev5
+}
+
+for n in $(seq 1 4); do
+ #COMM "fail mirror image $(($n - 1)) of 4-way (2 converting) mirrored LV"
+ prepare_lvs_
+ test_2way_mirror_plus_2_fail_1_ $n
+ eval recover_vg_ \$dev$n
+done
+
+# ---------------------------------------------------------------------
+# LV has 4 images, 2 of them are in the temporary mirror for syncing.
+# 3 out of 4 images fail
+
+# test_2way_mirror_plus_2_fail_3_ <PV# NOT to fail>
+test_2way_mirror_plus_2_fail_3_()
+{
+ local index=$1
+
+ lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+ lvchange -an $vg/$lv1
+ lvconvert -m+2 $vg/$lv1 $dev3 $dev4
+ mimages_are_on_ $lv1 $dev1 $dev2 $dev3 $dev4
+ mirrorlog_is_on_ $lv1 $dev5
+ disable_dev $(rest_pvs_ $index 4)
+ vgreduce --removemissing --force $vg
+ lvs -a -o+devices $vg
+ eval local dev=\$dev$n
+ mimages_are_on_ $lv1 $dev || lv_is_on_ $lv1 $dev
+ not mirrorlog_is_on_ $lv1 $dev5
+}
+
+for n in $(seq 1 4); do
+ #COMM "fail mirror images other than mirror image $(($n - 1)) of 4-way (2 converting) mirrored LV"
+ prepare_lvs_
+ test_2way_mirror_plus_2_fail_3_ $n
+ recover_vg_ $(rest_pvs_ $n 4)
+done
+
+# ---------------------------------------------------------------------
+# log device is gone (flat mirror and stacked mirror)
+
+#COMM "fail mirror log of 2-way mirrored LV"
+prepare_lvs_
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1
+mimages_are_on_ $lv1 $dev1 $dev2
+mirrorlog_is_on_ $lv1 $dev5
+disable_dev $dev5
+vgreduce --removemissing --force $vg
+mimages_are_on_ $lv1 $dev1 $dev2
+not mirrorlog_is_on_ $lv1 $dev5
+recover_vg_ $dev5
+
+#COMM "fail mirror log of 3-way (1 converting) mirrored LV"
+prepare_lvs_
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1
+lvconvert -m+1 $vg/$lv1 $dev3
+mimages_are_on_ $lv1 $dev1 $dev2 $dev3
+mirrorlog_is_on_ $lv1 $dev5
+disable_dev $dev5
+vgreduce --removemissing --force $vg
+mimages_are_on_ $lv1 $dev1 $dev2 $dev3
+not mirrorlog_is_on_ $lv1 $dev5
+recover_vg_ $dev5
+
+# ---------------------------------------------------------------------
+# all images are gone (flat mirror and stacked mirror)
+
+#COMM "fail all mirror images of 2-way mirrored LV"
+prepare_lvs_
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1
+mimages_are_on_ $lv1 $dev1 $dev2
+mirrorlog_is_on_ $lv1 $dev5
+disable_dev $dev1 $dev2
+vgreduce --removemissing --force $vg
+not lvs $vg/$lv1
+recover_vg_ $dev1 $dev2
+
+#COMM "fail all mirror images of 3-way (1 converting) mirrored LV"
+prepare_lvs_
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1
+lvconvert -m+1 $vg/$lv1 $dev3
+mimages_are_on_ $lv1 $dev1 $dev2 $dev3
+mirrorlog_is_on_ $lv1 $dev5
+disable_dev $dev1 $dev2 $dev3
+vgreduce --removemissing --force $vg
+not lvs $vg/$lv1
+recover_vg_ $dev1 $dev2 $dev3
+
+# ---------------------------------------------------------------------
+# Multiple LVs
+
+#COMM "fail a mirror image of one of mirrored LV"
+prepare_lvs_
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1
+lvcreate -l2 -m1 -n $lv2 $vg $dev3 $dev4 $dev5:1
+lvchange -an $vg/$lv2
+mimages_are_on_ $lv1 $dev1 $dev2
+mimages_are_on_ $lv2 $dev3 $dev4
+mirrorlog_is_on_ $lv1 $dev5
+mirrorlog_is_on_ $lv2 $dev5
+disable_dev $dev2
+vgreduce --removemissing --force $vg
+mimages_are_on_ $lv2 $dev3 $dev4
+mirrorlog_is_on_ $lv2 $dev5
+lv_is_linear_ $lv1
+lv_is_on_ $lv1 $dev1
+recover_vg_ $dev2
+
+#COMM "fail mirror images, one for each mirrored LV"
+prepare_lvs_
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1
+lvcreate -l2 -m1 -n $lv2 $vg $dev3 $dev4 $dev5:1
+lvchange -an $vg/$lv2
+mimages_are_on_ $lv1 $dev1 $dev2
+mimages_are_on_ $lv2 $dev3 $dev4
+mirrorlog_is_on_ $lv1 $dev5
+mirrorlog_is_on_ $lv2 $dev5
+disable_dev $dev2
+disable_dev $dev4
+vgreduce --removemissing --force $vg
+lv_is_linear_ $lv1
+lv_is_on_ $lv1 $dev1
+lv_is_linear_ $lv2
+lv_is_on_ $lv2 $dev3
+recover_vg_ $dev2 $dev4
+
+# ---------------------------------------------------------------------
+# no failure
+
+#COMM "no failures"
+prepare_lvs_
+lvcreate -l2 -m1 -n $lv1 $vg $dev1 $dev2 $dev5:0
+lvchange -an $vg/$lv1
+mimages_are_on_ $lv1 $dev1 $dev2
+mirrorlog_is_on_ $lv1 $dev5
+vgreduce --removemissing --force $vg
+mimages_are_on_ $lv1 $dev1 $dev2
+mirrorlog_is_on_ $lv1 $dev5
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+
--- /dev/null
+#!/bin/bash
+
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+prepare_devs 4
+pvcreate $dev1 $dev2
+pvcreate --metadatacopies 0 $dev3 $dev4
+vgcreate -c n $vg $dev1 $dev2 $dev3 $dev4
+
+lvcreate -l1 -n linear1 $vg $dev1
+lvcreate -l1 -n linear2 $vg $dev2
+lvcreate -l2 -n linear12 $vg $dev1:4 $dev2:4
+
+lvcreate -l1 -n origin1 $vg $dev1
+lvcreate -s $vg/origin1 -l1 -n s_napshot2 $dev2
+
+lvcreate -l1 -m1 -n mirror12 --mirrorlog core $vg $dev1 $dev2
+lvcreate -l1 -m1 -n mirror123 $vg $dev1 $dev2 $dev3
+
+vgchange -a n $vg
+disable_dev $dev1
+not vgchange -a y $vg
+not vgck $vg
+
+check inactive $vg linear1
+check active $vg linear2
+check inactive $vg origin1
+check inactive $vg s_napshot2
+check inactive $vg linear12
+check inactive $vg mirror12
+check inactive $vg mirror123
+
+vgchange -a n $vg
+enable_dev $dev1
+disable_dev $dev2
+not vgchange -a y $vg
+not vgck $vg
+
+check active $vg linear1
+check inactive $vg linear2
+check inactive $vg linear12
+check inactive $vg origin1
+check inactive $vg s_napshot2
+check inactive $vg mirror12
+check inactive $vg mirror123
+
+vgchange -a n $vg
+enable_dev $dev2
+disable_dev $dev3
+not vgchange -a y $vg
+not vgck $vg
+
+check active $vg origin1
+check active $vg s_napshot2
+check active $vg linear1
+check active $vg linear2
+check active $vg linear12
+check inactive $vg mirror123
+check active $vg mirror12
+
+vgchange -a n $vg
+enable_dev $dev3
+disable_dev $dev4
+vgchange -a y $vg
+not vgck $vg
+
+check active $vg origin1
+check active $vg s_napshot2
+check active $vg linear1
+check active $vg linear2
+check active $vg linear12
+check active $vg mirror12
+check active $vg mirror123
--- /dev/null
+# Copyright (C) 2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+# create the old GFS pool labeled linear devices
+create_pool_label_()
+{
+ # FIXME
+ # echo -e is bashism, dash builtin sh doesn't do \xNN in printf either
+ # printf comes from coreutils, and is probably not posix either
+ env printf "\x01\x16\x70\x06\x5f\xcf\xff\xb9\xf8\x24\x8apool1" | dd of=$2 bs=5 seek=1 conv=notrunc
+ env printf "\x04\x01\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x0$1\x68\x01\x16\x70\x00\x00\x00\x00\x00\x06\x5f\xd0" | dd of=$2 bs=273 seek=1 conv=notrunc
+}
+
+env printf "" || exit 200 # skip if printf is not available
+
+aux prepare_devs 2
+
+create_pool_label_ 0 "$dev1"
+create_pool_label_ 1 "$dev2"
+
+# check that pvcreate fails without -ff on the pool device
+not pvcreate "$dev1"
+
+# check that vgdisplay and pvcreate -ff works with the pool device
+vgdisplay --config 'global { locking_type = 0 }'
+disable_dev "$dev2"
+# FIXME! since pool1 cannot be opened, vgdisplay gives error... should we say
+# "not" there instead, checking that it indeed does fail?
+vgdisplay --config 'global { locking_type = 0 }' || true
+pvcreate -ff -y "$dev1"
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# 'Ensure that pvmove diagnoses PE-range values 2^32 and larger.'
+
+. ./test-utils.sh
+
+aux prepare_vg 2
+
+lvcreate -L4 -n"$lv" $vg
+
+# Test for the bogus diagnostic reported in BZ 284771
+# http://bugzilla.redhat.com/284771.
+# 'run pvmove with an unrecognized LV name to show bad diagnostic'
+not pvmove -v -nbogus $dev1 $dev2 2> err
+grep " Logical volume bogus not found." err
+
+# With lvm-2.02.28 and earlier, on a system with 64-bit "long int",
+# the PE range parsing code would accept values up to 2^64-1, but would
+# silently truncate them to int32_t. I.e., $dev1:$(echo 2^32|bc) would be
+# treated just like $dev1:0.
+# 'run the offending pvmove command'
+not pvmove -v -n$lv $dev1:4294967296 $dev2
+
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# 'Test pvchange option values'
+
+. ./test-utils.sh
+
+aux prepare_devs 4
+
+for mda in 0 1 2
+do
+# "setup pv with metadatacopies = $mda"
+ pvcreate $dev4
+ pvcreate --metadatacopies $mda $dev1
+ vgcreate $vg1 $dev1 $dev4
+
+# "pvchange adds/dels tag to pvs with metadatacopies = $mda "
+ pvchange $dev1 --addtag test$mda
+ check_pv_field_ $dev1 pv_tags test$mda
+ pvchange $dev1 --deltag test$mda
+ check_pv_field_ $dev1 pv_tags ""
+
+# "vgchange disable/enable allocation for pvs with metadatacopies = $mda (bz452982)"
+ pvchange $dev1 -x n
+ check_pv_field_ $dev1 pv_attr --
+ pvchange $dev1 -x y
+ check_pv_field_ $dev1 pv_attr a-
+
+# 'remove pv'
+ vgremove $vg1
+ pvremove $dev1 $dev4
+done
+
+# "pvchange uuid"
+pvcreate --metadatacopies 0 $dev1
+pvcreate --metadatacopies 2 $dev2
+vgcreate $vg1 $dev1 $dev2
+pvchange -u $dev1
+pvchange -u $dev2
+vg_validate_pvlv_counts_ $vg1 2 0 0
+pvchange -u --all
+vg_validate_pvlv_counts_ $vg1 2 0 0
+
+# "pvchange rejects uuid change under an active lv"
+lvcreate -l 16 -i 2 -n $lv --alloc anywhere $vg1
+vg_validate_pvlv_counts_ $vg1 2 1 0
+not pvchange -u $dev1
+lvchange -an "$vg1"/"$lv"
+pvchange -u $dev1
+
+# "cleanup"
+lvremove -f "$vg1"/"$lv"
+vgremove $vg1
+
+# "pvchange reject --addtag to lvm1 pv"
+pvcreate -M1 $dev1
+not pvchange $dev1 --addtag test
+
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#
+# Testcase for bugzilla #450651
+# also checks that vgremove properly removes all lv devices in the right order
+#
+# 'Test pvcreate without metadata on all pvs'
+
+. ./test-utils.sh
+
+aux prepare_devs 2 128
+
+#lv_snap=$lv2
+pvcreate "$dev1"
+pvcreate --metadatacopies 0 "$dev2"
+
+# "check lv snapshot"
+vgcreate -c n "$vg" "$dev1" "$dev2"
+lvcreate -n "$lv" -l 60%FREE "$vg"
+lvcreate -s -n $lv2 -l 10%FREE "$vg"/"$lv"
+pvdisplay
+lvdisplay
+vgremove -f "$vg"
--- /dev/null
+# Copyright (C) 2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# skip this test if mdadm or sfdisk (or others) aren't available
+which mdadm || exit 200
+which sfdisk || exit 200
+which perl || exit 200
+which awk || exit 200
+which cut || exit 200
+
+test -f /proc/mdstat && grep -q raid0 /proc/mdstat || \
+modprobe raid0 || exit 200
+
+. ./test-utils.sh
+
+prepare_lvmconf '[ "a|/dev/md.*|", "a/dev\/mapper\/.*$/", "r/.*/" ]'
+aux prepare_devs 2
+
+# Have MD use a non-standard name to avoid colliding with an existing MD device
+# - mdadm >= 3.0 requires that non-standard device names be in /dev/md/
+# - newer mdadm _completely_ defers to udev to create the associated device node
+mdadm_maj=$(mdadm --version 2>&1 | perl -pi -e 's|.* v(\d+).*|\1|')
+[ $mdadm_maj -ge 3 ] && \
+ mddev=/dev/md/md_lvm_test0 || \
+ mddev=/dev/md_lvm_test0
+
+cleanup_md() {
+ # sleeps offer hack to defeat: 'md: md127 still in use'
+ # see: https://bugzilla.redhat.com/show_bug.cgi?id=509908#c25
+ sleep 2
+ mdadm --stop $mddev || true
+ if [ -b "$mddev" ]; then
+ # mdadm doesn't always cleanup the device node
+ sleep 2
+ rm -f $mddev
+ fi
+}
+
+cleanup_md_and_teardown() {
+ cleanup_md
+ teardown
+}
+
+# create 2 disk MD raid0 array (stripe_width=128K)
+test -b "$mddev" && exit 200
+mdadm --create --metadata=1.0 $mddev --auto=md --level 0 --raid-devices=2 --chunk 64 $dev1 $dev2
+trap 'aux cleanup_md_and_teardown' EXIT # cleanup this MD device at the end of the test
+test -b "$mddev" || exit 200
+
+# Test alignment of PV on MD without any MD-aware or topology-aware detection
+# - should treat $mddev just like any other block device
+pv_align="1.00m"
+pvcreate --metadatasize 128k \
+ --config 'devices {md_chunk_alignment=0 data_alignment_detection=0 data_alignment_offset_detection=0}' \
+ $mddev
+check_pv_field_ $mddev pe_start $pv_align
+
+# Test md_chunk_alignment independent of topology-aware detection
+pv_align="1.00m"
+pvcreate --metadatasize 128k \
+ --config 'devices {data_alignment_detection=0 data_alignment_offset_detection=0}' \
+ $mddev
+check_pv_field_ $mddev pe_start $pv_align
+
+# Get linux minor version
+linux_minor=$(echo `uname -r` | cut -d'.' -f3 | cut -d'-' -f1)
+
+# Test newer topology-aware alignment detection
+# - first added to 2.6.31 but not "reliable" until 2.6.33
+if [ $linux_minor -ge 33 ]; then
+ pv_align="1.00m"
+ # optimal_io_size=131072, minimum_io_size=65536
+ pvcreate --metadatasize 128k \
+ --config 'devices { md_chunk_alignment=0 }' $mddev
+ check_pv_field_ $mddev pe_start $pv_align
+fi
+
+# partition MD array directly, depends on blkext in Linux >= 2.6.28
+if [ $linux_minor -ge 28 ]; then
+ # create one partition
+ sfdisk $mddev <<EOF
+,,83
+EOF
+ # make sure partition on MD is _not_ removed
+ # - tests partition -> parent lookup via sysfs paths
+ not pvcreate --metadatasize 128k $mddev
+
+ # verify alignment_offset is accounted for in pe_start
+ # - topology infrastructure is available in Linux >= 2.6.31
+ # - also tests partition -> parent lookup via sysfs paths
+
+ # Oh joy: need to lookup /sys/block/md127 rather than /sys/block/md_lvm_test0
+ mddev_maj_min=$(ls -lL $mddev | awk '{ print $5 $6 }' | perl -pi -e 's|,|:|')
+ mddev_p_sysfs_name=$(echo /sys/dev/block/${mddev_maj_min}/*p1)
+ base_mddev_p=`basename $mddev_p_sysfs_name`
+ mddev_p=/dev/${base_mddev_p}
+
+ # Checking for 'alignment_offset' in sysfs implies Linux >= 2.6.31
+ # but reliable alignment_offset support requires kernel.org Linux >= 2.6.33
+ sysfs_alignment_offset=/sys/dev/block/${mddev_maj_min}/${base_mddev_p}/alignment_offset
+ [ -f $sysfs_alignment_offset -a $linux_minor -ge 33 ] && \
+ alignment_offset=`cat $sysfs_alignment_offset` || \
+ alignment_offset=0
+
+ if [ $alignment_offset -gt 0 ]; then
+ # default alignment is 1M, add alignment_offset
+ pv_align=$((1048576+$alignment_offset))B
+ pvcreate --metadatasize 128k $mddev_p
+ check_pv_field_ $mddev_p pe_start $pv_align "--units b"
+ pvremove $mddev_p
+ fi
+fi
+
+# Test newer topology-aware alignment detection w/ --dataalignment override
+if [ $linux_minor -ge 33 ]; then
+ cleanup_md
+ pvcreate -f $dev1
+ pvcreate -f $dev2
+
+ # create 2 disk MD raid0 array (stripe_width=2M)
+ test -b "$mddev" && exit 200
+ mdadm --create --metadata=1.0 $mddev --auto=md --level 0 --raid-devices=2 --chunk 1024 $dev1 $dev2
+ test -b "$mddev" || exit 200
+
+ # optimal_io_size=2097152, minimum_io_size=1048576
+ pv_align="2.00m"
+ pvcreate --metadatasize 128k \
+ --config 'devices { md_chunk_alignment=0 }' $mddev
+ check_pv_field_ $mddev pe_start $pv_align
+
+ # now verify pe_start alignment override using --dataalignment
+ pv_align="192.00k"
+ pvcreate --dataalignment 64k --metadatasize 128k \
+ --config 'devices { md_chunk_alignment=0 }' $mddev
+ check_pv_field_ $mddev pe_start $pv_align
+fi
--- /dev/null
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_devs 4
+
+for mdatype in 1 2
+do
+# pvcreate (lvm$mdatype) refuses to overwrite an mounted filesystem (bz168330)
+ test ! -d $TESTDIR/mnt && mkdir $TESTDIR/mnt
+ if mke2fs $dev1; then
+ mount $dev1 $TESTDIR/mnt
+ not pvcreate -M$mdatype $dev1 2>err
+ grep "Can't open $dev1 exclusively. Mounted filesystem?" err
+ umount $dev1
+ fi
+
+# pvcreate (lvm$mdatype) succeeds when run repeatedly (pv not in a vg) (bz178216)
+ pvcreate -M$mdatype $dev1
+ pvcreate -M$mdatype $dev1
+ pvremove -f $dev1
+
+# pvcreate (lvm$mdatype) fails when PV belongs to VG" \
+ pvcreate -M$mdatype $dev1
+ vgcreate -M$mdatype $vg1 $dev1
+ not pvcreate -M$mdatype $dev1
+
+ vgremove -f $vg1
+ pvremove -f $dev1
+
+# pvcreate (lvm$mdatype) fails when PV1 does and PV2 does not belong to VG
+ pvcreate -M$mdatype $dev1
+ pvcreate -M$mdatype $dev2
+ vgcreate -M$mdatype $vg1 $dev1
+
+# pvcreate a second time on $dev2 and $dev1
+ not pvcreate -M$mdatype $dev2 $dev1
+
+ vgremove -f $vg1
+ pvremove -f $dev2
+ pvremove -f $dev1
+
+# NOTE: Force pvcreate after test completion to ensure clean device
+#test_expect_success \
+# "pvcreate (lvm$mdatype) fails on md component device" \
+# 'mdadm -C -l raid0 -n 2 /dev/md0 $dev1 $dev2 &&
+# pvcreate -M$mdatype $dev1;
+# status=$?; echo status=$status; test $status != 0 &&
+# mdadm --stop /dev/md0 &&
+# pvcreate -ff -y -M$mdatype $dev1 $dev2 &&
+# pvremove -f $dev1 $dev2'
+done
+
+# pvcreate (lvm2) fails without -ff when PV with metadatacopies=0 belongs to VG
+pvcreate --metadatacopies 0 $dev1
+pvcreate --metadatacopies 1 $dev2
+vgcreate $vg1 $dev1 $dev2
+not pvcreate $dev1
+vgremove -f $vg1
+pvremove -f $dev2
+pvremove -f $dev1
+
+# pvcreate (lvm2) succeeds with -ff when PV with metadatacopies=0 belongs to VG
+pvcreate --metadatacopies 0 $dev1
+pvcreate --metadatacopies 1 $dev2
+vgcreate $vg1 $dev1 $dev2
+pvcreate -ff -y $dev1
+vgreduce --removemissing $vg1
+vgremove -ff $vg1
+pvremove -f $dev2
+pvremove -f $dev1
+
+for i in 0 1 2 3
+do
+# pvcreate (lvm2) succeeds writing LVM label at sector $i
+ pvcreate --labelsector $i $dev1
+ dd if=$dev1 bs=512 skip=$i count=1 2>/dev/null | strings | grep -q LABELONE;
+ pvremove -f $dev1
+done
+
+# pvcreate (lvm2) fails writing LVM label at sector 4
+not pvcreate --labelsector 4 $dev1
+
+backupfile=$PREFIX.mybackupfile
+uuid1=freddy-fred-fred-fred-fred-fred-freddy
+uuid2=freddy-fred-fred-fred-fred-fred-fredie
+bogusuuid=fred
+
+# pvcreate rejects uuid option with less than 32 characters
+not pvcreate --norestorefile --uuid $bogusuuid $dev1
+
+# pvcreate rejects uuid option without restorefile
+not pvcreate --uuid $uuid1 $dev1
+
+# pvcreate rejects uuid already in use
+pvcreate --norestorefile --uuid $uuid1 $dev1
+not pvcreate --norestorefile --uuid $uuid1 $dev2
+
+# pvcreate rejects non-existent file given with restorefile
+not pvcreate --uuid $uuid1 --restorefile $backupfile $dev1
+
+# pvcreate rejects restorefile with uuid not found in file
+pvcreate --norestorefile --uuid $uuid1 $dev1
+vgcfgbackup -f $backupfile
+not pvcreate --uuid $uuid2 --restorefile $backupfile $dev2
+
+# pvcreate wipes swap signature when forced
+dd if=/dev/zero of=$dev1 bs=1024 count=64
+mkswap $dev1
+blkid -c /dev/null $dev1 | grep "swap"
+pvcreate -f $dev1
+# blkid cannot make up its mind whether not finding anything it knows is a failure or not
+(blkid -c /dev/null $dev1 || true) | not grep "swap"
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+test_description='Test pvcreate option values'
+PAGESIZE=$(getconf PAGESIZE)
+
+. ./test-utils.sh
+
+aux prepare_devs 4
+
+#COMM 'pvcreate rejects negative setphysicalvolumesize'
+not pvcreate --setphysicalvolumesize -1024 $dev1
+
+#COMM 'pvcreate rejects negative metadatasize'
+not pvcreate --metadatasize -1024 $dev1
+
+# x. metadatasize 0, defaults to 255
+# FIXME: unable to check default value, not in reporting cmds
+# should default to 255 according to code
+# check_pv_field_ pv_mda_size 255
+#COMM 'pvcreate accepts metadatasize 0'
+pvcreate --metadatasize 0 $dev1
+pvremove $dev1
+
+#Verify vg_mda_size is smaller pv_mda_size
+pvcreate --metadatasize 512k $dev1
+pvcreate --metadatasize 96k $dev2
+vgcreate $vg $dev1 $dev2
+compare_two_fields_ vgs $vg vg_mda_size pvs $dev2 pv_mda_size
+vgremove -ff $vg
+
+# x. metadatasize too large
+# For some reason we allow this, even though there's no room for data?
+##COMM 'pvcreate rejects metadatasize too large'
+#not pvcreate --metadatasize 100000000000000 $dev1
+
+#COMM 'pvcreate rejects metadatacopies < 0'
+not pvcreate --metadatacopies -1 $dev1
+
+#COMM 'pvcreate accepts metadatacopies = 0, 1, 2'
+for j in metadatacopies pvmetadatacopies
+do
+pvcreate --$j 0 $dev1
+pvcreate --$j 1 $dev2
+pvcreate --$j 2 $dev3
+check_pv_field_ $dev1 pv_mda_count 0
+check_pv_field_ $dev2 pv_mda_count 1
+check_pv_field_ $dev3 pv_mda_count 2
+pvremove $dev1
+pvremove $dev2
+pvremove $dev3
+done
+
+#COMM 'pvcreate rejects metadatacopies > 2'
+not pvcreate --metadatacopies 3 $dev1
+
+#COMM 'pvcreate rejects invalid device'
+not pvcreate $dev1bogus
+
+#COMM 'pvcreate rejects labelsector < 0'
+not pvcreate --labelsector -1 $dev1
+
+#COMM 'pvcreate rejects labelsector > 1000000000000'
+not pvcreate --labelsector 1000000000000 $dev1
+
+# other possibilites based on code inspection (not sure how hard)
+# x. device too small (min of 512 * 1024 KB)
+# x. device filtered out
+# x. unable to open /dev/urandom RDONLY
+# x. device too large (pe_count > UINT32_MAX)
+# x. device read-only
+# x. unable to open device readonly
+# x. BLKGETSIZE64 fails
+# x. set size to value inconsistent with device / PE size
+
+#COMM 'pvcreate basic dataalignment sanity checks'
+not pvcreate --dataalignment -1 $dev1
+not pvcreate -M 1 --dataalignment 1 $dev1
+not pvcreate --dataalignment 1e $dev1
+
+#COMM 'pvcreate always rounded up to page size for start of device'
+#pvcreate --metadatacopies 0 --dataalignment 1 $dev1
+# amuse shell experts
+#check_pv_field_ $dev1 pe_start $(($(getconf PAGESIZE)/1024))".00k"
+
+#COMM 'pvcreate sets data offset directly'
+pvcreate --dataalignment 512k $dev1
+check_pv_field_ $dev1 pe_start 512.00k
+
+#COMM 'vgcreate/vgremove do not modify data offset of existing PV'
+vgcreate $vg $dev1 --config 'devices { data_alignment = 1024 }'
+check_pv_field_ $dev1 pe_start 512.00k
+vgremove $vg --config 'devices { data_alignment = 1024 }'
+check_pv_field_ $dev1 pe_start 512.00k
+
+#COMM 'pvcreate sets data offset next to mda area'
+pvcreate --metadatasize 100k --dataalignment 100k $dev1
+check_pv_field_ $dev1 pe_start 200.00k
+
+# metadata area start is aligned according to pagesize
+# pagesize should be 64k or 4k ...
+if [ $PAGESIZE -eq 65536 ] ; then
+ pv_align="192.50k"
+else
+ pv_align="133.00k"
+fi
+
+pvcreate --metadatasize 128k --dataalignment 3.5k $dev1
+check_pv_field_ $dev1 pe_start $pv_align
+
+pvcreate --metadatasize 128k --metadatacopies 2 --dataalignment 3.5k $dev1
+check_pv_field_ $dev1 pe_start $pv_align
+
+# data area is aligned to 1M by default,
+# data area start is shifted by the specified alignment_offset
+pv_align="1052160B" # 1048576 + (7*512)
+pvcreate --metadatasize 128k --dataalignmentoffset 7s $dev1
+check_pv_field_ $dev1 pe_start $pv_align "--units b"
+
+# 2nd metadata area is created without problems when
+# data area start is shifted by the specified alignment_offset
+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
+pvcreate --dataalignment 256k $dev1
+vgcreate -s 1m $vg $dev1
+vgconvert -M1 $vg
+vgconvert -M2 $vg
+check_pv_field_ $dev1 pe_start 256.00k
+vgremove $vg
+
+#COMM 'pv with LVM1 incompatible data alignment cannot be convereted'
+pvcreate --dataalignment 10k $dev1
+vgcreate -s 1m $vg $dev1
+not vgconvert -M1 $vg
+vgremove $vg
+
+#COMM 'vgcfgrestore allows pe_start=0'
+#basically it produces nonsense, but it tests vgcfgrestore,
+#not that final cfg is usable...
+pvcreate --metadatacopies 0 $dev1
+pvcreate $dev2
+vgcreate $vg $dev1 $dev2
+vgcfgbackup -f "$(pwd)/backup.$$" $vg
+sed 's/pe_start = [0-9]*/pe_start = 0/' "$(pwd)/backup.$$" > "$(pwd)/backup.$$1"
+vgcfgrestore -f "$(pwd)/backup.$$1" $vg
+check_pv_field_ $dev1 pe_start 0
+check_pv_field_ $dev2 pe_start 0
+vgremove $vg
+
+echo test pvcreate --metadataignore
+for pv_in_vg in 1 0; do
+for mdacp in 1 2; do
+for ignore in y n; do
+ echo pvcreate --metadataignore has proper mda_count and mda_used_count
+ pvcreate --metadatacopies $mdacp --metadataignore $ignore $dev1 $dev2
+ check_pv_field_ $dev1 pv_mda_count $mdacp
+ check_pv_field_ $dev2 pv_mda_count $mdacp
+ if [ $ignore = y ]; then
+ check_pv_field_ $dev1 pv_mda_used_count 0
+ check_pv_field_ $dev2 pv_mda_used_count 0
+ else
+ check_pv_field_ $dev1 pv_mda_used_count $mdacp
+ check_pv_field_ $dev2 pv_mda_used_count $mdacp
+ fi
+ echo vgcreate has proper vg_mda_count and vg_mda_used_count
+ if [ $pv_in_vg = 1 ]; then
+ vgcreate -c n "$vg" $dev1 $dev2
+ check_vg_field_ $vg vg_mda_count $(($mdacp * 2))
+ if [ $ignore = y ]; then
+ check_vg_field_ $vg vg_mda_used_count 1
+ else
+ check_vg_field_ $vg vg_mda_used_count $(($mdacp * 2))
+ fi
+ check_vg_field_ $vg vg_mda_copies unmanaged
+ vgremove $vg
+ fi
+done
+done
+done
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+# Copyright (C) 2007 NEC Corporation
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+test_description="ensure that pvmove works with basic options"
+
+. ./test-utils.sh
+
+# ---------------------------------------------------------------------
+# Utilities
+
+lvdev_() {
+ echo "$DM_DEV_DIR/$1/$2"
+}
+
+lv_is_on_() {
+ local lv=$1 #allready vg/lv
+ shift 1
+ lvs -a -odevices --noheadings $lv | sed 's/,/\n/g' > out
+#is on all specified devs
+ for d in $*; do grep "$d(" out; done
+#isn't on any other dev (we are set -e remember)
+ for d in $*; do ! grep -v "$d(" out; done
+ return 0
+}
+
+save_dev_sum_() {
+ mkfs.ext3 $1 > /dev/null && md5sum $1 > md5.$(basename $1)
+}
+
+check_dev_sum_() {
+ md5sum $1 > md5.tmp && cmp md5.$(basename $1) md5.tmp
+}
+
+# ---------------------------------------------------------------------
+# Initialize PVs and VGs
+
+aux prepare_vg 5 80
+
+# ---------------------------------------------------------------------
+# Common environment setup/cleanup for each sub testcases
+
+prepare_lvs_() {
+ lvcreate -l2 -n $lv1 $vg $dev1
+ lv_is_on_ $vg/$lv1 $dev1
+ lvcreate -l9 -i3 -n $lv2 $vg $dev2 $dev3 $dev4
+ lv_is_on_ $vg/$lv2 $dev2 $dev3 $dev4
+ lvextend -l+2 $vg/$lv1 $dev2
+ lv_is_on_ $vg/$lv1 $dev1 $dev2
+ lvextend -l+2 $vg/$lv1 $dev3
+ lv_is_on_ $vg/$lv1 $dev1 $dev2 $dev3
+ lvextend -l+2 $vg/$lv1 $dev1
+ lv_is_on_ $vg/$lv1 $dev1 $dev2 $dev3 $dev1
+ lvcreate -l1 -n $lv3 $vg $dev2
+ lv_is_on_ $vg/$lv3 $dev2
+ save_dev_sum_ $(lvdev_ $vg $lv1)
+ save_dev_sum_ $(lvdev_ $vg $lv2)
+ save_dev_sum_ $(lvdev_ $vg $lv3)
+ lvs -a -o devices --noheadings $vg/$lv1 > ${lv1}_devs
+ lvs -a -o devices --noheadings $vg/$lv2 > ${lv2}_devs
+ lvs -a -o devices --noheadings $vg/$lv3 > ${lv3}_devs
+}
+
+lv_not_changed_() {
+ lvs -a -o devices --noheadings $1 > out
+ diff $(basename $1)_devs out
+}
+
+check_and_cleanup_lvs_() {
+ lvs -a -o+devices $vg
+ check_dev_sum_ $(lvdev_ $vg $lv1)
+ check_dev_sum_ $(lvdev_ $vg $lv2)
+ check_dev_sum_ $(lvdev_ $vg $lv3)
+ lvs -a -o name $vg > out && ! grep ^pvmove out
+ lvremove -ff $vg
+ if ! dmsetup table|not grep $vg; then
+ echo "ERROR: lvremove did leave some some mappings in DM behind!" && \
+ return 1
+ fi
+ :
+}
+
+#COMM "check environment setup/cleanup"
+prepare_lvs_
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+# pvmove tests
+
+# ---
+# filter by LV
+
+#COMM "only specified LV is moved: from pv2 to pv5 only for lv1"
+prepare_lvs_
+pvmove -i1 -n $vg/$lv1 $dev2 $dev5
+lv_is_on_ $vg/$lv1 $dev1 $dev5 $dev3 $dev1
+lv_not_changed_ $vg/$lv2
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+# ---
+# segments in a LV
+
+#COMM "the 1st seg of 3-segs LV is moved: from pv1 of lv1 to pv4"
+prepare_lvs_
+pvmove -i1 -n $vg/$lv1 $dev1 $dev4
+lv_is_on_ $vg/$lv1 $dev4 $dev2 $dev3 $dev4
+lv_not_changed_ $vg/$lv2
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "the 2nd seg of 3-segs LV is moved: from pv2 of lv1 to pv4"
+prepare_lvs_
+pvmove -i1 -n $vg/$lv1 $dev2 $dev4
+lv_is_on_ $vg/$lv1 $dev1 $dev4 $dev3 $dev1
+lv_not_changed_ $vg/$lv2
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "the 3rd seg of 3-segs LV is moved: from pv3 of lv1 to pv4"
+prepare_lvs_
+pvmove -i1 -n $vg/$lv1 $dev3 $dev4
+lv_is_on_ $vg/$lv1 $dev1 $dev2 $dev4 $dev1
+lv_not_changed_ $vg/$lv2
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+# ---
+# multiple LVs matching
+
+#COMM "1 out of 3 LVs is moved: from pv4 to pv5"
+prepare_lvs_
+pvmove -i1 $dev4 $dev5
+lv_not_changed_ $vg/$lv1
+lv_is_on_ $vg/$lv2 $dev2 $dev3 $dev5
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "2 out of 3 LVs are moved: from pv3 to pv5"
+prepare_lvs_
+pvmove -i1 $dev3 $dev5
+lv_is_on_ $vg/$lv1 $dev1 $dev2 $dev5 $dev1
+lv_is_on_ $vg/$lv2 $dev2 $dev5 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "3 out of 3 LVs are moved: from pv2 to pv5"
+prepare_lvs_
+pvmove -i1 $dev2 $dev5
+lv_is_on_ $vg/$lv1 $dev1 $dev5 $dev3 $dev1
+lv_is_on_ $vg/$lv2 $dev5 $dev3 $dev4
+lv_is_on_ $vg/$lv3 $dev5
+check_and_cleanup_lvs_
+
+# ---
+# areas of striping
+
+#COMM "move the 1st stripe: from pv2 of lv2 to pv1"
+prepare_lvs_
+pvmove -i1 -n $vg/$lv2 $dev2 $dev1
+lv_not_changed_ $vg/$lv1
+lv_is_on_ $vg/$lv2 $dev1 $dev3 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "move the 2nd stripe: from pv3 of lv2 to pv1"
+prepare_lvs_
+pvmove -i1 -n $vg/$lv2 $dev3 $dev1
+lv_not_changed_ $vg/$lv1
+lv_is_on_ $vg/$lv2 $dev2 $dev1 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "move the 3rd stripe: from pv4 of lv2 to pv1"
+prepare_lvs_
+pvmove -i1 -n $vg/$lv2 $dev4 $dev1
+lv_not_changed_ $vg/$lv1
+lv_is_on_ $vg/$lv2 $dev2 $dev3 $dev1
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+# ---
+# partial segment match (source segment splitted)
+
+#COMM "match to the start of segment:from pv2:0-0 to pv5"
+prepare_lvs_
+pvmove -i1 $dev2:0-0 $dev5
+lv_not_changed_ $vg/$lv1
+lv_is_on_ $vg/$lv2 $dev5 $dev2 $dev3 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "match to the middle of segment: from pv2:1-1 to pv5"
+prepare_lvs_
+pvmove -i1 $dev2:1-1 $dev5
+lv_not_changed_ $vg/$lv1
+lv_is_on_ $vg/$lv2 $dev2 $dev5 $dev2 $dev3 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "match to the end of segment: from pv2:2-2 to pv5"
+prepare_lvs_
+pvmove -i1 $dev2:2-2 $dev5
+lv_not_changed_ $vg/$lv1
+lv_is_on_ $vg/$lv2 $dev2 $dev5 $dev3 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+# ---
+# destination segment splitted
+
+#COMM "no destination split: from pv2:0-2 to pv5"
+prepare_lvs_
+pvmove -i1 $dev2:0-2 $dev5
+lv_not_changed_ $vg/$lv1
+lv_is_on_ $vg/$lv2 $dev5 $dev3 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "destination split into 2: from pv2:0-2 to pv5:5-5 and pv4:5-6"
+prepare_lvs_
+pvmove -i1 --alloc anywhere $dev2:0-2 $dev5:5-5 $dev4:5-6
+lv_not_changed_ $vg/$lv1
+lv_is_on_ $vg/$lv2 $dev5 $dev4 $dev3 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "destination split into 3: from pv2:0-2 to {pv3,4,5}:5-5"
+prepare_lvs_
+pvmove -i1 --alloc anywhere $dev2:0-2 $dev3:5-5 $dev4:5-5 $dev5:5-5
+lv_not_changed_ $vg/$lv1
+lv_is_on_ $vg/$lv2 $dev3 $dev4 $dev5 $dev3 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+# ---
+# alloc policy (anywhere, contiguous) with both success and failure cases
+
+#COMM "alloc normal on same PV for source and destination: from pv3:0-2 to pv3:5-7"
+prepare_lvs_
+not pvmove -i1 $dev3:0-2 $dev3:5-7
+# "(cleanup previous test)"
+lv_not_changed_ $vg/$lv1
+lv_not_changed_ $vg/$lv2
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "alloc anywhere on same PV for source and destination: from pv3:0-2 to pv3:5-7"
+prepare_lvs_
+pvmove -i1 --alloc anywhere $dev3:0-2 $dev3:5-7
+lv_not_changed_ $vg/$lv1
+lv_is_on_ $vg/$lv2 $dev2 $dev3 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "alloc anywhere but better area available: from pv3:0-2 to pv3:5-7 or pv5:5-6,pv4:5-5"
+prepare_lvs_
+pvmove -i1 --alloc anywhere $dev3:0-2 $dev3:5-7 $dev5:5-6 $dev4:5-5
+lv_not_changed_ $vg/$lv1
+#lv_is_on_ $vg/$lv2 $dev2 $dev5 $dev4 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "alloc contiguous but area not available: from pv2:0-2 to pv5:5-5 and pv4:5-6"
+prepare_lvs_
+not pvmove -i1 --alloc contiguous $dev2:0-2 $dev5:5-5 $dev4:5-6
+# "(cleanup previous test)"
+lv_not_changed_ $vg/$lv1
+lv_not_changed_ $vg/$lv2
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "alloc contiguous and contiguous area available: from pv2:0-2 to pv5:0-0,pv5:3-5 and pv4:5-6"
+prepare_lvs_
+pvmove -i1 --alloc contiguous $dev2:0-2 $dev5:0-0 $dev5:3-5 $dev4:5-6
+lv_not_changed_ $vg/$lv1
+lv_is_on_ $vg/$lv2 $dev5 $dev3 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+# ---
+# multiple segments in a LV
+
+#COMM "multiple source LVs: from pv3 to pv5"
+prepare_lvs_
+pvmove -i1 $dev3 $dev5
+lv_is_on_ $vg/$lv1 $dev1 $dev2 $dev5
+lv_is_on_ $vg/$lv2 $dev2 $dev5 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+# ---
+# move inactive LV
+
+#COMM "move inactive LV: from pv2 to pv5"
+prepare_lvs_
+lvchange -an $vg/$lv1
+lvchange -an $vg/$lv3
+pvmove -i1 $dev2 $dev5
+lv_is_on_ $vg/$lv1 $dev1 $dev5 $dev3
+lv_is_on_ $vg/$lv2 $dev5 $dev3 $dev4
+lv_is_on_ $vg/$lv3 $dev5
+check_and_cleanup_lvs_
+
+# ---
+# other failure cases
+
+#COMM "no PEs to move: from pv3 to pv1"
+prepare_lvs_
+pvmove -i1 $dev3 $dev1
+not pvmove -i1 $dev3 $dev1
+# "(cleanup previous test)"
+lv_is_on_ $vg/$lv1 $dev1 $dev2 $dev1
+lv_is_on_ $vg/$lv2 $dev2 $dev1 $dev4
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "no space available: from pv2:0-0 to pv1:0-0"
+prepare_lvs_
+not pvmove -i1 $dev2:0-0 $dev1:0-0
+# "(cleanup previous test)"
+lv_not_changed_ $vg/$lv1
+lv_not_changed_ $vg/$lv2
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM 'same source and destination: from pv1 to pv1'
+prepare_lvs_
+not pvmove -i1 $dev1 $dev1
+#"(cleanup previous test)"
+lv_not_changed_ $vg/$lv1
+lv_not_changed_ $vg/$lv2
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+#COMM "sum of specified destination PEs is large enough, but it includes source PEs and the free PEs are not enough"
+prepare_lvs_
+not pvmove --alloc anywhere $dev1:0-2 $dev1:0-2 $dev5:0-0 2> err
+#"(cleanup previous test)"
+grep "Insufficient free space" err
+lv_not_changed_ $vg/$lv1
+lv_not_changed_ $vg/$lv2
+lv_not_changed_ $vg/$lv3
+check_and_cleanup_lvs_
+
+# ---------------------------------------------------------------------
+
+#COMM "pvmove abort"
+prepare_lvs_
+pvmove -i100 -b $dev1 $dev3
+pvmove --abort
+check_and_cleanup_lvs_
+
+#COMM "pvmove out of --metadatacopies 0 PV (bz252150)"
+vgremove -ff $vg
+pvcreate $devs
+pvcreate --metadatacopies 0 $dev1 $dev2
+vgcreate -c n $vg $devs
+lvcreate -l4 -n $lv1 $vg $dev1
+pvmove $dev1
+
+#COMM "pvmove fails activating mirror, properly restores state before pvmove"
+dmsetup create "$vg-pvmove0" --notable
+not pvmove -i 1 $dev2
+test $(dmsetup info --noheadings -c -o suspended "$vg-$lv1") = "Active"
+dmsetup remove "$vg-pvmove0"
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_devs 3
+pvcreate $dev1
+pvcreate --metadatacopies 0 $dev2
+pvcreate --metadatacopies 2 $dev3
+pvremove $dev2
+
+# failing, but still removing everything what can be removed
+# is somewhat odd as default, what do we have -f for?
+pvs | not grep $dev2
+pvcreate --metadatacopies 0 $dev2
+
+# check pvremove refuses to remove pv in a vg
+vgcreate -c n $vg $dev1 $dev2
+not pvremove $dev2 $dev3
+
+for mdacp in 0 1 2; do
+ # check pvremove truly wipes the label (pvscan wont find) (---metadatacopies $mdacp)
+ pvcreate --metadatacopies $mdacp $dev3
+ pvremove $dev3
+ # try to remove agail - should fail cleanly
+ not pvremove $dev3
+ pvscan | not grep $dev3
+
+ # bz179473 refuse to wipe non-PV device without -f
+ not pvremove $dev3
+ pvremove -f $dev3
+
+ # reset setup
+ vgremove -ff $vg
+ pvcreate --metadatacopies $mdacp $dev1
+ pvcreate $dev2
+ vgcreate $vg $dev1 $dev2
+
+ # pvremove -f fails when pv in a vg (---metadatacopies $mdacp)
+ not pvremove -f $dev1
+ pvs $dev1
+
+ # pvremove -ff fails without confirmation when pv in a vg (---metadatacopies $mdacp)
+ echo n | not pvremove -ff $dev1
+
+ # pvremove -ff succeds with confirmation when pv in a vg (---metadatacopies $mdacp)
+ pvremove -ffy $dev1
+ not pvs $dev1
+
+ vgreduce --removemissing $vg
+ pvcreate --metadatacopies $mdacp $dev1
+ vgextend $vg $dev1
+
+ # pvremove -ff -y is sufficient when pv in a vg (---metadatacopies $mdacp)" '
+ echo n | pvremove -ff -y $dev1
+
+ vgreduce --removemissing $vg
+ pvcreate --metadatacopies $mdacp $dev1
+ vgextend $vg $dev1
+done
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#
+# tests basic functionality of read-ahead and ra regressions
+#
+
+test_description='Test read-ahead functionality'
+
+. ./test-utils.sh
+
+
+get_lvs_() {
+ lvs --units s --nosuffix --noheadings -o $1 "$vg"/"$lv"
+}
+
+check_lvs_() {
+ case $(get_lvs_ $1) in
+ *$2) true ;;
+ *) false ;;
+ esac
+}
+
+aux prepare_vg 5
+
+#COMM "test various read ahead settings (bz450922)"
+lvcreate -n "$lv" -l 100%FREE -i5 -I256 "$vg"
+ra="$(get_lvs_ lv_kernel_read_ahead)"
+test "$(( ( $ra / 5 ) * 5 ))" -eq $ra
+lvdisplay "$vg"/"$lv"
+not lvchange -r auto "$vg"/"$lv" 2>&1 | grep auto
+check_lvs_ lv_read_ahead auto
+check_lvs_ lv_kernel_read_ahead 5120
+lvchange -r 640 "$vg/$lv"
+check_lvs_ lv_read_ahead 640
+lvremove -ff "$vg"
+
+#COMM "read ahead is properly inherited from underlying PV"
+blockdev --setra 768 $dev1
+vgscan
+lvcreate -n $lv -L4m $vg $dev1
+test $(blockdev --getra $DM_DEV_DIR/$vg/$lv) -eq 768
+lvremove -ff $vg
+
+# Check default, active/inactive values for read_ahead / kernel_read_ahead
+lvcreate -n $lv -l 50%FREE $vg
+lvchange -an $vg/$lv
+check_lv_field_ $vg/$lv lv_read_ahead auto
+check_lv_field_ $vg/$lv lv_kernel_read_ahead -1
+lvchange -r 512 $vg/$lv
+lvchange -ay $vg/$lv
+check_lv_field_ $vg/$lv lv_read_ahead 256.00k
+check_lv_field_ $vg/$lv lv_kernel_read_ahead 256.00k
+lvremove -ff $vg
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# no automatic extensions please
+LVM_TEST_CONFIG_SNAPSHOT_AUTOEXTEND="
+ snapshot_autoextend_percent = 0
+ snapshot_autoextend_threshold = 100"
+
+. ./test-utils.sh
+
+which mkfs.ext2 || exit 200
+
+prepare_lvmconf
+
+aux prepare_vg 2
+aux prepare_dmeventd
+
+lvcreate -l 8 -n base $vg
+mkfs.ext2 $DM_DEV_DIR/$vg/base
+
+lvcreate -s -l 4 -n snap $vg/base
+lvchange --monitor y $vg/snap
+
+mkdir mnt
+mount $DM_DEV_DIR/$vg/snap mnt
+mount
+cat /proc/mounts | grep $vg-snap
+
+dd if=/dev/zero of=mnt/file$1 bs=1M count=17
+sync
+sleep 10 # dmeventd only checks every 10 seconds :(
+
+cat /proc/mounts | not grep $vg-snap
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+set -xv
+
+which mkfs.ext3 || exit 200
+
+. ./test-utils.sh
+
+lvdev_()
+{
+ echo "$DM_DEV_DIR/$1/$2"
+}
+
+snap_lv_name_() {
+ echo ${1}_snap
+}
+
+setup_merge() {
+ local VG_NAME=$1
+ local LV_NAME=$2
+ local NUM_EXTRA_SNAPS="$3"
+ test -z "$NUM_EXTRA_SNAPS" && NUM_EXTRA_SNAPS=0
+ local BASE_SNAP_LV_NAME=$(snap_lv_name_ $LV_NAME)
+
+ lvcreate -n $LV_NAME -l 50%FREE $VG_NAME
+ lvcreate -s -n $BASE_SNAP_LV_NAME -l 20%FREE ${VG_NAME}/${LV_NAME}
+ mkfs.ext3 $(lvdev_ $VG_NAME $LV_NAME)
+
+ if [ $NUM_EXTRA_SNAPS -gt 0 ]; then
+ for i in `seq 1 $NUM_EXTRA_SNAPS`; do
+ lvcreate -s -n ${BASE_SNAP_LV_NAME}_${i} -l 20%FREE ${VG_NAME}/${LV_NAME}
+ done
+ fi
+}
+
+aux prepare_vg 1 100
+
+
+# test full merge of a single LV
+setup_merge $vg $lv1
+# now that snapshot LV is created: test if snapshot-merge target is available
+$(dmsetup targets | grep -q snapshot-merge) || exit 200
+lvs -a
+# make sure lvconvert --merge requires explicit LV listing
+not lvconvert --merge 2>err
+lvconvert --merge $vg/$(snap_lv_name_ $lv1)
+lvremove -f $vg/$lv1
+
+
+# test that an actively merging snapshot may not be removed
+setup_merge $vg $lv1
+lvconvert -i+100 --merge --background $vg/$(snap_lv_name_ $lv1)
+not lvremove -f $vg/$(snap_lv_name_ $lv1)
+lvremove -f $vg/$lv1
+
+
+# "onactivate merge" test
+setup_merge $vg $lv1
+lvs -a
+mkdir test_mnt
+mount $(lvdev_ $vg $lv1) test_mnt
+lvconvert --merge $vg/$(snap_lv_name_ $lv1)
+# -- refresh LV while FS is still mounted (merge must not start),
+# verify 'snapshot-origin' target is still being used
+lvchange --refresh $vg/$lv1
+umount test_mnt
+rm -r test_mnt
+dmsetup table ${vg}-${lv1} | grep -q " snapshot-origin "
+# -- refresh LV to start merge (now that FS is unmounted),
+# an active merge uses the 'snapshot-merge' target
+lvchange --refresh $vg/$lv1
+dmsetup table ${vg}-${lv1} | grep -q " snapshot-merge "
+# -- don't care if merge is still active; lvremove at this point
+# may test stopping an active merge
+lvremove -f $vg/$lv1
+
+
+# "onactivate merge" test
+# -- deactivate/remove after disallowed merge attempt, tests
+# to make sure preload of origin's metadata is _not_ performed
+setup_merge $vg $lv1
+lvs -a
+mkdir test_mnt
+mount $(lvdev_ $vg $lv1) test_mnt
+lvconvert --merge $vg/$(snap_lv_name_ $lv1)
+# -- refresh LV while FS is still mounted (merge must not start),
+# verify 'snapshot-origin' target is still being used
+lvchange --refresh $vg/$lv1
+umount test_mnt
+rm -r test_mnt
+dmsetup table ${vg}-${lv1} | grep -q " snapshot-origin "
+lvremove -f $vg/$lv1
+
+
+# test multiple snapshot merge; tests copy out that is driven by merge
+setup_merge $vg $lv1 1
+lvs -a
+lvconvert --merge $vg/$(snap_lv_name_ $lv1)
+lvremove -f $vg/$lv1
+
+
+# test merging multiple snapshots that share the same tag
+setup_merge $vg $lv1
+setup_merge $vg $lv2
+lvs -a
+lvchange --addtag this_is_a_test $vg/$(snap_lv_name_ $lv1)
+lvchange --addtag this_is_a_test $vg/$(snap_lv_name_ $lv2)
+lvconvert --merge @this_is_a_test
+lvs | not grep $(snap_lv_name_ $lv1)
+lvs | not grep $(snap_lv_name_ $lv2)
+lvremove -f $vg/$lv1
+lvremove -f $vg/$lv2
+
+# FIXME following tests would need to poll merge progress, via periodic lvs?
+# Background processes don't lend themselves to lvm testsuite...
+
+# test: onactivate merge of a single lv
+
+# test: do onactivate, deactivate the origin LV, reactivate the LV, merge should resume
+
+# test: multiple onactivate merge
+
+
+vgremove -f "$vg"
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+prepare_vg 4
+
+# Create snapshot of a mirror origin
+lvcreate -m 1 -L 10M -n lv $vg
+lvcreate -s $vg/lv -L 10M -n snap
+
+# Down-convert (mirror -> linear) under a snapshot
+lvconvert -m0 $vg/lv
+
+# Up-convert (linear -> mirror)
+lvconvert -m2 $vg/lv
+
+# Down-convert (mirror -> mirror)
+lvconvert -m1 $vg/lv
+
+# Up-convert (mirror -> mirror) -- Not supported!
+not lvconvert -m2 $vg/lv
+
+# Log conversion (disk -> core)
+lvconvert --mirrorlog core $vg/lv
+
+# Log conversion (core -> mirrored)
+lvconvert --mirrorlog mirrored $vg/lv
+
+# Log conversion (mirrored -> core)
+lvconvert --mirrorlog core $vg/lv
+
+# Log conversion (core -> disk)
+lvconvert --mirrorlog disk $vg/lv
+
+# Clean-up
+lvremove -ff $vg
--- /dev/null
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_pvs 5
+
+# vgcreate with --addtag
+vgcreate -c n --addtag firstvg $vg1 $dev1 $dev2
+vgcreate -c n --addtag secondvg $vg2 $dev3 $dev4
+check_vg_field_ $vg1 tags firstvg
+check_vg_field_ $vg2 tags secondvg
+vgremove -ff $vg1
+vgremove -ff $vg2
+
+# vgchange with --addtag and --deltag
+vgcreate -c n $vg1 $dev1 $dev2
+vgcreate -c n $vg2 $dev3 $dev4
+vgchange --addtag firstvgtag1 $vg1
+# adding a tag multiple times is not an error
+vgchange --addtag firstvgtag2 $vg1
+vgchange --addtag firstvgtag2 $vg1
+vgchange --addtag firstvgtag3 $vg1
+vgchange --addtag secondvgtag1 $vg2
+vgchange --addtag secondvgtag2 $vg2
+vgchange --addtag secondvgtag3 $vg2
+check_vg_field_ @firstvgtag2 tags "firstvgtag1,firstvgtag2,firstvgtag3"
+check_vg_field_ @secondvgtag1 tags "secondvgtag1,secondvgtag2,secondvgtag3"
+vgchange --deltag firstvgtag2 $vg1
+check_vg_field_ @firstvgtag1 tags "firstvgtag1,firstvgtag3"
+# deleting a tag multiple times is not an error
+vgchange --deltag firstvgtag2 $vg1
+vgchange --deltag firstvgtag1 $vg2
+vgremove -ff $vg1
+vgremove -ff $vg2
+
+# lvcreate with --addtag
+vgcreate -c n $vg1 $dev1 $dev2
+lvcreate --addtag firstlvtag1 -l 4 -n $lv1 $vg1
+lvcreate --addtag secondlvtag1 -l 4 -n $lv2 $vg1
+check_lv_field_ @firstlvtag1 tags "firstlvtag1"
+not check_lv_field_ @secondlvtag1 tags "firstlvtag1"
+check_lv_field_ $vg1/$lv2 tags "secondlvtag1"
+not check_lv_field_ $vg1/$lv1 tags "secondlvtag1"
+vgremove -ff $vg1
+
+# lvchange with --addtag and --deltag
+vgcreate -c n $vg1 $dev1 $dev2
+lvcreate -l 4 -n $lv1 $vg1
+lvcreate -l 4 -n $lv2 $vg1
+lvchange --addtag firstlvtag1 $vg1/$lv1
+# adding a tag multiple times is not an error
+lvchange --addtag firstlvtag2 $vg1/$lv1
+lvchange --addtag firstlvtag2 $vg1/$lv1
+lvchange --addtag firstlvtag3 $vg1/$lv1
+lvchange --addtag secondlvtag1 $vg1/$lv2
+lvchange --addtag secondlvtag2 $vg1/$lv2
+lvchange --addtag secondlvtag3 $vg1/$lv2
+check_lv_field_ $vg1/$lv1 tags "firstlvtag1,firstlvtag2,firstlvtag3"
+not $(check_lv_field_ $vg1/$lv1 tags "secondlvtag1")
+check_lv_field_ $vg1/$lv2 tags "secondlvtag1,secondlvtag2,secondlvtag3"
+not $(check_lv_field_ $vg1/$lv1 tags "secondlvtag1")
+# deleting a tag multiple times is not an error
+lvchange --deltag firstlvtag2 $vg1/$lv1
+lvchange --deltag firstlvtag2 $vg1/$lv1
+check_lv_field_ $vg1/$lv1 tags "firstlvtag1,firstlvtag3"
+check_lv_field_ $vg1/$lv2 tags "secondlvtag1,secondlvtag2,secondlvtag3"
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#
+# Testcase for bugzilla #621173
+# excercises partition table scanning code path
+#
+
+which sfdisk || exit 200
+
+LVM_TEST_CONFIG_DEVICES="types = [\"device-mapper\", 142]"
+
+. ./test-utils.sh
+
+aux prepare_pvs 1 30
+
+pvs
+
+# create small partition table
+echo "1 2" | sfdisk $dev1
+
+pvs
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+which mkfs.ext3 || exit 200
+
+# Get linux minor version
+linux_minor=$(echo `uname -r` | cut -d'.' -f3 | cut -d'-' -f1)
+
+test $linux_minor -ge 31 || exit 200
+
+. ./test-utils.sh
+
+check_logical_block_size()
+{
+ local DEV_=$1
+ local LOGICAL_BS=$2
+ # Verify logical_block_size - requires Linux >= 2.6.31
+ SYSFS_LOGICAL_BLOCK_SIZE=`echo /sys/block/$(basename $DEV_)/queue/logical_block_size`
+ if [ -f "$SYSFS_LOGICAL_BLOCK_SIZE" ] ; then
+ ACTUAL_LOGICAL_BLOCK_SIZE=`cat $SYSFS_LOGICAL_BLOCK_SIZE`
+ test $ACTUAL_LOGICAL_BLOCK_SIZE = $LOGICAL_BS
+ fi
+}
+
+lvdev_()
+{
+ echo "$DM_DEV_DIR/$1/$2"
+}
+
+test_snapshot_mount()
+{
+ lvcreate -L 16M -n $lv1 $vg $dev1
+ mkfs.ext3 $(lvdev_ $vg $lv1)
+ mkdir test_mnt
+ mount $(lvdev_ $vg $lv1) test_mnt
+ lvcreate -L 16M -n $lv2 -s $vg/$lv1
+ umount test_mnt
+ # mount the origin
+ mount $(lvdev_ $vg $lv1) test_mnt
+ umount test_mnt
+ # mount the snapshot
+ mount $(lvdev_ $vg $lv2) test_mnt
+ umount test_mnt
+ rm -r test_mnt
+ vgchange -an $vg
+ lvremove -f $vg/$lv2
+ lvremove -f $vg/$lv1
+}
+
+# FIXME add more topology-specific tests and validation (striped LVs, etc)
+
+NUM_DEVS=1
+PER_DEV_SIZE=34
+DEV_SIZE=$(($NUM_DEVS*$PER_DEV_SIZE))
+
+# ---------------------------------------------
+# Create "desktop-class" 4K drive
+# (logical_block_size=512, physical_block_size=4096, alignment_offset=0):
+LOGICAL_BLOCK_SIZE=512
+prepare_scsi_debug_dev $DEV_SIZE \
+ sector_size=$LOGICAL_BLOCK_SIZE physblk_exp=3
+check_logical_block_size $SCSI_DEBUG_DEV $LOGICAL_BLOCK_SIZE
+
+aux prepare_pvs $NUM_DEVS $PER_DEV_SIZE
+vgcreate -c n $vg $devs
+test_snapshot_mount
+vgremove $vg
+
+cleanup_scsi_debug_dev
+
+# ---------------------------------------------
+# Create "desktop-class" 4K drive w/ 63-sector DOS partition compensation
+# (logical_block_size=512, physical_block_size=4096, alignment_offset=3584):
+LOGICAL_BLOCK_SIZE=512
+prepare_scsi_debug_dev $DEV_SIZE \
+ sector_size=$LOGICAL_BLOCK_SIZE physblk_exp=3 lowest_aligned=7
+check_logical_block_size $SCSI_DEBUG_DEV $LOGICAL_BLOCK_SIZE
+
+aux prepare_pvs $NUM_DEVS $PER_DEV_SIZE
+vgcreate -c n $vg $devs
+test_snapshot_mount
+vgremove $vg
+
+cleanup_scsi_debug_dev
+
+# ---------------------------------------------
+# Create "enterprise-class" 4K drive
+# (logical_block_size=4096, physical_block_size=4096, alignment_offset=0):
+LOGICAL_BLOCK_SIZE=4096
+prepare_scsi_debug_dev $DEV_SIZE \
+ sector_size=$LOGICAL_BLOCK_SIZE
+check_logical_block_size $SCSI_DEBUG_DEV $LOGICAL_BLOCK_SIZE
+
+aux prepare_pvs $NUM_DEVS $PER_DEV_SIZE
+vgcreate -c n $vg $devs
+test_snapshot_mount
+vgremove $vg
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_vg 4
+
+lvcreate -l 1 -n $lv1 $vg
+lvcreate -l 2 -m 1 -n $lv2 $vg
+
+vgcfgbackup -f bak0 $vg
+sed -e 's,striped,unstriped,;s,mirror,unmirror,' -i.orig bak0
+vgcfgrestore -f bak0 $vg
+
+# we have on-disk metadata with unknown segments now
+not lvchange -a y $vg/$lv1 # check that activation is refused
+
+vgcfgbackup -f bak1 $vg
+cat bak1
+sed -e 's,unstriped,striped,;s,unmirror,mirror,' -i.orig bak1
+vgcfgrestore -f bak1 $vg
+vgcfgbackup -f bak2 $vg
+
+egrep -v 'description|seqno|creation_time|Generated' < bak0.orig > a
+egrep -v 'description|seqno|creation_time|Generated' < bak2 > b
+diff -u a b
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_vg 3
+
+lvcreate -m 1 -l 1 -n mirror $vg
+lvchange -a n $vg/mirror
+
+check() {
+vgscan 2>&1 | tee vgscan.out
+grep "Inconsistent metadata found for VG $vg" vgscan.out
+vgscan 2>&1 | tee vgscan.out
+not grep "Inconsistent metadata found for VG $vg" vgscan.out
+}
+
+# try orphaning a missing PV (bz45867)
+disable_dev $dev1
+vgreduce --removemissing --force $vg
+enable_dev $dev1
+check
+
+# try to just change metadata; we expect the new version (with MISSING_PV set
+# on the reappeared volume) to be written out to the previously missing PV
+vgextend $vg $dev1
+disable_dev $dev1
+lvremove $vg/mirror
+enable_dev $dev1
+check
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_pvs 4
+
+# vgcfgbackup handles similar VG names (bz458941)
+vg1=${PREFIX}vg00
+vg2=${PREFIX}vg01
+vgcreate $vg1 $dev1
+vgcreate $vg2 $dev2
+vgcfgbackup -f $TESTDIR/bak-%s >out
+grep "Volume group \"$vg1\" successfully backed up." out
+grep "Volume group \"$vg2\" successfully backed up." out
+vgremove -ff $vg1
+vgremove -ff $vg2
+
+# vgcfgbackup correctly stores metadata with missing PVs
+# and vgcfgrestore able to restore them when device reappears
+pv1_uuid=$(pvs --noheadings -o pv_uuid $dev1)
+pv2_uuid=$(pvs --noheadings -o pv_uuid $dev2)
+vgcreate $vg $devs
+lvcreate -l1 -n $lv1 $vg $dev1
+lvcreate -l1 -n $lv2 $vg $dev2
+lvcreate -l1 -n $lv3 $vg $dev3
+vgchange -a n $vg
+pvcreate -ff -y $dev1
+pvcreate -ff -y $dev2
+vgcfgbackup -f "$(pwd)/backup.$$" $vg
+sed 's/flags = \[\"MISSING\"\]/flags = \[\]/' "$(pwd)/backup.$$" > "$(pwd)/backup.$$1"
+pvcreate -ff -y --norestorefile -u $pv1_uuid $dev1
+pvcreate -ff -y --norestorefile -u $pv2_uuid $dev2
+vgcfgrestore -f "$(pwd)/backup.$$1" $vg
+vgremove -ff $vg
+
+# vgcfgbackup correctly stores metadata LVM1 with missing PVs
+# FIXME: clvmd seems to have problem with metadata format change here
+# fix it and remove this vgscan
+vgscan
+pvcreate -M1 $devs
+vgcreate -M1 -c n $vg $devs
+lvcreate -l1 -n $lv1 $vg $dev1
+pvremove -ff -y $dev2
+not lvcreate -l1 -n $lv1 $vg $dev3
+vgcfgbackup -f "$(pwd)/backup.$$" $vg
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+prepare_dmeventd
+aux prepare_pvs 3
+
+vgcreate -c n -l 2 $vg $dev1 $dev2 $dev3
+lvcreate -n one -l 1 $vg
+lvcreate -n two -l 1 $vg
+not lvcreate -n three -l 1 $vg
+vgchange -an $vg
+vgremove -ff $vg
+
+vgcreate -c n -l 3 $vg $dev1 $dev2 $dev3
+lvcreate -n one -l 1 $vg
+lvcreate -n snap -s -l 1 $vg/one
+lvcreate -n two -l 1 $vg
+not lvcreate -n three -l 1 $vg
+vgchange --monitor y $vg
+vgchange -an $vg 2>&1 | tee vgchange.out
+not grep "event server" vgchange.out
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+test_description='Exercise some vgchange diagnostics'
+
+. ./test-utils.sh
+
+aux prepare_pvs 3
+pvcreate --metadatacopies 0 $dev1
+vgcreate $vg $devs
+
+vgdisplay $vg
+
+# vgchange -p MaxPhysicalVolumes (bz202232)
+aux check_vg_field_ $vg max_pv 0
+vgchange -p 128 $vg
+aux check_vg_field_ $vg max_pv 128
+
+pv_count=$(get_vg_field $vg pv_count)
+not vgchange -p 2 $vg 2>err
+grep "MaxPhysicalVolumes is less than the current number $pv_count of PVs for" err
+aux check_vg_field_ $vg max_pv 128
+
+# vgchange -l MaxLogicalVolumes
+aux check_vg_field_ $vg max_lv 0
+vgchange -l 128 $vg
+aux check_vg_field_ $vg max_lv 128
+
+lvcreate -l4 -n$lv1 $vg
+lvcreate -l4 -n$lv2 $vg
+
+lv_count=$(get_vg_field $vg lv_count)
+not vgchange -l 1 $vg 2>err
+grep "MaxLogicalVolume is less than the current number $lv_count of LVs for" err
+aux check_vg_field_ $vg max_lv 128
+
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+test_description='Exercise some vgcreate diagnostics'
+
+. ./test-utils.sh
+
+aux prepare_devs 3
+pvcreate $dev1 $dev2
+pvcreate --metadatacopies 0 $dev3
+
+vg=${PREFIX}vg
+
+#COMM 'vgcreate accepts 8.00m physicalextentsize for VG'
+vgcreate -c n $vg --physicalextentsize 8.00m $dev1 $dev2
+check_vg_field_ $vg vg_extent_size 8.00m
+vgremove $vg
+# try vgck and to remove it again - should fail (but not segfault)
+not vgremove $vg
+not vgck $vg
+
+#COMM 'vgcreate accepts smaller (128) maxlogicalvolumes for VG'
+vgcreate -c n $vg --maxlogicalvolumes 128 $dev1 $dev2
+check_vg_field_ $vg max_lv 128
+vgremove $vg
+
+#COMM 'vgcreate accepts smaller (128) maxphysicalvolumes for VG'
+vgcreate -c n $vg --maxphysicalvolumes 128 $dev1 $dev2
+check_vg_field_ $vg max_pv 128
+vgremove $vg
+
+#COMM 'vgcreate rejects a zero physical extent size'
+not vgcreate -c n --physicalextentsize 0 $vg $dev1 $dev2 2>err
+grep "^ Physical extent size may not be zero\$" err
+
+#COMM 'vgcreate rejects "inherit" allocation policy'
+not vgcreate -c n --alloc inherit $vg $dev1 $dev2 2>err
+grep "^ Volume Group allocation policy cannot inherit from anything\$" err
+
+#COMM 'vgcreate rejects vgname "."'
+vginvalid=.;
+not vgcreate -c n $vginvalid $dev1 $dev2 2>err
+grep "New volume group name \"$vginvalid\" is invalid\$" err
+
+#COMM 'vgcreate rejects vgname greater than 128 characters'
+vginvalid=thisnameisridiculouslylongtotestvalidationcodecheckingmaximumsizethisiswhathappenswhenprogrammersgetboredandorarenotcreativedonttrythisathome
+not vgcreate -c n $vginvalid $dev1 $dev2 2>err
+grep "New volume group name \"$vginvalid\" is invalid\$" err
+
+#COMM 'vgcreate rejects already existing vgname "/tmp/$vg"'
+#touch /tmp/$vg
+#not vgcreate $vg $dev1 $dev2 2>err
+#grep "New volume group name \"$vg\" is invalid\$" err
+
+#COMM "vgcreate rejects repeated invocation (run 2 times) (bz178216)"
+vgcreate -c n $vg $dev1 $dev2
+not vgcreate -c n $vg $dev1 $dev2
+vgremove -ff $vg
+
+#COMM 'vgcreate rejects MaxLogicalVolumes > 255'
+not vgcreate -c n --metadatatype 1 --maxlogicalvolumes 1024 $vg $dev1 $dev2 2>err
+grep "^ Number of volumes may not exceed 255\$" err
+
+#COMM "vgcreate fails when the only pv has --metadatacopies 0"
+not vgcreate -c n $vg $dev3
+
+# Test default (4MB) vg_extent_size as well as limits of extent_size
+not vgcreate -c n --physicalextentsize 0k $vg $dev1 $dev2
+vgcreate -c n --physicalextentsize 1k $vg $dev1 $dev2
+check_vg_field_ $vg vg_extent_size 1.00k
+vgremove -ff $vg
+not vgcreate -c n --physicalextentsize 3K $vg $dev1 $dev2
+not vgcreate -c n --physicalextentsize 1024t $vg $dev1 $dev2
+#not vgcreate --physicalextentsize 1T $vg $dev1 $dev2
+# FIXME: vgcreate allows physicalextentsize larger than pv size!
+
+# Test default max_lv, max_pv, extent_size, alloc_policy, clustered
+vgcreate -c n $vg $dev1 $dev2
+check_vg_field_ $vg vg_extent_size 4.00m
+check_vg_field_ $vg max_lv 0
+check_vg_field_ $vg max_pv 0
+check_vg_field_ $vg vg_attr "wz--n-"
+vgremove -ff $vg
+
+# Implicit pvcreate tests, test pvcreate options on vgcreate
+# --force, --yes, --metadata{size|copies|type}, --zero
+# --dataalignment[offset]
+pvremove $dev1 $dev2
+vgcreate -c n --force --yes --zero y $vg $dev1 $dev2
+vgremove -f $vg
+pvremove -f $dev1
+
+for i in 0 1 2 3
+do
+# vgcreate (lvm2) succeeds writing LVM label at sector $i
+ vgcreate -c n --labelsector $i $vg $dev1
+ dd if=$dev1 bs=512 skip=$i count=1 2>/dev/null | strings | grep -q LABELONE;
+ vgremove -f $vg
+ pvremove -f $dev1
+done
+
+# pvmetadatacopies
+for i in 1 2
+do
+ vgcreate -c n --pvmetadatacopies $i $vg $dev1
+ check_pv_field_ $dev1 pv_mda_count $i
+ vgremove -f $vg
+ pvremove -f $dev1
+done
+not vgcreate -c n --pvmetadatacopies 0 $vg $dev1
+pvcreate --metadatacopies 1 $dev2
+vgcreate -c n --pvmetadatacopies 0 $vg $dev1 $dev2
+check_pv_field_ $dev1 pv_mda_count 0
+check_pv_field_ $dev2 pv_mda_count 1
+vgremove -f $vg
+pvremove -f $dev1
+
+# metadatasize, dataalignment, dataalignmentoffset
+#COMM 'pvcreate sets data offset next to mda area'
+vgcreate -c n --metadatasize 100k --dataalignment 100k $vg $dev1
+check_pv_field_ $dev1 pe_start 200.00k
+vgremove -f $vg
+pvremove -f $dev1
+
+# data area is aligned to 1M by default,
+# data area start is shifted by the specified alignment_offset
+pv_align="1052160B" # 1048576 + (7*512)
+vgcreate -c n --metadatasize 128k --dataalignmentoffset 7s $vg $dev1
+check_pv_field_ $dev1 pe_start $pv_align "--units b"
+vgremove -f $vg
+pvremove -f $dev1
+
+# metadatatype
+for i in 1 2
+do
+ vgcreate -c n -M $i $vg $dev1
+ check_vg_field_ $vg vg_fmt lvm$i
+ vgremove -f $vg
+ pvremove -f $dev1
+done
+
+# vgcreate fails if pv belongs to existing vg
+vgcreate -c n $vg1 $dev1 $dev2
+not vgcreate $vg2 $dev2
+vgremove -f $vg1
+pvremove -f $dev1 $dev2
+
+# all PVs exist in the VG after created
+pvcreate $dev1
+vgcreate -c n $vg1 $dev1 $dev2 $dev3
+check_pv_field_ $dev1 vg_name $vg1
+check_pv_field_ $dev2 vg_name $vg1
+check_pv_field_ $dev3 vg_name $vg1
+vgremove -f $vg1
+pvremove -f $dev1 $dev2 $dev3
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+
+aux prepare_vg 3
+lvcreate -m 1 -l 1 -n mirror $vg
+lvchange -a n $vg/mirror
+lvcreate -l 1 -n lv1 $vg $dev1
+
+# try to just change metadata; we expect the new version (with MISSING_PV set
+# on the reappeared volume) to be written out to the previously missing PV
+disable_dev $dev1
+lvremove $vg/mirror
+enable_dev $dev1
+not vgck $vg 2>&1 | tee log
+grep "missing 1 physical volume" log
+not lvcreate -m 1 -l 1 -n mirror $vg # write operations fail
+vgextend --restore $vg $dev1 # restore the missing device
+vgck $vg
+lvcreate -m 1 -l 1 -n mirror $vg
--- /dev/null
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#
+# Exercise various vgextend commands
+#
+
+. ./test-utils.sh
+
+aux prepare_devs 5
+
+for mdatype in 1 2
+do
+
+# Explicit pvcreate
+pvcreate -M$mdatype $dev1 $dev2 $dev3 $dev4 $dev5
+vgcreate -M$mdatype $vg1 $dev1 $dev2
+vgextend $vg1 $dev3 $dev4 $dev5
+vgremove -ff $vg1
+
+# Implicit pvcreate
+pvremove $dev1 $dev2 $dev3 $dev4 $dev5
+vgcreate -M$mdatype $vg1 $dev1 $dev2
+vgextend -M$mdatype $vg1 $dev3 $dev4 $dev5
+vgremove -ff $vg1
+pvremove $dev1 $dev2 $dev3 $dev4 $dev5
+
+done
+
+# Implicit pvcreate tests, test pvcreate options on vgcreate
+# --force, --yes, --metadata{size|copies|type}, --zero
+# --dataalignment[offset]
+vgcreate $vg $dev2
+vgextend --force --yes --zero y $vg $dev1
+vgreduce $vg $dev1
+pvremove -f $dev1
+
+for i in 0 1 2 3
+do
+# vgcreate (lvm2) succeeds writing LVM label at sector $i
+ vgextend --labelsector $i $vg $dev1
+ dd if=$dev1 bs=512 skip=$i count=1 2>/dev/null | strings | grep -q LABELONE;
+ vgreduce $vg $dev1
+ pvremove -f $dev1
+done
+
+# pvmetadatacopies
+for i in 0 1 2
+do
+ vgextend --pvmetadatacopies $i $vg $dev1
+ check_pv_field_ $dev1 pv_mda_count $i
+ vgreduce $vg $dev1
+ pvremove -f $dev1
+done
+
+# metadatasize, dataalignment, dataalignmentoffset
+#COMM 'pvcreate sets data offset next to mda area'
+vgextend --metadatasize 100k --dataalignment 100k $vg $dev1
+check_pv_field_ $dev1 pe_start 200.00k
+vgreduce $vg $dev1
+pvremove -f $dev1
+
+# data area is aligned to 1M by default,
+# data area start is shifted by the specified alignment_offset
+pv_align="1052160B" # 1048576 + (7*512)
+vgextend --metadatasize 128k --dataalignmentoffset 7s $vg $dev1
+check_pv_field_ $dev1 pe_start $pv_align "--units b"
+vgremove -f $vg
+pvremove -f $dev1
+
+# vgextend fails if pv belongs to existing vg
+vgcreate $vg1 $dev1 $dev3
+vgcreate $vg2 $dev2
+not vgextend $vg2 $dev3
+vgremove -f $vg1
+vgremove -f $vg2
+pvremove -f $dev1 $dev2 $dev3
+
+#vgextend fails if vg is not resizeable
+vgcreate $vg1 $dev1 $dev2
+vgchange --resizeable n $vg1
+not vgextend $vg1 $dev3
+vgremove -f $vg1
+pvremove -f $dev1 $dev2
+
+# all PVs exist in the VG after extended
+pvcreate $dev1
+vgcreate $vg1 $dev2
+vgextend $vg1 $dev1 $dev3
+check_pv_field_ $dev1 vg_name $vg1
+check_pv_field_ $dev2 vg_name $vg1
+check_pv_field_ $dev3 vg_name $vg1
+vgremove -f $vg1
+pvremove -f $dev1 $dev2 $dev3
+
+echo test vgextend --metadataignore
+for mdacp in 1 2; do
+for ignore in y n; do
+ echo vgextend --metadataignore has proper mda_count and mda_used_count
+ vgcreate $vg $dev3
+ vgextend --metadataignore $ignore --pvmetadatacopies $mdacp $vg $dev1 $dev2
+ check_pv_field_ $dev1 pv_mda_count $mdacp
+ check_pv_field_ $dev2 pv_mda_count $mdacp
+ if [ $ignore = y ]; then
+ check_pv_field_ $dev1 pv_mda_used_count 0
+ check_pv_field_ $dev2 pv_mda_used_count 0
+ else
+ check_pv_field_ $dev1 pv_mda_used_count $mdacp
+ check_pv_field_ $dev2 pv_mda_used_count $mdacp
+ fi
+ echo vg has proper vg_mda_count and vg_mda_used_count
+ check_vg_field_ $vg vg_mda_count $(($mdacp * 2 + 1))
+ if [ $ignore = y ]; then
+ check_vg_field_ $vg vg_mda_used_count 1
+ else
+ check_vg_field_ $vg vg_mda_used_count $(($mdacp * 2 + 1))
+ fi
+ check_vg_field_ $vg vg_mda_copies unmanaged
+ vgremove $vg
+ pvremove -ff $dev1 $dev2 $dev3
+done
+done
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2007-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+test_description='Test vgmerge operation'
+
+. ./test-utils.sh
+
+aux prepare_pvs 4 64
+
+# 'vgmerge succeeds with single linear LV in source VG'
+vgcreate -c n $vg1 $dev1 $dev2
+vgcreate -c n $vg2 $dev3 $dev4
+lvcreate -l 4 -n $lv1 $vg1 $dev1
+vgchange -an $vg1
+vg_validate_pvlv_counts_ $vg1 2 1 0
+vg_validate_pvlv_counts_ $vg2 2 0 0
+vgmerge $vg2 $vg1
+vg_validate_pvlv_counts_ $vg2 4 1 0
+vgremove -f $vg2
+
+# 'vgmerge succeeds with single linear LV in source and destination VG'
+vgcreate -c n $vg1 $dev1 $dev2
+vgcreate -c n $vg2 $dev3 $dev4
+lvcreate -l 4 -n $lv1 $vg1
+lvcreate -l 4 -n $lv2 $vg2
+vgchange -an $vg1
+vgchange -an $vg2
+vg_validate_pvlv_counts_ $vg1 2 1 0
+vg_validate_pvlv_counts_ $vg2 2 1 0
+vgmerge $vg2 $vg1
+vg_validate_pvlv_counts_ $vg2 4 2 0
+vgremove -f $vg2
+
+# 'vgmerge succeeds with linear LV + snapshots in source VG'
+vgcreate -c n $vg1 $dev1 $dev2
+vgcreate -c n $vg2 $dev3 $dev4
+lvcreate -l 16 -n $lv1 $vg1
+lvcreate -l 4 -s -n $lv2 $vg1/$lv1
+vgchange -an $vg1
+vg_validate_pvlv_counts_ $vg1 2 2 1
+vg_validate_pvlv_counts_ $vg2 2 0 0
+vgmerge $vg2 $vg1
+vg_validate_pvlv_counts_ $vg2 4 2 1
+lvremove -f $vg2/$lv2
+vgremove -f $vg2
+
+# 'vgmerge succeeds with mirrored LV in source VG'
+vgcreate -c n $vg1 $dev1 $dev2 $dev3
+vgcreate -c n $vg2 $dev4
+lvcreate -l 4 -n $lv1 -m1 $vg1
+vgchange -an $vg1
+vg_validate_pvlv_counts_ $vg1 3 1 0
+vg_validate_pvlv_counts_ $vg2 1 0 0
+vgmerge $vg2 $vg1
+vg_validate_pvlv_counts_ $vg2 4 1 0
+lvremove -f $vg2/$lv1
+vgremove -f $vg2
+
+# 'vgmerge rejects LV name collision'
+vgcreate -c n $vg1 $dev1 $dev2
+vgcreate -c n $vg2 $dev3 $dev4
+lvcreate -l 4 -n $lv1 $vg1
+lvcreate -l 4 -n $lv1 $vg2
+vgchange -an $vg1
+aux vg_validate_pvlv_counts_ $vg1 2 1 0
+aux vg_validate_pvlv_counts_ $vg2 2 1 0
+not vgmerge $vg2 $vg1 2>err
+grep "Duplicate logical volume name \"$lv1\" in \"$vg2\" and \"$vg1" err
+aux vg_validate_pvlv_counts_ $vg1 2 1 0
+aux vg_validate_pvlv_counts_ $vg2 2 1 0
+vgremove -f $vg1
+vgremove -f $vg2
+
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# 'Test vgmerge command options for validity'
+
+. ./test-utils.sh
+
+aux prepare_pvs 4
+
+# 'vgmerge normal operation'
+# ensure ordering does not matter
+vgcreate $vg1 $dev1 $dev2
+vgcreate $vg2 $dev3 $dev4
+vgmerge $vg1 $vg2
+vgremove $vg1
+vgcreate -c n $vg2 $dev1 $dev2
+vgcreate -c n $vg1 $dev3 $dev4
+vgmerge $vg2 $vg1
+vgremove $vg2
+
+# 'vgmerge rejects duplicate vg name'
+vgcreate $vg1 $dev1 $dev2
+vgcreate $vg2 $dev3 $dev4
+not vgmerge $vg1 $vg1 2>err
+grep "^ Duplicate volume group name \"$vg1\"\$" err
+vgremove $vg2
+vgremove $vg1
+
+# 'vgmerge rejects vgs with incompatible extent_size'
+vgcreate --physicalextentsize 4M $vg1 $dev1 $dev2
+vgcreate --physicalextentsize 8M $vg2 $dev3 $dev4
+not vgmerge $vg1 $vg2 2>err
+grep "^ Extent sizes differ" err
+vgremove $vg2
+vgremove $vg1
+
+# 'vgmerge rejects vgmerge because max_pv is exceeded'
+vgcreate --maxphysicalvolumes 2 $vg1 $dev1 $dev2
+vgcreate --maxphysicalvolumes 2 $vg2 $dev3 $dev4
+not vgmerge $vg1 $vg2 2>err
+grep "^ Maximum number of physical volumes (2) exceeded" err
+vgremove $vg2
+vgremove $vg1
+
+# 'vgmerge rejects vg with active lv'
+vgcreate $vg1 $dev1 $dev2
+vgcreate $vg2 $dev3 $dev4
+lvcreate -l 4 -n lv1 $vg2
+not vgmerge $vg1 $vg2 2>err
+grep "^ Logical volumes in \"$vg2\" must be inactive\$" err
+vgremove -f $vg2
+vgremove -f $vg1
+
+# 'vgmerge rejects vgmerge because max_lv is exceeded'
+vgcreate --maxlogicalvolumes 2 $vg1 $dev1 $dev2
+vgcreate --maxlogicalvolumes 2 $vg2 $dev3 $dev4
+lvcreate -l 4 -n lv1 $vg1
+lvcreate -l 4 -n lv2 $vg1
+lvcreate -l 4 -n lv3 $vg2
+vgchange -an $vg1
+vgchange -an $vg2
+not vgmerge $vg1 $vg2 2>err
+grep "^ Maximum number of logical volumes (2) exceeded" err
+vgremove -f $vg2
+vgremove -f $vg1
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_devs 4
+
+for mdatype in 1 2
+do
+ # setup PVs
+ pvcreate -M$mdatype $dev1 $dev2
+
+ # (lvm$mdatype) vgreduce removes only the specified pv from vg (bz427382)" '
+ vgcreate -c n -M$mdatype $vg1 $dev1 $dev2
+ vgreduce $vg1 $dev1
+ check_pv_field_ $dev2 vg_name $vg1
+ vgremove -f $vg1
+
+ # (lvm$mdatype) vgreduce rejects removing the last pv (--all)
+ vgcreate -c n -M$mdatype $vg1 $dev1 $dev2
+ not vgreduce --all $vg1
+ vgremove -f $vg1
+
+ # (lvm$mdatype) vgreduce rejects removing the last pv
+ vgcreate -c n -M$mdatype $vg1 $dev1 $dev2
+ not vgreduce $vg1 $dev1 $dev2
+ vgremove -f $vg1
+
+ pvremove -ff $dev1 $dev2
+done
+
+mdatype=2 # we only expect the following to work for lvm2 metadata
+
+# (lvm$mdatype) setup PVs (--metadatacopies 0)
+pvcreate -M$mdatype $dev1 $dev2
+pvcreate --metadatacopies 0 -M$mdatype $dev3 $dev4
+
+# (lvm$mdatype) vgreduce rejects removing pv with the last mda copy (bz247448)
+vgcreate -c n -M$mdatype $vg1 $dev1 $dev3
+not vgreduce $vg1 $dev1
+vgremove -f $vg1
+
+#COMM "(lvm$mdatype) vgreduce --removemissing --force repares to linear (bz221921)"
+# (lvm$mdatype) setup: create mirror & damage one pv
+vgcreate -c n -M$mdatype $vg1 $dev1 $dev2 $dev3
+lvcreate -n $lv1 -m1 -l 4 $vg1
+lvcreate -n $lv2 -l 4 $vg1 $dev2
+lvcreate -n $lv3 -l 4 $vg1 $dev3
+vgchange -an $vg1
+aux disable_dev $dev1
+# (lvm$mdatype) vgreduce --removemissing --force repares to linear
+vgreduce --removemissing --force $vg1
+check_lv_field_ $vg1/$lv1 segtype linear
+vg_validate_pvlv_counts_ $vg1 2 3 0
+# cleanup
+aux enable_dev $dev1
+vgremove -ff $vg1
+
+#COMM "vgreduce rejects --removemissing --mirrorsonly --force when nonmirror lv lost too"
+# (lvm$mdatype) setup: create mirror + linear lvs
+vgcreate -c n -M$mdatype $vg1 $devs
+lvcreate -n $lv2 -l 4 $vg1
+lvcreate -m1 -n $lv1 -l 4 $vg1 $dev1 $dev2 $dev3
+lvcreate -n $lv3 -l 4 $vg1 $dev3
+pvs --segments -o +lv_name # for record only
+# (lvm$mdatype) setup: damage one pv
+vgchange -an $vg1
+aux disable_dev $dev1
+#pvcreate -ff -y $dev1
+# vgreduce rejects --removemissing --mirrorsonly --force when nonmirror lv lost too
+not vgreduce -c n --removemissing --mirrorsonly --force $vg1
+
+aux enable_dev $dev1
+
+pvs -P # for record
+lvs -P # for record
+vgs -P # for record
--- /dev/null
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+aux prepare_devs 4
+pvcreate $dev1 $dev2
+pvcreate --metadatacopies 0 $dev3 $dev4
+
+# vgrename normal operation - rename vg1 to vg2
+# vgrename normal operation - rename vg2 to vg1
+# ensure name ordering does not matter
+vgcreate $vg1 $dev1 $dev2
+vgrename $vg1 $vg2
+check_vg_field_ $vg2 vg_name $vg2
+vgrename $vg2 $vg1
+check_vg_field_ $vg1 vg_name $vg1
+vgremove $vg1
+
+# vgrename by uuid (bz231187)
+vgcreate $vg1 $dev1 $dev3
+UUID=$(vgs --noheading -o vg_uuid $vg1)
+check_vg_field_ $vg1 vg_uuid $UUID
+vgrename $UUID $vg2
+check_vg_field_ $vg2 vg_name $vg2
+vgremove $vg2
+
+# vgrename fails - new vg already exists
+vgcreate $vg1 $dev1
+vgcreate $vg2 $dev2
+not vgrename $vg1 $vg2
+vgremove $vg1
+vgremove $vg2
+
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Test vgsplit operation, including different LV types
+
+. ./test-utils.sh
+
+COMM() {
+ LAST_TEST="$@"
+}
+
+prepare_pvs 5 258
+# FIXME: paramaterize lvm1 vs lvm2 metadata; most of these tests should run
+# fine with lvm1 metadata as well; for now, just add disks 5 and 6 as lvm1
+# metadata
+
+#
+# vgsplit can be done into a new or existing VG
+#
+for i in new existing
+do
+ #
+ # We can have PVs or LVs on the cmdline
+ #
+ for j in PV LV
+ do
+COMM "vgsplit correctly splits single linear LV into $i VG ($j args)"
+ vgcreate $vg1 $dev1 $dev2
+ if [ $i = existing ]; then
+ vgcreate $vg2 $dev3 $dev4
+ fi
+ lvcreate -l 4 -n $lv1 $vg1 $dev1
+ vgchange -an $vg1
+ if [ $j = PV ]; then
+ vgsplit $vg1 $vg2 $dev1
+ else
+ vgsplit -n $lv1 $vg1 $vg2
+ fi
+ vg_validate_pvlv_counts_ $vg1 1 0 0
+ if [ $i = existing ]; then
+ aux vg_validate_pvlv_counts_ $vg2 3 1 0
+ else
+ aux vg_validate_pvlv_counts_ $vg2 1 1 0
+ fi
+ lvremove -f $vg2/$lv1
+ vgremove -f $vg2
+ vgremove -f $vg1
+
+COMM "vgsplit correctly splits single striped LV into $i VG ($j args)"
+ vgcreate $vg1 $dev1 $dev2
+ if [ $i = existing ]; then
+ vgcreate $vg2 $dev3 $dev4
+ fi
+ lvcreate -l 4 -i 2 -n $lv1 $vg1 $dev1 $dev2
+ vgchange -an $vg1
+ if [ $j = PV ]; then
+ vgsplit $vg1 $vg2 $dev1 $dev2
+ else
+ vgsplit -n $lv1 $vg1 $vg2
+ fi
+ if [ $i = existing ]; then
+ aux vg_validate_pvlv_counts_ $vg2 4 1 0
+ else
+ aux vg_validate_pvlv_counts_ $vg2 2 1 0
+ fi
+ lvremove -f $vg2/$lv1
+ vgremove -f $vg2
+
+COMM "vgsplit correctly splits mirror LV into $i VG ($j args)"
+ vgcreate -c n $vg1 $dev1 $dev2 $dev3
+ if [ $i = existing ]; then
+ vgcreate -c n $vg2 $dev4
+ fi
+ lvcreate -l 64 -m1 -n $lv1 $vg1 $dev1 $dev2 $dev3
+ vgchange -an $vg1
+ if [ $j = PV ]; then
+ vgsplit $vg1 $vg2 $dev1 $dev2 $dev3
+ else
+ vgsplit -n $lv1 $vg1 $vg2
+ fi
+ if [ $i = existing ]; then
+ aux vg_validate_pvlv_counts_ $vg2 4 1 0
+ else
+ aux vg_validate_pvlv_counts_ $vg2 3 1 0
+ fi
+ lvremove -f $vg2/$lv1
+ vgremove -f $vg2
+
+COMM "vgsplit correctly splits origin and snapshot LV into $i VG ($j args)"
+ vgcreate -c n $vg1 $dev1 $dev2
+ if [ $i = existing ]; then
+ vgcreate -c n $vg2 $dev3 $dev4
+ fi
+ lvcreate -l 64 -i 2 -n $lv1 $vg1 $dev1 $dev2
+ lvcreate -l 4 -i 2 -s -n $lv2 $vg1/$lv1
+ vgchange -an $vg1
+ if [ $j = PV ]; then
+ vgsplit $vg1 $vg2 $dev1 $dev2
+ else
+ vgsplit -n $lv1 $vg1 $vg2
+ fi
+ if [ $i = existing ]; then
+ aux vg_validate_pvlv_counts_ $vg2 4 2 1
+ else
+ aux vg_validate_pvlv_counts_ $vg2 2 2 1
+ fi
+ lvremove -f $vg2/$lv2
+ lvremove -f $vg2/$lv1
+ vgremove -f $vg2
+
+COMM "vgsplit correctly splits linear LV but not snap+origin LV into $i VG ($j args)"
+ vgcreate -c n $vg1 $dev1 $dev2
+ if [ $i = existing ]; then
+ vgcreate -c n $vg2 $dev3
+ fi
+ lvcreate -l 64 -i 2 -n $lv1 $vg1
+ lvcreate -l 4 -i 2 -s -n $lv2 $vg1/$lv1
+ vgextend $vg1 $dev4
+ lvcreate -l 64 -n $lv3 $vg1 $dev4
+ vgchange -an $vg1
+ if [ $j = PV ]; then
+ vgsplit $vg1 $vg2 $dev4
+ else
+ vgsplit -n $lv3 $vg1 $vg2
+ fi
+ if [ $i = existing ]; then
+ aux vg_validate_pvlv_counts_ $vg2 2 1 0
+ aux vg_validate_pvlv_counts_ $vg1 2 2 1
+ else
+ aux vg_validate_pvlv_counts_ $vg2 1 1 0
+ aux vg_validate_pvlv_counts_ $vg1 2 2 1
+ fi
+ lvremove -f $vg1/$lv2
+ lvremove -f $vg1/$lv1
+ lvremove -f $vg2/$lv3
+ vgremove -f $vg1
+ vgremove -f $vg2
+
+COMM "vgsplit correctly splits linear LV but not mirror LV into $i VG ($j args)"
+ vgcreate -c n $vg1 $dev1 $dev2 $dev3
+ if [ $i = existing ]; then
+ vgcreate -c n $vg2 $dev5
+ fi
+ lvcreate -l 64 -m1 -n $lv1 $vg1 $dev1 $dev2 $dev3
+ vgextend $vg1 $dev4
+ lvcreate -l 64 -n $lv2 $vg1 $dev4
+ vgchange -an $vg1
+ vgs
+ lvs
+ pvs
+ if [ $j = PV ]; then
+ vgsplit $vg1 $vg2 $dev4
+ else
+ vgsplit -n $lv2 $vg1 $vg2
+ fi
+ if [ $i = existing ]; then
+ aux vg_validate_pvlv_counts_ $vg1 3 1 0
+ aux vg_validate_pvlv_counts_ $vg2 2 1 0
+ else
+ vgs
+ lvs
+ pvs
+ aux vg_validate_pvlv_counts_ $vg1 3 1 0
+ aux vg_validate_pvlv_counts_ $vg2 1 1 0
+ fi
+ lvremove -f $vg1/$lv1
+ lvremove -f $vg2/$lv2
+ vgremove -f $vg1
+ vgremove -f $vg2
+
+ done
+done
+
+#
+# Test more complex setups where the code has to find associated PVs and
+# LVs to split the VG correctly
+#
+COMM "vgsplit fails splitting 3 striped LVs into VG when only 1 LV specified"
+vgcreate $vg1 $dev1 $dev2 $dev3 $dev4
+lvcreate -l 4 -n $lv1 -i 2 $vg1 $dev1 $dev2
+lvcreate -l 4 -n $lv2 -i 2 $vg1 $dev2 $dev3
+lvcreate -l 4 -n $lv3 -i 2 $vg1 $dev3 $dev4
+vgchange -an $vg1
+not vgsplit -n $lv1 $vg1 $vg2
+vgremove -ff $vg1
+
+COMM "vgsplit fails splitting one LV with 2 snapshots, only origin LV specified"
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4
+lvcreate -l 16 -n $lv1 $vg1 $dev1 $dev2
+lvcreate -l 4 -n $lv2 -s $vg1/$lv1 $dev3
+lvcreate -l 4 -n $lv3 -s $vg1/$lv1 $dev4
+vg_validate_pvlv_counts_ $vg1 4 3 2
+vgchange -an $vg1
+not vgsplit -n $lv1 $vg1 $vg2;
+lvremove -f $vg1/$lv2
+lvremove -f $vg1/$lv3
+lvremove -f $vg1/$lv1
+vgremove -ff $vg1
+
+COMM "vgsplit fails splitting one LV with 2 snapshots, only snapshot LV specified"
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4
+lvcreate -l 16 -n $lv1 $vg1 $dev1 $dev2
+lvcreate -l 4 -n $lv2 -s $vg1/$lv1 $dev3
+lvcreate -l 4 -n $lv3 -s $vg1/$lv1 $dev4
+vg_validate_pvlv_counts_ $vg1 4 3 2
+vgchange -an $vg1
+not vgsplit -n $lv2 $vg1 $vg2
+lvremove -f $vg1/$lv2
+lvremove -f $vg1/$lv3
+lvremove -f $vg1/$lv1
+vgremove -ff $vg1
+
+COMM "vgsplit fails splitting one mirror LV, only one PV specified"
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4
+lvcreate -l 16 -n $lv1 -m1 $vg1 $dev1 $dev2 $dev3
+vg_validate_pvlv_counts_ $vg1 4 1 0
+vgchange -an $vg1
+not vgsplit $vg1 $vg2 $dev2
+vgremove -ff $vg1
+
+COMM "vgsplit fails splitting 1 mirror + 1 striped LV, only striped LV specified"
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4
+lvcreate -l 16 -n $lv1 -m1 $vg1 $dev1 $dev2 $dev3
+lvcreate -l 16 -n $lv2 -i 2 $vg1 $dev3 $dev4
+vg_validate_pvlv_counts_ $vg1 4 2 0
+vgchange -an $vg1
+not vgsplit -n $lv2 $vg1 $vg2 2>err
+vgremove -ff $vg1
+
+#
+# Verify vgsplit rejects active LVs only when active LVs involved in split
+#
+COMM "vgsplit fails, active mirror involved in split"
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4
+lvcreate -l 16 -n $lv1 -m1 $vg1 $dev1 $dev2 $dev3
+lvcreate -l 16 -n $lv2 $vg1 $dev4
+lvchange -an $vg1/$lv2
+vg_validate_pvlv_counts_ $vg1 4 2 0
+not vgsplit -n $lv1 $vg1 $vg2;
+vg_validate_pvlv_counts_ $vg1 4 2 0
+vgremove -ff $vg1
+
+COMM "vgsplit succeeds, active mirror not involved in split"
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4
+lvcreate -l 16 -n $lv1 -m1 $vg1 $dev1 $dev2 $dev3
+lvcreate -l 16 -n $lv2 $vg1 $dev4
+lvchange -an $vg1/$lv2
+vg_validate_pvlv_counts_ $vg1 4 2 0
+vgsplit -n $lv2 $vg1 $vg2
+vg_validate_pvlv_counts_ $vg1 3 1 0
+vg_validate_pvlv_counts_ $vg2 1 1 0
+vgremove -ff $vg1
+vgremove -ff $vg2
+
+COMM "vgsplit fails, active snapshot involved in split"
+vgcreate -c n $vg1 $dev1 $dev2 $dev3 $dev4
+lvcreate -l 64 -i 2 -n $lv1 $vg1 $dev1 $dev2
+lvcreate -l 4 -i 2 -s -n $lv2 $vg1/$lv1
+lvcreate -l 64 -i 2 -n $lv3 $vg1 $dev3 $dev4
+lvchange -an $vg1/$lv3
+vg_validate_pvlv_counts_ $vg1 4 3 1
+not vgsplit -n $lv2 $vg1 $vg2;
+vg_validate_pvlv_counts_ $vg1 4 3 1
+lvremove -f $vg1/$lv2
+vgremove -ff $vg1
+
+COMM "vgsplit succeeds, active snapshot not involved in split"
+vgcreate -c n $vg1 $dev1 $dev2 $dev3
+lvcreate -l 64 -i 2 -n $lv1 $vg1 $dev1 $dev2
+lvcreate -l 4 -s -n $lv2 $vg1/$lv1
+vgextend $vg1 $dev4
+lvcreate -l 64 -n $lv3 $vg1 $dev4
+lvchange -an $vg1/$lv3
+vg_validate_pvlv_counts_ $vg1 4 3 1
+vgsplit -n $lv3 $vg1 $vg2
+vg_validate_pvlv_counts_ $vg1 3 2 1
+vg_validate_pvlv_counts_ $vg2 1 1 0
+vgchange -an $vg1
+lvremove -f $vg1/$lv2
+vgremove -ff $vg1
+vgremove -ff $vg2
+
--- /dev/null
+# Copyright (C) 2010 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+. ./test-utils.sh
+
+prepare_lvmconf '[ "a/dev\/mirror/", "a/dev\/mapper\/.*$/", "a/dev\/LVMTEST/", "r/.*/" ]'
+aux prepare_devs 3
+
+pvcreate $devs
+vgcreate $vg1 $dev1 $dev2
+lvcreate -n $lv1 -l 100%FREE $vg1
+
+#top VG
+pvcreate $DM_DEV_DIR/$vg1/$lv1
+vgcreate $vg $DM_DEV_DIR/$vg1/$lv1 $dev3
+
+vgchange -a n $vg
+vgchange -a n $vg1
+
+# this should fail but not segfault, RHBZ 481793.
+not vgsplit $vg $vg1 $dev3
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2007-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Test vgsplit command options for validity
+
+. ./test-utils.sh
+
+aux prepare_devs 5
+
+for mdatype in 1 2
+do
+
+pvcreate -M$mdatype $devs
+
+# ensure name order does not matter
+# NOTE: if we're using lvm1, we must use -M on vgsplit
+vgcreate -M$mdatype $vg1 $devs
+vgsplit -M$mdatype $vg1 $vg2 $dev1
+vgremove $vg1
+vgremove $vg2
+vgcreate -M$mdatype $vg2 $devs
+vgsplit -M$mdatype $vg2 $vg1 $dev1
+vgremove $vg1
+vgremove $vg2
+
+# vgsplit accepts new vg as destination of split
+# lvm1 -- bz244792
+vgcreate -M$mdatype $vg1 $devs
+vgsplit $vg1 $vg2 $dev1 1>err
+grep "New volume group \"$vg2\" successfully split from \"$vg1\"" err
+vgremove $vg1
+vgremove $vg2
+
+# vgsplit accepts existing vg as destination of split
+vgcreate -M$mdatype $vg1 $dev1 $dev2
+vgcreate -M$mdatype $vg2 $dev3 $dev4
+vgsplit $vg1 $vg2 $dev1 1>err
+grep "Existing volume group \"$vg2\" successfully split from \"$vg1\"" err
+vgremove $vg1
+vgremove $vg2
+
+# vgsplit accepts --maxphysicalvolumes 128 on new VG
+vgcreate -M$mdatype $vg1 $dev1 $dev2
+vgsplit --maxphysicalvolumes 128 $vg1 $vg2 $dev1
+check_vg_field_ $vg2 max_pv 128
+vgremove $vg1
+vgremove $vg2
+
+# vgsplit accepts --maxlogicalvolumes 128 on new VG
+vgcreate -M$mdatype $vg1 $dev1 $dev2
+vgsplit --maxlogicalvolumes 128 $vg1 $vg2 $dev1
+check_vg_field_ $vg2 max_lv 128
+vgremove $vg1
+vgremove $vg2
+
+# vgsplit rejects split because max_pv of destination would be exceeded
+vgcreate -M$mdatype --maxphysicalvolumes 2 $vg1 $dev1 $dev2
+vgcreate -M$mdatype --maxphysicalvolumes 2 $vg2 $dev3 $dev4
+not vgsplit $vg1 $vg2 $dev1 2>err;
+grep "^ Maximum number of physical volumes (2) exceeded" err
+vgremove $vg2
+vgremove $vg1
+
+# vgsplit rejects split because maxphysicalvolumes given with existing vg
+vgcreate -M$mdatype --maxphysicalvolumes 2 $vg1 $dev1 $dev2
+vgcreate -M$mdatype --maxphysicalvolumes 2 $vg2 $dev3 $dev4
+not vgsplit --maxphysicalvolumes 2 $vg1 $vg2 $dev1 2>err;
+grep "^ Volume group \"$vg2\" exists, but new VG option specified" err
+vgremove $vg2
+vgremove $vg1
+
+# vgsplit rejects split because maxlogicalvolumes given with existing vg
+vgcreate -M$mdatype --maxlogicalvolumes 2 $vg1 $dev1 $dev2
+vgcreate -M$mdatype --maxlogicalvolumes 2 $vg2 $dev3 $dev4
+not vgsplit --maxlogicalvolumes 2 $vg1 $vg2 $dev1 2>err
+grep "^ Volume group \"$vg2\" exists, but new VG option specified" err
+vgremove $vg2
+vgremove $vg1
+
+# vgsplit rejects split because alloc given with existing vg
+vgcreate -M$mdatype --alloc cling $vg1 $dev1 $dev2
+vgcreate -M$mdatype --alloc cling $vg2 $dev3 $dev4
+not vgsplit --alloc cling $vg1 $vg2 $dev1 2>err;
+grep "^ Volume group \"$vg2\" exists, but new VG option specified" err
+vgremove $vg2
+vgremove $vg1
+
+# vgsplit rejects split because clustered given with existing vg
+vgcreate -M$mdatype --clustered n $vg1 $dev1 $dev2
+vgcreate -M$mdatype --clustered n $vg2 $dev3 $dev4
+not vgsplit --clustered n $vg1 $vg2 $dev1 2>err
+grep "^ Volume group \"$vg2\" exists, but new VG option specified" err
+vgremove $vg2
+vgremove $vg1
+
+# vgsplit rejects vg with active lv
+pvcreate -M$mdatype -ff $dev3 $dev4
+vgcreate -M$mdatype $vg1 $dev1 $dev2
+vgcreate -M$mdatype $vg2 $dev3 $dev4
+lvcreate -l 4 -n $lv1 $vg1
+not vgsplit $vg1 $vg2 $dev1 2>err;
+grep "^ Logical volumes in \"$vg1\" must be inactive\$" err
+vgremove -f $vg2
+vgremove -f $vg1
+
+# vgsplit rejects split because max_lv is exceeded
+vgcreate -M$mdatype --maxlogicalvolumes 2 $vg1 $dev1 $dev2
+vgcreate -M$mdatype --maxlogicalvolumes 2 $vg2 $dev3 $dev4
+lvcreate -l 4 -n $lv1 $vg1
+lvcreate -l 4 -n $lv2 $vg1
+lvcreate -l 4 -n $lv3 $vg2
+vgchange -an $vg1
+vgchange -an $vg2
+not vgsplit $vg1 $vg2 $dev1 2>err;
+grep "^ Maximum number of logical volumes (2) exceeded" err
+vgremove -f $vg2
+vgremove -f $vg1
+
+# vgsplit verify default - max_lv attribute from new VG is same as source VG" \
+vgcreate -M$mdatype $vg1 $dev1 $dev2
+lvcreate -l 4 -n $lv1 $vg1
+vgchange -an $vg1
+vgsplit $vg1 $vg2 $dev1
+compare_vg_field_ $vg1 $vg2 max_lv
+vgremove -f $vg2
+vgremove -f $vg1
+
+# vgsplit verify default - max_pv attribute from new VG is same as source VG" \
+vgcreate -M$mdatype $vg1 $dev1 $dev2
+lvcreate -l 4 -n $lv1 $vg1
+vgchange -an $vg1
+vgsplit $vg1 $vg2 $dev1
+compare_vg_field_ $vg1 $vg2 max_pv
+vgremove -f $vg2
+vgremove -f $vg1
+
+# vgsplit verify default - vg_fmt attribute from new VG is same as source VG" \
+vgcreate -M$mdatype $vg1 $dev1 $dev2
+lvcreate -l 4 -n $lv1 $vg1
+vgchange -an $vg1
+vgsplit $vg1 $vg2 $dev1
+compare_vg_field_ $vg1 $vg2 vg_fmt
+vgremove -f $vg2
+vgremove -f $vg1
+
+# vgsplit rejects split because PV not in VG
+vgcreate -M$mdatype $vg1 $dev1 $dev2
+vgcreate -M$mdatype $vg2 $dev3 $dev4
+lvcreate -l 4 -n $lv1 $vg1
+lvcreate -l 4 -n $lv2 $vg1
+vgchange -an $vg1
+not vgsplit $vg1 $vg2 $dev3 2>err;
+vgremove -f $vg2
+vgremove -f $vg1
+done
+
+# ONLY LVM2 metadata
+# setup PVs" '
+pvcreate --metadatacopies 0 $dev5
+
+# vgsplit rejects to give away pv with the last mda copy
+vgcreate $vg1 $dev5 $dev2
+lvcreate -l 10 -n $lv1 $vg1
+lvchange -an $vg1/$lv1
+vg_validate_pvlv_counts_ $vg1 2 1 0
+not vgsplit $vg1 $vg2 $dev5;
+vg_validate_pvlv_counts_ $vg1 2 1 0
+vgremove -ff $vg1
+
+# vgsplit rejects split because metadata types differ
+pvcreate -ff -M1 $dev3 $dev4
+pvcreate -ff $dev1 $dev2
+vgcreate -M1 $vg1 $dev3 $dev4
+vgcreate $vg2 $dev1 $dev2
+not vgsplit $vg1 $vg2 $dev3 2>err;
+grep "^ Metadata types differ" err
+vgremove $vg2
+vgremove $vg1
+
--- /dev/null
+# Copyright (C) 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+aux() {
+ # use just "$@" for verbose operation
+ "$@" > /dev/null 2> /dev/null
+ #"$@"
+}
+
+STACKTRACE() {
+ trap - ERR;
+ i=0;
+
+ while FUNC=${FUNCNAME[$i]}; test "$FUNC" != "main"; do
+ echo "$i ${FUNC}() called from ${BASH_SOURCE[$i]}:${BASH_LINENO[$i]}"
+ i=$(($i + 1));
+ done
+
+ # Get backtraces from coredumps
+ if which gdb >& /dev/null; then
+ echo bt full > gdb_commands.txt
+ echo l >> gdb_commands.txt
+ echo quit >> gdb_commands.txt
+ for core in `ls core* 2>/dev/null`; do
+ bin=$(gdb -batch -c $core 2>&1 | grep "generated by" | \
+ sed -e "s,.*generated by \`\([^ ']*\).*,\1,")
+ gdb -batch -c $core -x gdb_commands.txt `which $bin`
+ done
+ fi
+
+ test -f debug.log && {
+ sed -e "s,^,## DEBUG: ,;s,$top_srcdir/\?,," < debug.log
+ }
+}
+
+init_udev_transaction() {
+ if test "$DM_UDEV_SYNCHRONISATION" = 1; then
+ COOKIE=$(dmsetup udevcreatecookie)
+ # Cookie is not generated if udev is not running!
+ if test -n "$COOKIE"; then
+ export DM_UDEV_COOKIE=$COOKIE
+ fi
+ fi
+}
+
+finish_udev_transaction() {
+ if test "$DM_UDEV_SYNCHRONISATION" = 1 -a -n "$DM_UDEV_COOKIE"; then
+ dmsetup udevreleasecookie
+ unset DM_UDEV_COOKIE
+ fi
+}
+
+prepare_clvmd() {
+ if test -z "$LVM_TEST_LOCKING" || test "$LVM_TEST_LOCKING" -ne 3 ; then
+ return 0 # not needed
+ fi
+
+ if pgrep clvmd ; then
+ echo "Cannot use fake cluster locking with real clvmd ($(pgrep clvmd)) running."
+ exit 200
+ fi
+
+ # skip if we don't have our own clvmd...
+ (which clvmd | grep $abs_builddir) || exit 200
+
+ # skip if we singlenode is not compiled in
+ (clvmd --help 2>&1 | grep "Available cluster managers" | grep singlenode) || exit 200
+
+ trap_teardown
+
+ clvmd -Isinglenode -d 1 &
+ LOCAL_CLVMD="$!"
+
+ # check that it is really running now
+ sleep .1
+ ps $LOCAL_CLVMD || exit 200
+}
+
+prepare_dmeventd() {
+ if pgrep dmeventd ; then
+ echo "Cannot test dmeventd with real dmeventd ($(pgrep dmeventd)) running."
+ exit 200
+ fi
+
+ # skip if we don't have our own dmeventd...
+ (which dmeventd | grep $abs_builddir) || exit 200
+
+ trap_teardown
+
+ dmeventd -f &
+ LOCAL_DMEVENTD="$!"
+}
+
+prepare_testroot() {
+ OLDPWD="`pwd`"
+ PREFIX="LVMTEST$$"
+
+ trap_teardown
+ TESTDIR=$($abs_srcdir/mkdtemp ${LVM_TEST_DIR-$(pwd)} $PREFIX.XXXXXXXXXX) \
+ || { echo "failed to create temporary directory in ${LVM_TEST_DIR-$(pwd)}"; exit 1; }
+
+ export LVM_SYSTEM_DIR=$TESTDIR/etc
+ export DM_DEV_DIR=$TESTDIR/dev
+ mkdir $LVM_SYSTEM_DIR $DM_DEV_DIR $DM_DEV_DIR/mapper $TESTDIR/lib
+
+ cd $TESTDIR
+
+ for i in `find $abs_top_builddir/daemons/dmeventd/plugins/ -name \*.so`; do
+ echo Setting up symlink from $i to $TESTDIR/lib
+ ln -s $i $TESTDIR/lib
+ done
+}
+
+teardown_devs() {
+ test -n "$PREFIX" && {
+ rm -rf $TESTDIR/dev/$PREFIX*
+
+ init_udev_transaction
+ while dmsetup table | grep -q ^$PREFIX; do
+ for s in `dmsetup info -c -o name --noheading | grep ^$PREFIX`; do
+ umount -fl $DM_DEV_DIR/mapper/$s || true
+ dmsetup remove $s >& /dev/null || true
+ done
+ done
+ finish_udev_transaction
+
+ }
+
+ # NOTE: SCSI_DEBUG_DEV test must come before the LOOP test because
+ # prepare_scsi_debug_dev() also sets LOOP to short-circuit prepare_loop()
+ if [ -n "$SCSI_DEBUG_DEV" ] ; then
+ modprobe -r scsi_debug
+ else
+ test -n "$LOOP" && losetup -d $LOOP
+ test -n "$LOOPFILE" && rm -f $LOOPFILE
+ fi
+ unset devs # devs is set in prepare_devs()
+ unset LOOP
+}
+
+teardown() {
+ echo $LOOP
+ echo $PREFIX
+
+ test -n "$LOCAL_CLVMD" && {
+ kill "$LOCAL_CLVMD"
+ sleep .1
+ kill -9 "$LOCAL_CLVMD" || true
+ }
+
+ test -n "$LOCAL_DMEVENTD" && kill -9 "$LOCAL_DMEVENTD"
+
+ teardown_devs
+
+ test -n "$TESTDIR" && {
+ cd $OLDPWD
+ rm -rf $TESTDIR || echo BLA
+ }
+}
+
+trap_teardown() {
+ trap 'set +vx; STACKTRACE; set -vx' ERR
+ trap 'aux teardown' EXIT # don't forget to clean up
+}
+
+make_ioerror() {
+ echo 0 10000000 error | dmsetup create ioerror
+ ln -s $DM_DEV_DIR/mapper/ioerror $DM_DEV_DIR/ioerror
+}
+
+prepare_loop() {
+ size=$1
+ test -n "$size" || size=32
+
+ # skip if prepare_scsi_debug_dev() was used
+ if [ -n "$SCSI_DEBUG_DEV" -a -n "$LOOP" ]; then
+ return 0
+ fi
+
+ test -z "$LOOP"
+ test -n "$DM_DEV_DIR"
+
+ trap_teardown
+
+ for i in 0 1 2 3 4 5 6 7; do
+ test -e $DM_DEV_DIR/loop$i || mknod $DM_DEV_DIR/loop$i b 7 $i
+ done
+
+ LOOPFILE="$PWD/test.img"
+ dd if=/dev/zero of="$LOOPFILE" bs=$((1024*1024)) count=0 seek=$(($size-1))
+ if LOOP=`losetup -s -f "$LOOPFILE" 2>/dev/null`; then
+ return 0
+ elif LOOP=`losetup -f` && losetup $LOOP "$LOOPFILE"; then
+ # no -s support
+ return 0
+ else
+ # no -f support
+ # Iterate through $DM_DEV_DIR/loop{,/}{0,1,2,3,4,5,6,7}
+ for slash in '' /; do
+ for i in 0 1 2 3 4 5 6 7; do
+ local dev=$DM_DEV_DIR/loop$slash$i
+ ! losetup $dev >/dev/null 2>&1 || continue
+ # got a free
+ losetup "$dev" "$LOOPFILE"
+ LOOP=$dev
+ break
+ done
+ if [ -n "$LOOP" ]; then
+ break
+ fi
+ done
+ test -n "$LOOP" # confirm or fail
+ return 0
+ fi
+ exit 1 # should not happen
+}
+
+# A drop-in replacement for prepare_loop() that uses scsi_debug to create
+# a ramdisk-based SCSI device upon which all LVM devices will be created
+# - scripts must take care not to use a DEV_SIZE that will enduce OOM-killer
+prepare_scsi_debug_dev()
+{
+ local DEV_SIZE="$1"
+ shift
+ local SCSI_DEBUG_PARAMS="$@"
+
+ test -n "$SCSI_DEBUG_DEV" && return 0
+ test -z "$LOOP"
+ test -n "$DM_DEV_DIR"
+
+ trap_teardown
+
+ # Skip test if awk isn't available (required for get_sd_devs_)
+ which awk || exit 200
+
+ # Skip test if scsi_debug module is unavailable or is already in use
+ modprobe --dry-run scsi_debug || exit 200
+ lsmod | grep -q scsi_debug && exit 200
+
+ # Create the scsi_debug device and determine the new scsi device's name
+ # NOTE: it will _never_ make sense to pass num_tgts param;
+ # last param wins.. so num_tgts=1 is imposed
+ modprobe scsi_debug dev_size_mb=$DEV_SIZE $SCSI_DEBUG_PARAMS num_tgts=1 || exit 200
+ sleep 2 # allow for async Linux SCSI device registration
+
+ local DEBUG_DEV=/dev/$(grep -H scsi_debug /sys/block/*/device/model | cut -f4 -d /)
+ [ -b $DEBUG_DEV ] || exit 1 # should not happen
+
+ # Create symlink to scsi_debug device in $DM_DEV_DIR
+ SCSI_DEBUG_DEV=$DM_DEV_DIR/$(basename $DEBUG_DEV)
+ # Setting $LOOP provides means for prepare_devs() override
+ LOOP=$SCSI_DEBUG_DEV
+ ln -snf $DEBUG_DEV $SCSI_DEBUG_DEV
+ return 0
+}
+
+cleanup_scsi_debug_dev()
+{
+ aux teardown_devs
+ unset SCSI_DEBUG_DEV
+ unset LOOP
+}
+
+prepare_devs() {
+ local n="$1"
+ test -z "$n" && n=3
+ local devsize="$2"
+ test -z "$devsize" && devsize=34
+ local pvname="$3"
+ test -z "$pvname" && pvname="pv"
+
+ prepare_loop $(($n*$devsize))
+
+ if ! loopsz=`blockdev --getsz $LOOP 2>/dev/null`; then
+ loopsz=`blockdev --getsize $LOOP 2>/dev/null`
+ fi
+
+ local size=$(($loopsz/$n))
+
+ init_udev_transaction
+ for i in `seq 1 $n`; do
+ local name="${PREFIX}$pvname$i"
+ local dev="$DM_DEV_DIR/mapper/$name"
+ eval "dev$i=$dev"
+ devs="$devs $dev"
+ echo 0 $size linear $LOOP $((($i-1)*$size)) > $name.table
+ dmsetup create $name $name.table
+ done
+ finish_udev_transaction
+
+ for i in `seq 1 $n`; do
+ local name="${PREFIX}$pvname$i"
+ dmsetup info -c $name
+ done
+ for i in `seq 1 $n`; do
+ local name="${PREFIX}$pvname$i"
+ dmsetup table $name
+ done
+}
+
+disable_dev() {
+
+ init_udev_transaction
+ for dev in "$@"; do
+ # first we make the device inaccessible
+ echo 0 10000000 error | dmsetup load $dev
+ dmsetup resume $dev
+ # now let's try to get rid of it if it's unused
+ #dmsetup remove $dev
+ done
+ finish_udev_transaction
+
+}
+
+enable_dev() {
+
+ init_udev_transaction
+ for dev in "$@"; do
+ local name=`echo "$dev" | sed -e 's,.*/,,'`
+ dmsetup create $name $name.table || dmsetup load $name $name.table
+ dmsetup resume $dev
+ done
+ finish_udev_transaction
+}
+
+backup_dev() {
+ for dev in "$@"; do
+ dd if=$dev of=$dev.backup bs=1024
+ done
+}
+
+restore_dev() {
+ for dev in "$@"; do
+ test -e $dev.backup || {
+ echo "Internal error: $dev not backed up, can't restore!"
+ exit 1
+ }
+ dd of=$dev if=$dev.backup bs=1024
+ done
+}
+
+prepare_pvs() {
+ prepare_devs "$@"
+ pvcreate -ff $devs
+}
+
+prepare_vg() {
+ vgremove -ff $vg || true
+ teardown_devs
+
+ prepare_pvs "$@"
+ vgcreate -c n $vg $devs
+ pvs -v
+}
+
+prepare_lvmconf() {
+ local filter="$1"
+ test -z "$filter" && \
+ filter='[ "a/dev\/mirror/", "a/dev\/mapper\/.*pv[0-9_]*$/", "r/.*/" ]'
+ locktype=
+ if test -z "$LVM_TEST_CONFIG_SNAPSHOT_AUTOEXTEND"; then
+ LVM_TEST_CONFIG_SNAPSHOT_AUTOEXTEND="
+ snapshot_autoextend_percent = 50
+ snapshot_autoextend_threshold = 50"
+ fi
+ if test -n "$LVM_TEST_LOCKING"; then locktype="locking_type = $LVM_TEST_LOCKING"; fi
+ cat > $TESTDIR/etc/lvm.conf.new <<-EOF
+ $LVM_TEST_CONFIG
+ devices {
+ dir = "$DM_DEV_DIR"
+ scan = "$DM_DEV_DIR"
+ filter = $filter
+ cache_dir = "$TESTDIR/etc"
+ sysfs_scan = 0
+ default_data_alignment = 1
+ $LVM_TEST_CONFIG_DEVICES
+ }
+ log {
+ syslog = 0
+ indent = 1
+ level = 9
+ file = "$TESTDIR/debug.log"
+ overwrite = 1
+ activation = 1
+ }
+ backup {
+ backup = 0
+ archive = 0
+ }
+ global {
+ abort_on_internal_errors = 1
+ library_dir = "$TESTDIR/lib"
+ locking_dir = "$TESTDIR/var/lock/lvm"
+ $locktype
+ si_unit_consistency = 1
+ fallback_to_local_locking = 0
+ }
+ activation {
+ udev_sync = 1
+ udev_rules = 1
+ polling_interval = 0
+ $LVM_TEST_CONFIG_SNAPSHOT_AUTOEXTEND
+ }
+EOF
+ # FIXME remove this workaround after mmap & truncating file problems solved
+ mv -f $TESTDIR/etc/lvm.conf.new $TESTDIR/etc/lvm.conf
+ cat $TESTDIR/etc/lvm.conf
+}
+
+prepare() {
+ ulimit -c unlimited
+ # FIXME any way to set this just for our children?
+ # echo 1 > /proc/sys/kernel/core_uses_pid
+ prepare_testroot
+ prepare_lvmconf
+ prepare_clvmd
+
+ # set up some default names
+ vg=${PREFIX}vg
+ vg1=${PREFIX}vg1
+ vg2=${PREFIX}vg2
+ lv=LV
+ lv1=LV1
+ lv2=LV2
+ lv3=LV3
+ lv4=LV4
+}
+
+apitest() {
+ t=$1
+ shift
+ test -x $abs_top_builddir/test/api/$t.t || exit 200
+ $abs_top_builddir/test/api/$t.t "$@"
+}
+
+api() {
+ test -x $abs_top_builddir/test/api/wrapper || exit 200
+ $abs_top_builddir/test/api/wrapper "$@"
+}
+
+LANG=C
+LC_ALL=C
+TZ=UTC
+unset CDPATH
+
+. ./init.sh || { echo >&2 you must run make first; exit 1; }
+. ./lvm-utils.sh
+
+set -vexE -o pipefail
+aux prepare
--- /dev/null
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES =\
+ dumpconfig.c \
+ formats.c \
+ lvchange.c \
+ lvconvert.c \
+ lvcreate.c \
+ lvdisplay.c \
+ lvextend.c \
+ lvmchange.c \
+ lvmcmdline.c \
+ lvmdiskscan.c \
+ lvreduce.c \
+ lvremove.c \
+ lvrename.c \
+ lvresize.c \
+ lvscan.c \
+ polldaemon.c \
+ pvchange.c \
+ pvck.c \
+ pvcreate.c \
+ pvdisplay.c \
+ pvmove.c \
+ pvremove.c \
+ pvresize.c \
+ pvscan.c \
+ reporter.c \
+ segtypes.c \
+ toollib.c \
+ vgcfgbackup.c \
+ vgcfgrestore.c \
+ vgchange.c \
+ vgck.c \
+ vgcreate.c \
+ vgconvert.c \
+ vgdisplay.c \
+ vgexport.c \
+ vgextend.c \
+ vgimport.c \
+ vgmerge.c \
+ vgmknodes.c \
+ vgreduce.c \
+ vgremove.c \
+ vgrename.c \
+ vgscan.c \
+ vgsplit.c
+
+SOURCES2 =\
+ dmsetup.c \
+ lvm.c \
+ lvm2cmd-static.c \
+ lvm2cmd.c \
+ lvmcmdlib.c
+
+TARGETS =\
+ .commands \
+ liblvm2cmd.a \
+ lvm
+
+TARGETS_DM = dmsetup
+
+INSTALL_LVM_TARGETS = install_tools_dynamic
+INSTALL_DMSETUP_TARGETS = install_dmsetup_dynamic
+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
+
+LVMLIBS = $(LVMINTERNAL_LIBS)
+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
+
+ifeq ("@CMDLIB@", "yes")
+ TARGETS += liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION)
+ INSTALL_LVM_TARGETS += $(INSTALL_CMDLIB_TARGETS)
+endif
+
+ifeq ("@DMEVENTD@", "yes")
+ LVMLIBS += -ldevmapper-event
+endif
+
+LVMLIBS += -ldevmapper
+
+EXPORTED_HEADER = $(srcdir)/lvm2cmd.h
+EXPORTED_FN_PREFIX = lvm2
+
+DEFS += -DLVM_SHARED_PATH=\"$(exec_prefix)/sbin/lvm\"
+
+CFLOW_LIST = lvmcmdlib.c lvm2cmd.c
+CFLOW_LIST_TARGET = liblvm2cmd.cflow
+CFLOW_TARGET = lvm
+
+include $(top_builddir)/make.tmpl
+
+LIBS += $(UDEV_LIBS)
+
+device-mapper: $(TARGETS_DM)
+
+dmsetup: dmsetup.o $(top_builddir)/libdm/libdevmapper.$(LIB_SUFFIX)
+ $(CC) $(CFLAGS) $(LDFLAGS) -L$(top_builddir)/libdm \
+ -o $@ dmsetup.o -ldevmapper $(LIBS)
+
+dmsetup.static: dmsetup.o $(interfacebuilddir)/libdevmapper.a
+ $(CC) $(CFLAGS) $(LDFLAGS) -static -L$(interfacebuilddir) \
+ -o $@ dmsetup.o -ldevmapper $(STATIC_LIBS) $(LIBS)
+
+all: device-mapper
+
+lvm: $(OBJECTS) lvm.o $(top_builddir)/lib/liblvm-internal.a
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) lvm.o \
+ $(LVMLIBS) $(READLINE_LIBS) $(LIBS) -rdynamic
+
+lvm.static: $(OBJECTS) lvm-static.o $(top_builddir)/lib/liblvm-internal.a $(interfacebuilddir)/libdevmapper.a
+ $(CC) $(CFLAGS) $(LDFLAGS) -static -L$(interfacebuilddir) -o $@ \
+ $(OBJECTS) lvm-static.o $(LVMLIBS) $(STATIC_LIBS) $(LIBS)
+
+liblvm2cmd.a: $(top_builddir)/lib/liblvm-internal.a $(OBJECTS) lvmcmdlib.o lvm2cmd.o
+ cat $(top_builddir)/lib/liblvm-internal.a > $@
+ $(AR) rs $@ $(OBJECTS) lvmcmdlib.o lvm2cmd.o
+
+liblvm2cmd-static.a: $(top_builddir)/lib/liblvm-internal.a $(OBJECTS) lvmcmdlib.o lvm2cmd-static.o
+ cat $(top_builddir)/lib/liblvm-internal.a > $@
+ $(AR) rs $@ $(OBJECTS) lvmcmdlib.o lvm2cmd-static.o
+
+liblvm2cmd.$(LIB_SUFFIX): liblvm2cmd.a $(LDDEPS)
+ $(CC) -shared -Wl,-soname,$@.$(LIB_VERSION) \
+ $(CFLAGS) $(CLDFLAGS) -o $@ \
+ @CLDWHOLEARCHIVE@ liblvm2cmd.a @CLDNOWHOLEARCHIVE@ \
+ $(LVMLIBS) $(LIBS)
+
+liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION): liblvm2cmd.$(LIB_SUFFIX)
+ $(LN_S) -f $< $@
+
+.commands: $(srcdir)/commands.h $(srcdir)/cmdnames.h Makefile
+ $(CC) -E -P $(srcdir)/cmdnames.h 2> /dev/null | \
+ egrep -v '^ *(|#.*|dumpconfig|formats|help|pvdata|segtypes|version) *$$' > .commands
+
+ifneq ("$(CFLOW_CMD)", "")
+CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
+-include $(top_builddir)/libdm/libdevmapper.cflow
+-include $(top_builddir)/lib/liblvm-internal.cflow
+endif
+
+.PHONY: install_cmdlib_dynamic install_cmdlib_static install_cmdlib_include \
+ install_tools_dynamic install_tools_static \
+ install_dmsetup_dynamic install_dmsetup_static
+
+install_cmdlib_include: $(srcdir)/lvm2cmd.h
+ $(INSTALL_DATA) -D $< $(includedir)/$(<F)
+
+install_cmdlib_dynamic: liblvm2cmd.$(LIB_SUFFIX)
+ $(INSTALL_PROGRAM) -D $< $(libdir)/$(<F).$(LIB_VERSION)
+ $(INSTALL_DIR) $(usrlibdir)
+ $(LN_S) -f $(USRLIB_RELPATH)$(<F).$(LIB_VERSION) $(usrlibdir)/$(<F)
+
+install_cmdlib_static: liblvm2cmd-static.a
+ $(INSTALL_DATA) -D $< $(usrlibdir)/liblvm2cmd.a
+
+install_tools_dynamic: lvm .commands
+ $(INSTALL_PROGRAM) -D lvm $(sbindir)/lvm
+ @echo Creating symbolic links for individual commands in $(sbindir)
+ @for v in `cat .commands`; do \
+ echo "$(LN_S) -f lvm $(sbindir)/$$v"; \
+ $(LN_S) -f lvm $(sbindir)/$$v; \
+ done;
+
+install_tools_static: lvm.static
+ $(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
+
+install_dmsetup_dynamic: dmsetup
+ $(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
+
+install_dmsetup_static: dmsetup.static
+ $(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
+
+install_device-mapper: $(INSTALL_DMSETUP_TARGETS)
+
+install_lvm2: $(INSTALL_LVM_TARGETS)
+
+install: install_lvm2 install_device-mapper
+
+DISTCLEAN_TARGETS += .exported_symbols_generated
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Put all long args that don't have a corresponding short option first.
+ */
+/* *INDENT-OFF* */
+arg(version_ARG, '\0', "version", NULL, 0)
+arg(quiet_ARG, '\0', "quiet", NULL, 0)
+arg(physicalvolumesize_ARG, '\0', "setphysicalvolumesize", size_mb_arg, 0)
+arg(ignorelockingfailure_ARG, '\0', "ignorelockingfailure", NULL, 0)
+arg(nolocking_ARG, '\0', "nolocking", NULL, 0)
+arg(pvmetadatacopies_ARG, '\0', "pvmetadatacopies", int_arg, 0)
+arg(vgmetadatacopies_ARG, '\0', "vgmetadatacopies", metadatacopies_arg, 0)
+arg(metadatacopies_ARG, '\0', "metadatacopies", metadatacopies_arg, 0)
+arg(metadatasize_ARG, '\0', "metadatasize", size_mb_arg, 0)
+arg(metadataignore_ARG, '\0', "metadataignore", yes_no_arg, 0)
+arg(norestorefile_ARG, '\0', "norestorefile", NULL, 0)
+arg(restorefile_ARG, '\0', "restorefile", string_arg, 0)
+arg(labelsector_ARG, '\0', "labelsector", int_arg, 0)
+arg(driverloaded_ARG, '\0', "driverloaded", yes_no_arg, 0)
+arg(aligned_ARG, '\0', "aligned", NULL, 0)
+arg(unbuffered_ARG, '\0', "unbuffered", NULL, 0)
+arg(noheadings_ARG, '\0', "noheadings", NULL, 0)
+arg(segments_ARG, '\0', "segments", NULL, 0)
+arg(units_ARG, '\0', "units", string_arg, 0)
+arg(nosuffix_ARG, '\0', "nosuffix", NULL, 0)
+arg(removemissing_ARG, '\0', "removemissing", NULL, 0)
+arg(restoremissing_ARG, '\0', "restoremissing", NULL, 0)
+arg(abort_ARG, '\0', "abort", NULL, 0)
+arg(addtag_ARG, '\0', "addtag", tag_arg, ARG_GROUPABLE)
+arg(deltag_ARG, '\0', "deltag", tag_arg, ARG_GROUPABLE)
+arg(refresh_ARG, '\0', "refresh", NULL, 0)
+arg(mknodes_ARG, '\0', "mknodes", NULL, 0)
+arg(minor_ARG, '\0', "minor", minor_arg, 0)
+arg(type_ARG, '\0', "type", segtype_arg, 0)
+arg(alloc_ARG, '\0', "alloc", alloc_arg, 0)
+arg(separator_ARG, '\0', "separator", string_arg, 0)
+arg(mirrorsonly_ARG, '\0', "mirrorsonly", NULL, 0)
+arg(nosync_ARG, '\0', "nosync", NULL, 0)
+arg(resync_ARG, '\0', "resync", NULL, 0)
+arg(corelog_ARG, '\0', "corelog", NULL, 0)
+arg(mirrorlog_ARG, '\0', "mirrorlog", string_arg, 0)
+arg(splitmirrors_ARG, '\0', "splitmirrors", int_arg, 0)
+arg(repair_ARG, '\0', "repair", NULL, 0)
+arg(use_policies_ARG, '\0', "use-policies", NULL, 0)
+arg(monitor_ARG, '\0', "monitor", yes_no_arg, 0)
+arg(config_ARG, '\0', "config", string_arg, 0)
+arg(trustcache_ARG, '\0', "trustcache", NULL, 0)
+arg(ignoremonitoring_ARG, '\0', "ignoremonitoring", NULL, 0)
+arg(nameprefixes_ARG, '\0', "nameprefixes", NULL, 0)
+arg(unquoted_ARG, '\0', "unquoted", NULL, 0)
+arg(rows_ARG, '\0', "rows", NULL, 0)
+arg(dataalignment_ARG, '\0', "dataalignment", size_kb_arg, 0)
+arg(dataalignmentoffset_ARG, '\0', "dataalignmentoffset", size_kb_arg, 0)
+arg(virtualoriginsize_ARG, '\0', "virtualoriginsize", size_mb_arg, 0)
+arg(virtualsize_ARG, '\0', "virtualsize", size_mb_arg, 0)
+arg(noudevsync_ARG, '\0', "noudevsync", NULL, 0)
+arg(poll_ARG, '\0', "poll", yes_no_arg, 0)
+arg(stripes_long_ARG, '\0', "stripes", int_arg, 0)
+arg(sysinit_ARG, '\0', "sysinit", NULL, 0)
+
+/* Allow some variations */
+arg(resizable_ARG, '\0', "resizable", yes_no_arg, 0)
+arg(allocation_ARG, '\0', "allocation", yes_no_arg, 0)
+
+/*
+ * ... and now the short args.
+ */
+arg(available_ARG, 'a', "available", yes_no_excl_arg, 0)
+arg(all_ARG, 'a', "all", NULL, 0)
+arg(autobackup_ARG, 'A', "autobackup", yes_no_arg, 0)
+arg(activevolumegroups_ARG, 'A', "activevolumegroups", NULL, 0)
+arg(background_ARG, 'b', "background", NULL, 0)
+arg(blockdevice_ARG, 'b', "blockdevice", NULL, 0)
+arg(chunksize_ARG, 'c', "chunksize", size_kb_arg, 0)
+arg(clustered_ARG, 'c', "clustered", yes_no_arg, 0)
+arg(colon_ARG, 'c', "colon", NULL, 0)
+arg(columns_ARG, 'C', "columns", NULL, 0)
+arg(contiguous_ARG, 'C', "contiguous", yes_no_arg, 0)
+arg(debug_ARG, 'd', "debug", NULL, ARG_COUNTABLE)
+arg(exported_ARG, 'e', "exported", NULL, 0)
+arg(physicalextent_ARG, 'E', "physicalextent", NULL, 0)
+arg(file_ARG, 'f', "file", string_arg, 0)
+arg(force_ARG, 'f', "force", NULL, ARG_COUNTABLE)
+arg(full_ARG, 'f', "full", NULL, 0)
+arg(help_ARG, 'h', "help", NULL, 0)
+arg(help2_ARG, '?', "", NULL, 0)
+arg(stripesize_ARG, 'I', "stripesize", size_kb_arg, 0)
+arg(stripes_ARG, 'i', "stripes", int_arg, 0)
+arg(interval_ARG, 'i', "interval", int_arg, 0)
+arg(iop_version_ARG, 'i', "iop_version", NULL, 0)
+arg(logicalvolume_ARG, 'l', "logicalvolume", int_arg, 0)
+arg(maxlogicalvolumes_ARG, 'l', "maxlogicalvolumes", int_arg, 0)
+arg(extents_ARG, 'l', "extents", int_arg_with_sign_and_percent, 0)
+arg(lvmpartition_ARG, 'l', "lvmpartition", NULL, 0)
+arg(list_ARG, 'l', "list", NULL, 0)
+arg(size_ARG, 'L', "size", size_mb_arg, 0)
+arg(logicalextent_ARG, 'L', "logicalextent", int_arg_with_sign, 0)
+arg(persistent_ARG, 'M', "persistent", yes_no_arg, 0)
+arg(merge_ARG, '\0', "merge", NULL, 0)
+arg(major_ARG, 'j', "major", major_arg, 0)
+arg(mirrors_ARG, 'm', "mirrors", int_arg_with_sign, 0)
+arg(metadatatype_ARG, 'M', "metadatatype", metadatatype_arg, 0)
+arg(maps_ARG, 'm', "maps", NULL, 0)
+arg(name_ARG, 'n', "name", string_arg, 0)
+arg(oldpath_ARG, 'n', "oldpath", NULL, 0)
+arg(nofsck_ARG, 'n', "nofsck", NULL, 0)
+arg(novolumegroup_ARG, 'n', "novolumegroup", NULL, 0)
+arg(options_ARG, 'o', "options", string_arg, 0)
+arg(sort_ARG, 'O', "sort", string_arg, 0)
+arg(permission_ARG, 'p', "permission", permission_arg, 0)
+arg(maxphysicalvolumes_ARG, 'p', "maxphysicalvolumes", int_arg, 0)
+arg(partial_ARG, 'P', "partial", NULL, 0)
+arg(physicalvolume_ARG, 'P', "physicalvolume", NULL, 0)
+arg(readahead_ARG, 'r', "readahead", readahead_arg, 0)
+arg(resizefs_ARG, 'r', "resizefs", NULL, 0)
+arg(reset_ARG, 'R', "reset", NULL, 0)
+arg(regionsize_ARG, 'R', "regionsize", size_mb_arg, 0)
+arg(physicalextentsize_ARG, 's', "physicalextentsize", size_mb_arg, 0)
+arg(stdin_ARG, 's', "stdin", NULL, 0)
+arg(snapshot_ARG, 's', "snapshot", NULL, 0)
+arg(short_ARG, 's', "short", NULL, 0)
+arg(test_ARG, 't', "test", NULL, 0)
+arg(uuid_ARG, 'u', "uuid", NULL, 0)
+arg(uuidstr_ARG, 'u', "uuid", string_arg, 0)
+arg(uuidlist_ARG, 'U', "uuidlist", NULL, 0)
+arg(verbose_ARG, 'v', "verbose", NULL, ARG_COUNTABLE)
+arg(volumegroup_ARG, 'V', "volumegroup", NULL, 0)
+arg(allocatable_ARG, 'x', "allocatable", yes_no_arg, 0)
+arg(resizeable_ARG, 'x', "resizeable", yes_no_arg, 0)
+arg(yes_ARG, 'y', "yes", NULL, 0)
+arg(zero_ARG, 'Z', "zero", yes_no_arg, 0)
+
+/* this should always be last */
+arg(ARG_COUNT, '-', "", NULL, 0)
+/* *INDENT-ON* */
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define xx(a, b, c...) a
+
+#include "commands.h"
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*********** Replace with script?
+xx(e2fsadm,
+ "Resize logical volume and ext2 filesystem",
+ "e2fsadm "
+ "[-d|--debug] " "[-h|--help] " "[-n|--nofsck]" "\n"
+ "\t{[-l|--extents] [+|-]LogicalExtentsNumber |" "\n"
+ "\t [-L|--size] [+|-]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}" "\n"
+ "\t[-t|--test] " "\n"
+ "\t[-v|--verbose] " "\n"
+ "\t[--version] " "\n"
+ "\tLogicalVolumePath" "\n",
+
+ extents_ARG, size_ARG, nofsck_ARG, test_ARG)
+*********/
+
+xx(dumpconfig,
+ "Dump active configuration",
+ PERMITTED_READ_ONLY,
+ "dumpconfig "
+ "\t[-f|--file filename] " "\n"
+ "[ConfigurationVariable...]\n",
+ file_ARG)
+
+xx(formats,
+ "List available metadata formats",
+ PERMITTED_READ_ONLY,
+ "formats\n")
+
+xx(help,
+ "Display help for commands",
+ PERMITTED_READ_ONLY,
+ "help <command>" "\n")
+
+/*********
+xx(lvactivate,
+ "Activate logical volume on given partition(s)",
+ "lvactivate "
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[-v|--verbose]\n"
+ "Logical Volume(s)\n")
+***********/
+
+xx(lvchange,
+ "Change the attributes of logical volume(s)",
+ CACHE_VGMETADATA | PERMITTED_READ_ONLY,
+ "lvchange\n"
+ "\t[-A|--autobackup y|n]\n"
+ "\t[-a|--available [e|l]y|n]\n"
+ "\t[--addtag Tag]\n"
+ "\t[--alloc AllocationPolicy]\n"
+ "\t[-C|--contiguous y|n]\n"
+ "\t[-d|--debug]\n"
+ "\t[--deltag Tag]\n"
+ "\t[-f|--force]\n"
+ "\t[-h|--help]\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[--ignoremonitoring]\n"
+ "\t[--monitor {y|n}]\n"
+ "\t[--poll {y|n}]\n"
+ "\t[--noudevsync]\n"
+ "\t[-M|--persistent y|n] [--major major] [--minor minor]\n"
+ "\t[-P|--partial] " "\n"
+ "\t[-p|--permission r|rw]\n"
+ "\t[-r|--readahead ReadAheadSectors|auto|none]\n"
+ "\t[--refresh]\n"
+ "\t[--resync]\n"
+ "\t[--sysinit]\n"
+ "\t[-t|--test]\n"
+ "\t[-v|--verbose]\n"
+ "\t[-y|--yes]\n"
+ "\t[--version]" "\n"
+ "\tLogicalVolume[Path] [LogicalVolume[Path]...]\n",
+
+ alloc_ARG, autobackup_ARG, available_ARG, contiguous_ARG, force_ARG,
+ ignorelockingfailure_ARG, ignoremonitoring_ARG, major_ARG, minor_ARG,
+ monitor_ARG, noudevsync_ARG, partial_ARG, permission_ARG, persistent_ARG,
+ poll_ARG, readahead_ARG, resync_ARG, refresh_ARG, addtag_ARG, deltag_ARG,
+ sysinit_ARG, test_ARG, yes_ARG)
+
+xx(lvconvert,
+ "Change logical volume layout",
+ 0,
+ "lvconvert "
+ "[-m|--mirrors Mirrors [{--mirrorlog {disk|core|mirrored}|--corelog}]]\n"
+ "\t[--repair [--use-policies]]\n"
+ "\t[-R|--regionsize MirrorLogRegionSize]\n"
+ "\t[--alloc AllocationPolicy]\n"
+ "\t[-b|--background]\n"
+ "\t[-d|--debug]\n"
+ "\t[-f|--force]\n"
+ "\t[-h|-?|--help]\n"
+ "\t[-i|--interval seconds]\n"
+ "\t[--stripes Stripes [-I|--stripesize StripeSize]]\n"
+ "\t[--noudevsync]\n"
+ "\t[-v|--verbose]\n"
+ "\t[-y|--yes]\n"
+ "\t[--version]" "\n"
+ "\tLogicalVolume[Path] [PhysicalVolume[Path]...]\n\n"
+
+ "lvconvert "
+ "[--splitmirrors Images --name SplitLogicalVolumeName]\n"
+ "\tLogicalVolume[Path] [SplittablePhysicalVolume[Path]...]\n\n"
+
+ "lvconvert "
+ "[-s|--snapshot]\n"
+ "\t[-c|--chunksize]\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|-?|--help]\n"
+ "\t[--noudevsync]\n"
+ "\t[-v|--verbose]\n"
+ "\t[-Z|--zero {y|n}]\n"
+ "\t[--version]" "\n"
+ "\tOriginalLogicalVolume[Path] SnapshotLogicalVolume[Path]\n\n"
+
+ "lvconvert "
+ "--merge\n"
+ "\t[-b|--background]\n"
+ "\t[-i|--interval seconds]\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|-?|--help]\n"
+ "\t[-v|--verbose]\n"
+ "\tSnapshotLogicalVolume[Path]\n",
+
+ alloc_ARG, background_ARG, chunksize_ARG, corelog_ARG, interval_ARG,
+ merge_ARG, mirrorlog_ARG, mirrors_ARG, name_ARG, noudevsync_ARG,
+ regionsize_ARG, repair_ARG, snapshot_ARG, splitmirrors_ARG,
+ stripes_long_ARG, stripesize_ARG, test_ARG,
+ use_policies_ARG, yes_ARG, force_ARG, zero_ARG)
+
+xx(lvcreate,
+ "Create a logical volume",
+ 0,
+ "lvcreate " "\n"
+ "\t[-A|--autobackup {y|n}]\n"
+ "\t[--addtag Tag]\n"
+ "\t[--alloc AllocationPolicy]\n"
+ "\t[-C|--contiguous {y|n}]\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|-?|--help]\n"
+ "\t[--ignoremonitoring]\n"
+ "\t[--monitor {y|n}]\n"
+ "\t[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n"
+ "\t{-l|--extents LogicalExtentsNumber[%{VG|PVS|FREE}] |\n"
+ "\t -L|--size LogicalVolumeSize[bBsSkKmMgGtTpPeE]}\n"
+ "\t[-M|--persistent {y|n}] [--major major] [--minor minor]\n"
+ "\t[-m|--mirrors Mirrors [--nosync] [{--mirrorlog {disk|core|mirrored}|--corelog}]]\n"
+ "\t[-n|--name LogicalVolumeName]\n"
+ "\t[--noudevsync]\n"
+ "\t[-p|--permission {r|rw}]\n"
+ "\t[-r|--readahead ReadAheadSectors|auto|none]\n"
+ "\t[-R|--regionsize MirrorLogRegionSize]\n"
+ "\t[-t|--test]\n"
+ "\t[--type VolumeType]\n"
+ "\t[-v|--verbose]\n"
+ "\t[-Z|--zero {y|n}]\n"
+ "\t[--version]\n"
+ "\tVolumeGroupName [PhysicalVolumePath...]\n\n"
+
+ "lvcreate \n"
+ "\t{ {-s|--snapshot} OriginalLogicalVolume[Path] |\n"
+ "\t [-s|--snapshot] VolumeGroupName[Path] --virtualsize VirtualSize}\n"
+ "\t[-c|--chunksize]\n"
+ "\t[-A|--autobackup {y|n}]\n"
+ "\t[--addtag Tag]\n"
+ "\t[--alloc AllocationPolicy]\n"
+ "\t[-C|--contiguous {y|n}]\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|-?|--help]\n"
+ "\t[--ignoremonitoring]\n"
+ "\t[--monitor {y|n}]\n"
+ "\t[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n"
+ "\t{-l|--extents LogicalExtentsNumber[%{VG|FREE|ORIGIN}] |\n"
+ "\t -L|--size LogicalVolumeSize[bBsSkKmMgGtTpPeE]}\n"
+ "\t[-M|--persistent {y|n}] [--major major] [--minor minor]\n"
+ "\t[-n|--name LogicalVolumeName]\n"
+ "\t[--noudevsync]\n"
+ "\t[-p|--permission {r|rw}]\n"
+ "\t[-r|--readahead ReadAheadSectors|auto|none]\n"
+ "\t[-t|--test]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]\n"
+
+ "\t[PhysicalVolumePath...]\n\n",
+
+ addtag_ARG, alloc_ARG, autobackup_ARG, chunksize_ARG, contiguous_ARG,
+ corelog_ARG, extents_ARG, ignoremonitoring_ARG, major_ARG, minor_ARG,
+ mirrorlog_ARG, mirrors_ARG, monitor_ARG, name_ARG, nosync_ARG, noudevsync_ARG,
+ permission_ARG, persistent_ARG, readahead_ARG, regionsize_ARG, size_ARG,
+ snapshot_ARG, stripes_ARG, stripesize_ARG, test_ARG, type_ARG,
+ virtualoriginsize_ARG, virtualsize_ARG, zero_ARG)
+
+xx(lvdisplay,
+ "Display information about a logical volume",
+ PERMITTED_READ_ONLY,
+ "lvdisplay\n"
+ "\t[-a|--all]\n"
+ "\t[-c|--colon]\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[-m|--maps]\n"
+ "\t[--nosuffix]\n"
+ "\t[-P|--partial] " "\n"
+ "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\t[LogicalVolume[Path] [LogicalVolume[Path]...]]\n"
+ "\n"
+ "lvdisplay --columns|-C\n"
+ "\t[--aligned]\n"
+ "\t[-a|--all]\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[--noheadings]\n"
+ "\t[--nosuffix]\n"
+ "\t[-o|--options [+]Field[,Field]]\n"
+ "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n"
+ "\t[-P|--partial] " "\n"
+ "\t[--segments]\n"
+ "\t[--separator Separator]\n"
+ "\t[--unbuffered]\n"
+ "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\t[LogicalVolume[Path] [LogicalVolume[Path]...]]\n",
+
+ aligned_ARG, all_ARG, colon_ARG, columns_ARG,
+ ignorelockingfailure_ARG, maps_ARG, noheadings_ARG, nosuffix_ARG,
+ options_ARG, sort_ARG, partial_ARG, segments_ARG, separator_ARG,
+ unbuffered_ARG, units_ARG)
+
+xx(lvextend,
+ "Add space to a logical volume",
+ 0,
+ "lvextend\n"
+ "\t[-A|--autobackup y|n]\n"
+ "\t[--alloc AllocationPolicy]\n"
+ "\t[-d|--debug]\n"
+ "\t[-f|--force]\n"
+ "\t[-h|--help]\n"
+ "\t[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n"
+ "\t{-l|--extents [+]LogicalExtentsNumber[%{VG|LV|PVS|FREE|ORIGIN}] |\n"
+ "\t -L|--size [+]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}\n"
+ "\t[-m|--mirrors Mirrors]\n"
+ "\t[--use-policies]\n"
+ "\t[-n|--nofsck]\n"
+ "\t[--noudevsync]\n"
+ "\t[-r|--resizefs]\n"
+ "\t[-t|--test]\n"
+ "\t[--type VolumeType]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\tLogicalVolume[Path] [ PhysicalVolumePath... ]\n",
+
+ alloc_ARG, autobackup_ARG, extents_ARG, force_ARG, mirrors_ARG,
+ nofsck_ARG, noudevsync_ARG, resizefs_ARG, size_ARG, stripes_ARG,
+ stripesize_ARG, test_ARG, type_ARG, use_policies_ARG)
+
+xx(lvmchange,
+ "With the device mapper, this is obsolete and does nothing.",
+ 0,
+ "lvmchange\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[-R|--reset]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n",
+
+ reset_ARG)
+
+xx(lvmdiskscan,
+ "List devices that may be used as physical volumes",
+ PERMITTED_READ_ONLY,
+ "lvmdiskscan\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[-l|--lvmpartition]\n"
+ "\t[--version]" "\n",
+
+ lvmpartition_ARG)
+
+xx(lvmsadc,
+ "Collect activity data",
+ 0,
+ "lvmsadc\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\t[LogFilePath]\n" )
+
+xx(lvmsar,
+ "Create activity report",
+ 0,
+ "lvmsar\n"
+ "\t[-d|--debug]\n"
+ "\t[-f|--full]\n"
+ "\t[-h|--help]\n"
+ "\t[-s|--stdin]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\tLogFilePath\n",
+
+ full_ARG, stdin_ARG)
+
+xx(lvreduce,
+ "Reduce the size of a logical volume",
+ 0,
+ "lvreduce\n"
+ "\t[-A|--autobackup y|n]\n"
+ "\t[-d|--debug]\n"
+ "\t[-f|--force]\n"
+ "\t[-h|--help]\n"
+ "\t{-l|--extents [-]LogicalExtentsNumber[%{VG|LV|FREE|ORIGIN}] |\n"
+ "\t -L|--size [-]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}\n"
+ "\t[-n|--nofsck]\n"
+ "\t[--noudevsync]\n"
+ "\t[-r|--resizefs]\n"
+ "\t[-t|--test]\n"
+ "\t[-v|--verbose]\n"
+ "\t[-y|--yes]\n"
+ "\t[--version]" "\n"
+ "\tLogicalVolume[Path]\n",
+
+ autobackup_ARG, force_ARG, extents_ARG, nofsck_ARG, noudevsync_ARG,
+ resizefs_ARG, size_ARG, test_ARG, yes_ARG)
+
+xx(lvremove,
+ "Remove logical volume(s) from the system",
+ 0,
+ "lvremove\n"
+ "\t[-A|--autobackup y|n]\n"
+ "\t[-d|--debug]\n"
+ "\t[-f|--force]\n"
+ "\t[-h|--help]\n"
+ "\t[--noudevsync]\n"
+ "\t[-t|--test]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\tLogicalVolume[Path] [LogicalVolume[Path]...]\n",
+
+ autobackup_ARG, force_ARG, noudevsync_ARG, test_ARG)
+
+xx(lvrename,
+ "Rename a logical volume",
+ 0,
+ "lvrename "
+ "\t[-A|--autobackup {y|n}] " "\n"
+ "\t[-d|--debug] " "\n"
+ "\t[-h|-?|--help] " "\n"
+ "\t[--noudevsync]\n"
+ "\t[-t|--test] " "\n"
+ "\t[-v|--verbose]" "\n"
+ "\t[--version] " "\n"
+ "\t{ OldLogicalVolumePath NewLogicalVolumePath |" "\n"
+ "\t VolumeGroupName OldLogicalVolumeName NewLogicalVolumeName }\n",
+
+ autobackup_ARG, noudevsync_ARG, test_ARG)
+
+xx(lvresize,
+ "Resize a logical volume",
+ 0,
+ "lvresize\n"
+ "\t[-A|--autobackup y|n]\n"
+ "\t[--alloc AllocationPolicy]\n"
+ "\t[-d|--debug]\n"
+ "\t[-f|--force]\n"
+ "\t[-h|--help]\n"
+ "\t[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n"
+ "\t{-l|--extents [+|-]LogicalExtentsNumber[%{VG|LV|PVS|FREE|ORIGIN}] |\n"
+ "\t -L|--size [+|-]LogicalVolumeSize[bBsSkKmMgGtTpPeE]}\n"
+ "\t[-n|--nofsck]\n"
+ "\t[--noudevsync]\n"
+ "\t[-r|--resizefs]\n"
+ "\t[-t|--test]\n"
+ "\t[--type VolumeType]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\tLogicalVolume[Path] [ PhysicalVolumePath... ]\n",
+
+ alloc_ARG, autobackup_ARG, extents_ARG, force_ARG, nofsck_ARG,
+ noudevsync_ARG, resizefs_ARG, size_ARG, stripes_ARG, stripesize_ARG,
+ test_ARG, type_ARG)
+
+xx(lvs,
+ "Display information about logical volumes",
+ PERMITTED_READ_ONLY,
+ "lvs" "\n"
+ "\t[-a|--all]\n"
+ "\t[--aligned]\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[--nameprefixes]\n"
+ "\t[--noheadings]\n"
+ "\t[--nosuffix]\n"
+ "\t[-o|--options [+]Field[,Field]]\n"
+ "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n"
+ "\t[-P|--partial] " "\n"
+ "\t[--rows]\n"
+ "\t[--segments]\n"
+ "\t[--separator Separator]\n"
+ "\t[--trustcache]\n"
+ "\t[--unbuffered]\n"
+ "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+ "\t[--unquoted]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\t[LogicalVolume[Path] [LogicalVolume[Path]...]]\n",
+
+ aligned_ARG, all_ARG, ignorelockingfailure_ARG, nameprefixes_ARG,
+ noheadings_ARG, nolocking_ARG, nosuffix_ARG, options_ARG, partial_ARG,
+ rows_ARG, segments_ARG, separator_ARG, sort_ARG, trustcache_ARG,
+ unbuffered_ARG, units_ARG, unquoted_ARG)
+
+xx(lvscan,
+ "List all logical volumes in all volume groups",
+ PERMITTED_READ_ONLY,
+ "lvscan " "\n"
+ "\t[-a|--all]\n"
+ "\t[-b|--blockdevice] " "\n"
+ "\t[-d|--debug] " "\n"
+ "\t[-h|-?|--help] " "\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[-P|--partial] " "\n"
+ "\t[-v|--verbose] " "\n"
+ "\t[--version]\n",
+
+ all_ARG, blockdevice_ARG, ignorelockingfailure_ARG, partial_ARG)
+
+xx(pvchange,
+ "Change attributes of physical volume(s)",
+ 0,
+ "pvchange\n"
+ "\t[-a|--all]\n"
+ "\t[-A|--autobackup y|n]\n"
+ "\t[-d|--debug]\n"
+ "\t[-f|--force]\n"
+ "\t[-h|--help]\n"
+ "\t[-t|--test]\n"
+ "\t[-u|--uuid]\n"
+ "\t[-x|--allocatable y|n]\n"
+ "\t[--metadataignore y|n]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--addtag Tag]\n"
+ "\t[--deltag Tag]\n"
+ "\t[--version]" "\n"
+ "\t[PhysicalVolumePath...]\n",
+
+ all_ARG, allocatable_ARG, allocation_ARG, autobackup_ARG, deltag_ARG,
+ addtag_ARG, force_ARG, metadataignore_ARG, test_ARG, uuid_ARG)
+
+xx(pvresize,
+ "Resize physical volume(s)",
+ 0,
+ "pvresize " "\n"
+ "\t[-d|--debug]" "\n"
+ "\t[-h|-?|--help] " "\n"
+ "\t[--setphysicalvolumesize PhysicalVolumeSize[bBsSkKmMgGtTpPeE]" "\n"
+ "\t[-t|--test] " "\n"
+ "\t[-v|--verbose] " "\n"
+ "\t[--version] " "\n"
+ "\tPhysicalVolume [PhysicalVolume...]\n",
+
+ physicalvolumesize_ARG, test_ARG)
+
+xx(pvck,
+ "Check the consistency of physical volume(s)",
+ 0,
+ "pvck "
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[--labelsector sector] " "\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\tPhysicalVolume [PhysicalVolume...]\n",
+
+ labelsector_ARG)
+
+xx(pvcreate,
+ "Initialize physical volume(s) for use by LVM",
+ 0,
+ "pvcreate " "\n"
+ "\t[--norestorefile]\n"
+ "\t[--restorefile file]\n"
+ "\t[-d|--debug]" "\n"
+ "\t[-f[f]|--force [--force]] " "\n"
+ "\t[-h|-?|--help] " "\n"
+ "\t[--labelsector sector] " "\n"
+ "\t[-M|--metadatatype 1|2]" "\n"
+ "\t[--pvmetadatacopies #copies]" "\n"
+ "\t[--metadatasize MetadataSize[bBsSkKmMgGtTpPeE]]" "\n"
+ "\t[--dataalignment Alignment[bBsSkKmMgGtTpPeE]]" "\n"
+ "\t[--dataalignmentoffset AlignmentOffset[bBsSkKmMgGtTpPeE]]" "\n"
+ "\t[--setphysicalvolumesize PhysicalVolumeSize[bBsSkKmMgGtTpPeE]" "\n"
+ "\t[-t|--test] " "\n"
+ "\t[-u|--uuid uuid] " "\n"
+ "\t[-v|--verbose] " "\n"
+ "\t[-y|--yes]" "\n"
+ "\t[-Z|--zero {y|n}]\n"
+ "\t[--version] " "\n"
+ "\tPhysicalVolume [PhysicalVolume...]\n",
+
+ dataalignment_ARG, dataalignmentoffset_ARG, force_ARG, test_ARG,
+ labelsector_ARG, metadatatype_ARG, metadatacopies_ARG,
+ metadatasize_ARG, metadataignore_ARG, norestorefile_ARG,
+ physicalvolumesize_ARG, pvmetadatacopies_ARG,
+ restorefile_ARG, uuidstr_ARG, yes_ARG, zero_ARG)
+
+xx(pvdata,
+ "Display the on-disk metadata for physical volume(s)",
+ 0,
+ "pvdata " "\n"
+ "\t[-a|--all] " "\n"
+ "\t[-d|--debug] " "\n"
+ "\t[-E|--physicalextent] " "\n"
+ "\t[-h|-?|--help]" "\n"
+ "\t[-L|--logicalvolume] " "\n"
+ "\t[-P[P]|--physicalvolume [--physicalvolume]]" "\n"
+ "\t[-U|--uuidlist] " "\n"
+ "\t[-v[v]|--verbose [--verbose]] " "\n"
+ "\t[-V|--volumegroup]" "\n"
+ "\t[--version] " "\n"
+ "\tPhysicalVolume [PhysicalVolume...]\n",
+
+ all_ARG, logicalextent_ARG, physicalextent_ARG,
+ physicalvolume_ARG, uuidlist_ARG, volumegroup_ARG)
+
+xx(pvdisplay,
+ "Display various attributes of physical volume(s)",
+ CACHE_VGMETADATA | PERMITTED_READ_ONLY,
+ "pvdisplay\n"
+ "\t[-c|--colon]\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[-m|--maps]\n"
+ "\t[--nosuffix]\n"
+ "\t[-s|--short]\n"
+ "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\t[PhysicalVolumePath [PhysicalVolumePath...]]\n"
+ "\n"
+ "pvdisplay --columns|-C\n"
+ "\t[--aligned]\n"
+ "\t[-a|--all]\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[--noheadings]\n"
+ "\t[--nosuffix]\n"
+ "\t[-o|--options [+]Field[,Field]]\n"
+ "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n"
+ "\t[--separator Separator]\n"
+ "\t[--unbuffered]\n"
+ "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\t[PhysicalVolumePath [PhysicalVolumePath...]]\n",
+
+ aligned_ARG, all_ARG, colon_ARG, columns_ARG, ignorelockingfailure_ARG,
+ maps_ARG, noheadings_ARG, nosuffix_ARG, options_ARG, separator_ARG,
+ short_ARG, sort_ARG, unbuffered_ARG, units_ARG)
+
+xx(pvmove,
+ "Move extents from one physical volume to another",
+ 0,
+ "pvmove " "\n"
+ "\t[--abort]\n"
+ "\t[-A|--autobackup {y|n}]\n"
+ "\t[--alloc AllocationPolicy]\n"
+ "\t[-b|--background]\n"
+ "\t[-d|--debug]\n "
+ "\t[-h|-?|--help]\n"
+ "\t[-i|--interval seconds]\n"
+ "\t[--noudevsync]\n"
+ "\t[-t|--test]\n "
+ "\t[-v|--verbose]\n "
+ "\t[--version]\n"
+ "\t[{-n|--name} LogicalVolume]\n"
+/* "\t[{-n|--name} LogicalVolume[:LogicalExtent[-LogicalExtent]...]]\n" */
+ "\tSourcePhysicalVolume[:PhysicalExtent[-PhysicalExtent]...]}\n"
+ "\t[DestinationPhysicalVolume[:PhysicalExtent[-PhysicalExtent]...]...]\n",
+
+ abort_ARG, alloc_ARG, autobackup_ARG, background_ARG,
+ interval_ARG, name_ARG, noudevsync_ARG, test_ARG)
+
+xx(pvremove,
+ "Remove LVM label(s) from physical volume(s)",
+ 0,
+ "pvremove " "\n"
+ "\t[-d|--debug]" "\n"
+ "\t[-f[f]|--force [--force]] " "\n"
+ "\t[-h|-?|--help] " "\n"
+ "\t[-t|--test] " "\n"
+ "\t[-v|--verbose] " "\n"
+ "\t[-y|--yes]" "\n"
+ "\t[--version] " "\n"
+ "\tPhysicalVolume [PhysicalVolume...]\n",
+
+ force_ARG, test_ARG, yes_ARG)
+
+xx(pvs,
+ "Display information about physical volumes",
+ CACHE_VGMETADATA | PERMITTED_READ_ONLY,
+ "pvs" "\n"
+ "\t[-a|--all]\n"
+ "\t[--aligned]\n"
+ "\t[-d|--debug]" "\n"
+ "\t[-h|-?|--help] " "\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[--nameprefixes]\n"
+ "\t[--noheadings]\n"
+ "\t[--nosuffix]\n"
+ "\t[-o|--options [+]Field[,Field]]\n"
+ "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n"
+ "\t[-P|--partial] " "\n"
+ "\t[--rows]\n"
+ "\t[--segments]\n"
+ "\t[--separator Separator]\n"
+ "\t[--trustcache]\n"
+ "\t[--unbuffered]\n"
+ "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+ "\t[--unquoted]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]\n"
+ "\t[PhysicalVolume [PhysicalVolume...]]\n",
+
+ aligned_ARG, all_ARG, ignorelockingfailure_ARG, nameprefixes_ARG,
+ noheadings_ARG, nolocking_ARG, nosuffix_ARG, options_ARG, partial_ARG,
+ rows_ARG, segments_ARG, separator_ARG, sort_ARG, trustcache_ARG,
+ unbuffered_ARG, units_ARG, unquoted_ARG)
+
+xx(pvscan,
+ "List all physical volumes",
+ PERMITTED_READ_ONLY,
+ "pvscan " "\n"
+ "\t[-d|--debug] " "\n"
+ "\t{-e|--exported | -n|--novolumegroup} " "\n"
+ "\t[-h|-?|--help]" "\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[-P|--partial] " "\n"
+ "\t[-s|--short] " "\n"
+ "\t[-u|--uuid] " "\n"
+ "\t[-v|--verbose] " "\n"
+ "\t[--version]\n",
+
+ exported_ARG, ignorelockingfailure_ARG, novolumegroup_ARG, partial_ARG,
+ short_ARG, uuid_ARG)
+
+xx(segtypes,
+ "List available segment types",
+ PERMITTED_READ_ONLY,
+ "segtypes\n")
+
+xx(vgcfgbackup,
+ "Backup volume group configuration(s)",
+ PERMITTED_READ_ONLY,
+ "vgcfgbackup " "\n"
+ "\t[-d|--debug] " "\n"
+ "\t[-f|--file filename] " "\n"
+ "\t[-h|-?|--help] " "\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[-P|--partial] " "\n"
+ "\t[-v|--verbose]" "\n"
+ "\t[--version] " "\n"
+ "\t[VolumeGroupName...]\n",
+
+ file_ARG, ignorelockingfailure_ARG, partial_ARG)
+
+xx(vgcfgrestore,
+ "Restore volume group configuration",
+ 0,
+ "vgcfgrestore " "\n"
+ "\t[-d|--debug] " "\n"
+ "\t[-f|--file filename] " "\n"
+ "\t[-l[l]|--list [--list]]" "\n"
+ "\t[-M|--metadatatype 1|2]" "\n"
+ "\t[-h|--help]" "\n"
+ "\t[-t|--test] " "\n"
+ "\t[-v|--verbose]" "\n"
+ "\t[--version] " "\n"
+ "\tVolumeGroupName",
+
+ file_ARG, list_ARG, metadatatype_ARG, test_ARG)
+
+xx(vgchange,
+ "Change volume group attributes",
+ CACHE_VGMETADATA | PERMITTED_READ_ONLY,
+ "vgchange" "\n"
+ "\t[-A|--autobackup {y|n}] " "\n"
+ "\t[--alloc AllocationPolicy] " "\n"
+ "\t[-P|--partial] " "\n"
+ "\t[-d|--debug] " "\n"
+ "\t[-h|--help] " "\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[--ignoremonitoring]\n"
+ "\t[--monitor {y|n}]\n"
+ "\t[--[vg]metadatacopies #copies] " "\n"
+ "\t[--poll {y|n}]\n"
+ "\t[--noudevsync]\n"
+ "\t[--refresh]\n"
+ "\t[--sysinit]\n"
+ "\t[-t|--test]" "\n"
+ "\t[-u|--uuid] " "\n"
+ "\t[-v|--verbose] " "\n"
+ "\t[--version]" "\n"
+ "\t{-a|--available [e|l]{y|n} |" "\n"
+ "\t -c|--clustered {y|n} |" "\n"
+ "\t -x|--resizeable {y|n} |" "\n"
+ "\t -l|--logicalvolume MaxLogicalVolumes |" "\n"
+ "\t -p|--maxphysicalvolumes MaxPhysicalVolumes |" "\n"
+ "\t -s|--physicalextentsize PhysicalExtentSize[bBsSkKmMgGtTpPeE] |" "\n"
+ "\t --addtag Tag |\n"
+ "\t --deltag Tag}\n"
+ "\t[VolumeGroupName...]\n",
+
+ addtag_ARG, alloc_ARG, allocation_ARG, autobackup_ARG, available_ARG,
+ clustered_ARG, deltag_ARG, ignorelockingfailure_ARG, ignoremonitoring_ARG,
+ logicalvolume_ARG, maxphysicalvolumes_ARG, monitor_ARG, noudevsync_ARG,
+ metadatacopies_ARG, vgmetadatacopies_ARG, partial_ARG,
+ physicalextentsize_ARG, poll_ARG, refresh_ARG, resizeable_ARG,
+ resizable_ARG, sysinit_ARG, test_ARG, uuid_ARG)
+
+xx(vgck,
+ "Check the consistency of volume group(s)",
+ 0,
+ "vgck "
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\t[VolumeGroupName...]\n" )
+
+xx(vgconvert,
+ "Change volume group metadata format",
+ 0,
+ "vgconvert " "\n"
+ "\t[-d|--debug]" "\n"
+ "\t[-h|--help] " "\n"
+ "\t[--labelsector sector] " "\n"
+ "\t[-M|--metadatatype 1|2]" "\n"
+ "\t[--pvmetadatacopies #copies]" "\n"
+ "\t[--metadatasize MetadataSize[bBsSkKmMgGtTpPeE]]" "\n"
+ "\t[-t|--test] " "\n"
+ "\t[-v|--verbose] " "\n"
+ "\t[--version] " "\n"
+ "\tVolumeGroupName [VolumeGroupName...]\n",
+
+ force_ARG, test_ARG, labelsector_ARG, metadatatype_ARG, metadatacopies_ARG,
+ pvmetadatacopies_ARG, metadatasize_ARG )
+
+xx(vgcreate,
+ "Create a volume group",
+ 0,
+ "vgcreate" "\n"
+ "\t[-A|--autobackup {y|n}] " "\n"
+ "\t[--addtag Tag] " "\n"
+ "\t[--alloc AllocationPolicy] " "\n"
+ "\t[-c|--clustered {y|n}] " "\n"
+ "\t[-d|--debug]" "\n"
+ "\t[-h|--help]" "\n"
+ "\t[-l|--maxlogicalvolumes MaxLogicalVolumes]" "\n"
+ "\t[-M|--metadatatype 1|2] " "\n"
+ "\t[--[vg]metadatacopies #copies] " "\n"
+ "\t[-p|--maxphysicalvolumes MaxPhysicalVolumes] " "\n"
+ "\t[-s|--physicalextentsize PhysicalExtentSize[bBsSkKmMgGtTpPeE]] " "\n"
+ "\t[-t|--test] " "\n"
+ "\t[-v|--verbose]" "\n"
+ "\t[--version] " "\n"
+ "\t[ PHYSICAL DEVICE OPTIONS ] " "\n"
+ "\tVolumeGroupName PhysicalDevicePath [PhysicalDevicePath...]\n",
+
+ addtag_ARG, alloc_ARG, autobackup_ARG, clustered_ARG, maxlogicalvolumes_ARG,
+ maxphysicalvolumes_ARG, metadatatype_ARG, physicalextentsize_ARG, test_ARG,
+ force_ARG, yes_ARG, zero_ARG, labelsector_ARG, metadatasize_ARG,
+ pvmetadatacopies_ARG, metadatacopies_ARG, vgmetadatacopies_ARG,
+ dataalignment_ARG, dataalignmentoffset_ARG)
+
+xx(vgdisplay,
+ "Display volume group information",
+ PERMITTED_READ_ONLY,
+ "vgdisplay " "\n"
+ "\t[-A|--activevolumegroups]" "\n"
+ "\t[-c|--colon | -s|--short | -v|--verbose]" "\n"
+ "\t[-d|--debug] " "\n"
+ "\t[-h|--help] " "\n"
+ "\t[--ignorelockingfailure]" "\n"
+ "\t[--nosuffix]\n"
+ "\t[-P|--partial] " "\n"
+ "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+ "\t[--version]" "\n"
+ "\t[VolumeGroupName [VolumeGroupName...]]\n"
+ "\n"
+ "vgdisplay --columns|-C\n"
+ "\t[--aligned]\n"
+ "\t[-d|--debug] " "\n"
+ "\t[-h|--help] " "\n"
+ "\t[--ignorelockingfailure]" "\n"
+ "\t[--noheadings]\n"
+ "\t[--nosuffix]\n"
+ "\t[-o|--options [+]Field[,Field]]\n"
+ "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n"
+ "\t[-P|--partial] " "\n"
+ "\t[--separator Separator]\n"
+ "\t[--unbuffered]\n"
+ "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+ "\t[--verbose]" "\n"
+ "\t[--version]" "\n"
+ "\t[VolumeGroupName [VolumeGroupName...]]\n",
+
+ activevolumegroups_ARG, aligned_ARG, colon_ARG, columns_ARG,
+ ignorelockingfailure_ARG, noheadings_ARG, nosuffix_ARG, options_ARG,
+ partial_ARG, short_ARG, separator_ARG, sort_ARG, unbuffered_ARG, units_ARG)
+
+xx(vgexport,
+ "Unregister volume group(s) from the system",
+ 0,
+ "vgexport " "\n"
+ "\t[-a|--all] " "\n"
+ "\t[-d|--debug] " "\n"
+ "\t[-h|--help]" "\n"
+ "\t[-v|--verbose] " "\n"
+ "\t[--version] " "\n"
+ "\tVolumeGroupName [VolumeGroupName...]\n",
+
+ all_ARG, test_ARG)
+
+xx(vgextend,
+ "Add physical volumes to a volume group",
+ 0,
+ "vgextend\n"
+ "\t[-A|--autobackup y|n]\n"
+ "\t[--restoremissing]\n"
+ "\t[-d|--debug]\n"
+ "\t[-f|--force]\n"
+ "\t[-h|--help]\n"
+ "\t[-t|--test]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\t[ PHYSICAL DEVICE OPTIONS ] " "\n"
+ "\tVolumeGroupName PhysicalDevicePath [PhysicalDevicePath...]\n",
+
+ autobackup_ARG, test_ARG,
+ force_ARG, yes_ARG, zero_ARG, labelsector_ARG, metadatatype_ARG,
+ metadatasize_ARG, pvmetadatacopies_ARG, metadatacopies_ARG,
+ metadataignore_ARG, dataalignment_ARG, dataalignmentoffset_ARG,
+ restoremissing_ARG)
+
+xx(vgimport,
+ "Register exported volume group with system",
+ 0,
+ "vgimport " "\n"
+ "\t[-a|--all]\n"
+ "\t[-d|--debug] " "\n"
+ "\t[-f|--force] " "\n"
+ "\t[-h|--help] " "\n"
+ "\t[-t|--test] " "\n"
+ "\t[-v|--verbose]" "\n"
+ "\t[--version]" "\n"
+ "\tVolumeGroupName..." "\n",
+
+ all_ARG, force_ARG, test_ARG)
+
+xx(vgmerge,
+ "Merge volume groups",
+ 0,
+ "vgmerge\n"
+ "\t[-A|--autobackup y|n]\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[-l|--list]\n"
+ "\t[-t|--test]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\tDestinationVolumeGroupName SourceVolumeGroupName\n",
+
+ autobackup_ARG, list_ARG, test_ARG)
+
+xx(vgmknodes,
+ "Create the special files for volume group devices in /dev",
+ 0,
+ "vgmknodes\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[--refresh]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\t[VolumeGroupName...]\n",
+
+ ignorelockingfailure_ARG, refresh_ARG)
+
+xx(vgreduce,
+ "Remove physical volume(s) from a volume group",
+ 0,
+ "vgreduce\n"
+ "\t[-a|--all]\n"
+ "\t[-A|--autobackup y|n]\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[--mirrorsonly]\n"
+ "\t[--removemissing]\n"
+ "\t[-f|--force]\n"
+ "\t[-t|--test]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\tVolumeGroupName\n"
+ "\t[PhysicalVolumePath...]\n",
+
+ all_ARG, autobackup_ARG, force_ARG, mirrorsonly_ARG, removemissing_ARG,
+ test_ARG)
+
+xx(vgremove,
+ "Remove volume group(s)",
+ 0,
+ "vgremove\n"
+ "\t[-d|--debug]\n"
+ "\t[-f|--force]\n"
+ "\t[-h|--help]\n"
+ "\t[--noudevsync]\n"
+ "\t[-t|--test]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\tVolumeGroupName [VolumeGroupName...]\n",
+
+ force_ARG, noudevsync_ARG, test_ARG)
+
+xx(vgrename,
+ "Rename a volume group",
+ 0,
+ "vgrename\n"
+ "\t[-A|--autobackup y|n]\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[-t|--test]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n"
+ "\tOldVolumeGroupPath NewVolumeGroupPath |\n"
+ "\tOldVolumeGroupName NewVolumeGroupName\n",
+
+ autobackup_ARG, force_ARG, test_ARG)
+
+xx(vgs,
+ "Display information about volume groups",
+ PERMITTED_READ_ONLY,
+ "vgs" "\n"
+ "\t[--aligned]\n"
+ "\t[-a|--all]\n"
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[--nameprefixes]\n"
+ "\t[--noheadings]\n"
+ "\t[--nosuffix]\n"
+ "\t[-o|--options [+]Field[,Field]]\n"
+ "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n"
+ "\t[-P|--partial] " "\n"
+ "\t[--rows]\n"
+ "\t[--separator Separator]\n"
+ "\t[--trustcache]\n"
+ "\t[--unbuffered]\n"
+ "\t[--units hHbBsSkKmMgGtTpPeE]\n"
+ "\t[--unquoted]\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]\n"
+ "\t[VolumeGroupName [VolumeGroupName...]]\n",
+
+ aligned_ARG, all_ARG, ignorelockingfailure_ARG, nameprefixes_ARG,
+ noheadings_ARG, nolocking_ARG, nosuffix_ARG, options_ARG, partial_ARG,
+ rows_ARG, separator_ARG, sort_ARG, trustcache_ARG, unbuffered_ARG, units_ARG,
+ unquoted_ARG)
+
+xx(vgscan,
+ "Search for all volume groups",
+ PERMITTED_READ_ONLY,
+ "vgscan "
+ "\t[-d|--debug]\n"
+ "\t[-h|--help]\n"
+ "\t[--ignorelockingfailure]\n"
+ "\t[--mknodes]\n"
+ "\t[-P|--partial] " "\n"
+ "\t[-v|--verbose]\n"
+ "\t[--version]" "\n",
+
+ ignorelockingfailure_ARG, mknodes_ARG, partial_ARG)
+
+xx(vgsplit,
+ "Move physical volumes into a new or existing volume group",
+ 0,
+ "vgsplit " "\n"
+ "\t[-A|--autobackup {y|n}] " "\n"
+ "\t[--alloc AllocationPolicy] " "\n"
+ "\t[-c|--clustered {y|n}] " "\n"
+ "\t[-d|--debug] " "\n"
+ "\t[-h|--help] " "\n"
+ "\t[-l|--maxlogicalvolumes MaxLogicalVolumes]" "\n"
+ "\t[-M|--metadatatype 1|2] " "\n"
+ "\t[--[vg]metadatacopies #copies] " "\n"
+ "\t[-n|--name LogicalVolumeName]\n"
+ "\t[-p|--maxphysicalvolumes MaxPhysicalVolumes] " "\n"
+ "\t[-t|--test] " "\n"
+ "\t[-v|--verbose] " "\n"
+ "\t[--version]" "\n"
+ "\tSourceVolumeGroupName DestinationVolumeGroupName" "\n"
+ "\t[PhysicalVolumePath...]\n",
+
+ alloc_ARG, autobackup_ARG, clustered_ARG,
+ maxlogicalvolumes_ARG, maxphysicalvolumes_ARG,
+ metadatatype_ARG, vgmetadatacopies_ARG, name_ARG, test_ARG)
+
+xx(version,
+ "Display software and driver version information",
+ PERMITTED_READ_ONLY,
+ "version\n" )
+
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "configure.h"
+
+#include "dm-logging.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/wait.h>
+#include <unistd.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>
+# define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
+# 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
+
+#if 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 "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
+
+#define err(msg, x...) fprintf(stderr, msg "\n", ##x)
+
+/*
+ * We have only very simple switches ATM.
+ */
+enum {
+ READ_ONLY = 0,
+ COLS_ARG,
+ EXEC_ARG,
+ FORCE_ARG,
+ GID_ARG,
+ HELP_ARG,
+ INACTIVE_ARG,
+ MAJOR_ARG,
+ MINOR_ARG,
+ MODE_ARG,
+ NAMEPREFIXES_ARG,
+ NOFLUSH_ARG,
+ NOHEADINGS_ARG,
+ NOLOCKFS_ARG,
+ NOOPENCOUNT_ARG,
+ NOTABLE_ARG,
+ UDEVCOOKIE_ARG,
+ NOUDEVRULES_ARG,
+ NOUDEVSYNC_ARG,
+ OPTIONS_ARG,
+ READAHEAD_ARG,
+ ROWS_ARG,
+ SEPARATOR_ARG,
+ SETUUID_ARG,
+ SHOWKEYS_ARG,
+ SORT_ARG,
+ TABLE_ARG,
+ TARGET_ARG,
+ TREE_ARG,
+ UID_ARG,
+ UNBUFFERED_ARG,
+ UNQUOTED_ARG,
+ UUID_ARG,
+ VERBOSE_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
+} report_type_t;
+
+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;
+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;
+
+/*
+ * Commands
+ */
+
+typedef int (*command_fn) (int argc, char **argv, void *data);
+
+struct command {
+ const char *name;
+ const char *help;
+ int min_args;
+ int max_args;
+ 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) {
+ err("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;
+}
+
+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;
+
+ /* one-line table on cmdline */
+ if (_table)
+ return _parse_line(dmt, _table, "", ++line);
+
+ /* OK for empty stdin */
+ if (file) {
+ if (!(fp = fopen(file, "r"))) {
+ err("Couldn't open '%s' for reading", file);
+ return 0;
+ }
+ } else
+ fp = stdin;
+
+#ifndef HAVE_GETLINE
+ buffer_size = LINE_SIZE;
+ if (!(buffer = dm_malloc(buffer_size))) {
+ err("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
+ dm_free(buffer);
+#else
+ free(buffer);
+#endif
+ if (file && fclose(fp))
+ fprintf(stderr, "%s: fclose failed: %s", file, strerror(errno));
+
+ 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;
+};
+
+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 err;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto err;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto err;
+
+ if (!dm_task_run(dmt))
+ goto err;
+
+ if (!dm_task_get_info(dmt, &info))
+ goto err;
+
+ if (!info.exists)
+ goto err;
+
+ return dmt;
+
+ err:
+ 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 = dm_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 = dm_malloc(sizeof(*split_name)))) {
+ log_error("Failed to allocate memory to split device name "
+ "into components.");
+ return NULL;
+ }
+
+ split_name->subsystem = _extract_uuid_prefix(uuid, separator);
+ split_name->vg_name = split_name->lv_name =
+ split_name->lv_layer = (char *) "";
+
+ if (!strcmp(split_name->subsystem, "LVM") &&
+ (!(split_name->vg_name = dm_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"))
+ dm_free(split_name->vg_name);
+
+ dm_free(split_name->subsystem);
+ dm_free(split_name);
+}
+
+static int _display_info_cols(struct dm_task *dmt, struct dm_info *info)
+{
+ struct dmsetup_report_obj obj;
+ int r = 0;
+
+ if (!info->exists) {
+ fprintf(stderr, "Device does not exist.\n");
+ return 0;
+ }
+
+ obj.task = dmt;
+ obj.info = info;
+ obj.deps_task = NULL;
+ obj.split_name = NULL;
+
+ if (_report_type & DR_TREE)
+ obj.tree_node = dm_tree_find_node(_dtree, info->major, info->minor);
+
+ if (_report_type & DR_DEPS)
+ obj.deps_task = _get_deps_task(info->major, info->minor);
+
+ if (_report_type & DR_NAME)
+ obj.split_name = _get_split_name(dm_task_get_uuid(dmt), dm_task_get_name(dmt), '-');
+
+ if (!dm_report_object(_report, &obj))
+ goto out;
+
+ r = 1;
+
+ out:
+ if (obj.deps_task)
+ dm_task_destroy(obj.deps_task);
+ if (obj.split_name)
+ _destroy_split_name(obj.split_name);
+ return r;
+}
+
+static void _display_info_long(struct dm_task *dmt, struct dm_info *info)
+{
+ const char *uuid;
+ uint32_t read_ahead;
+
+ if (!info->exists) {
+ printf("Device does not exist.\n");
+ return;
+ }
+
+ printf("Name: %s\n", dm_task_get_name(dmt));
+
+ printf("State: %s%s\n",
+ info->suspended ? "SUSPENDED" : "ACTIVE",
+ info->read_only ? " (READ-ONLY)" : "");
+
+ /* 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);
+
+ printf("\n");
+}
+
+static int _display_info(struct dm_task *dmt)
+{
+ struct dm_info info;
+
+ if (!dm_task_get_info(dmt, &info))
+ return 0;
+
+ if (!_switches[COLS_ARG])
+ _display_info_long(dmt, &info);
+ else
+ /* FIXME return code */
+ _display_info_cols(dmt, &info);
+
+ return info.exists ? 1 : 0;
+}
+
+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) {
+ fprintf(stderr, "No device specified.\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _load(int argc, char **argv, void *data __attribute__((unused)))
+{
+ int r = 0;
+ struct dm_task *dmt;
+ const char *file = NULL;
+ const char *name = NULL;
+
+ if (_switches[NOTABLE_ARG]) {
+ err("--notable only available when creating new device\n");
+ return 0;
+ }
+
+ if (!_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
+ if (argc == 1) {
+ err("Please specify device.\n");
+ return 0;
+ }
+ name = argv[1];
+ argc--;
+ argv++;
+ } else if (argc > 2) {
+ err("Too many command line arguments.\n");
+ return 0;
+ }
+
+ if (argc == 2)
+ file = argv[1];
+
+ 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 (!dm_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(int argc, char **argv, void *data __attribute__((unused)))
+{
+ int r = 0;
+ struct dm_task *dmt;
+ const char *file = NULL;
+ uint32_t cookie = 0;
+ uint16_t udev_flags = 0;
+
+ if (argc == 3)
+ file = argv[2];
+
+ if (!(dmt = dm_task_create(DM_DEVICE_CREATE)))
+ return 0;
+
+ if (!dm_task_set_name(dmt, argv[1]))
+ 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 (_udev_cookie) {
+ cookie = _udev_cookie;
+ if (_udev_only)
+ udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
+ }
+
+ if (!dm_task_set_cookie(dmt, &cookie, udev_flags) ||
+ !dm_task_run(dmt))
+ goto out;
+
+ r = 1;
+
+ if (!_udev_cookie)
+ (void) dm_udev_wait(cookie);
+
+ if (_switches[VERBOSE_ARG])
+ r = _display_info(dmt);
+
+ dm_task_destroy(dmt);
+
+ return r;
+
+ out:
+ if (!_udev_cookie)
+ (void) dm_udev_wait(cookie);
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _rename(int argc, char **argv, void *data __attribute__((unused)))
+{
+ 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, (argc == 3) ? argv[1] : NULL, 0))
+ goto out;
+
+ if (_switches[SETUUID_ARG]) {
+ if (!dm_task_set_newuuid(dmt, argv[argc - 1]))
+ goto out;
+ } else if (!dm_task_set_newname(dmt, argv[argc - 1]))
+ 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[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) ||
+ !dm_task_run(dmt))
+ goto out;
+
+ r = 1;
+
+ out:
+ if (!_udev_cookie)
+ (void) dm_udev_wait(cookie);
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _message(int argc, char **argv, void *data __attribute__((unused)))
+{
+ int r = 0, i;
+ size_t sz = 1;
+ struct dm_task *dmt;
+ char *str;
+
+ 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[1], 0))
+ goto out;
+ argc--;
+ argv++;
+ }
+
+ if (!dm_task_set_sector(dmt, (uint64_t) atoll(argv[1])))
+ goto out;
+
+ argc -= 2;
+ argv += 2;
+
+ if (argc <= 0)
+ err("No message supplied.\n");
+
+ for (i = 0; i < argc; i++)
+ sz += strlen(argv[i]) + 1;
+
+ if (!(str = dm_zalloc(sz))) {
+ err("message string allocation failed");
+ goto out;
+ }
+
+ for (i = 0; i < argc; i++) {
+ if (i)
+ strcat(str, " ");
+ strcat(str, argv[i]);
+ }
+
+ if (!dm_task_set_message(dmt, str))
+ goto out;
+
+ dm_free(str);
+
+ 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 (!dm_task_run(dmt))
+ goto out;
+
+ r = 1;
+
+ out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _setgeometry(int argc, char **argv, void *data __attribute__((unused)))
+{
+ 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[1], 0))
+ goto out;
+ argc--;
+ argv++;
+ }
+
+ if (!dm_task_set_geometry(dmt, argv[1], argv[2], argv[3], argv[4]))
+ 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;
+
+ /* run the task */
+ if (!dm_task_run(dmt))
+ goto out;
+
+ r = 1;
+
+ out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+static int _splitname(int argc, char **argv, void *data __attribute__((unused)))
+{
+ struct dmsetup_report_obj obj;
+ int r = 1;
+
+ obj.task = NULL;
+ obj.info = NULL;
+ obj.deps_task = NULL;
+ obj.tree_node = NULL;
+ obj.split_name = _get_split_name((argc == 3) ? argv[2] : "LVM",
+ argv[1], '\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;
+
+ if (!(value = strtoul(str_value, &p, 0)) ||
+ *p ||
+ (value == ULONG_MAX && errno == ERANGE) ||
+ value > 0xFFFFFFFF) {
+ err("Incorrect cookie value");
+ return 0;
+ }
+ else
+ return (uint32_t) value;
+}
+
+static int _udevflags(int args, char **argv, void *data __attribute__((unused)))
+{
+ 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[1])))
+ 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(int argc, char **argv, void *data __attribute__((unused)))
+{
+ uint32_t cookie;
+
+ if (!(cookie = _get_cookie_value(argv[1])))
+ 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(int argc, char **argv,
+ void *data __attribute__((unused)))
+{
+ log_error(_cmd_not_supported);
+
+ return 0;
+}
+
+static int _udevreleasecookie(int argc, char **argv,
+ void *data __attribute__((unused)))
+{
+ log_error(_cmd_not_supported);
+
+ return 0;
+}
+
+static int _udevcomplete_all(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+ log_error(_cmd_not_supported);
+
+ return 0;
+}
+
+static int _udevcookies(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+ log_error(_cmd_not_supported);
+
+ return 0;
+}
+
+#else /* UDEV_SYNC_SUPPORT */
+static int _set_up_udev_support(const char *dev_dir)
+{
+ struct udev *udev;
+ const char *udev_dev_dir;
+ size_t udev_dev_dir_len;
+ int dirs_diff;
+ const char *env;
+
+ 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);
+
+ if (!(udev = udev_new()) ||
+ !(udev_dev_dir = udev_get_dev_path(udev)) ||
+ !*udev_dev_dir) {
+ log_error("Could not get udev dev path.");
+ return 0;
+ }
+ udev_dev_dir_len = strlen(udev_dev_dir);
+
+ /*
+ * 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.
+ */
+
+ /* There's always a slash at the end of dev_dir. But check udev_dev_dir! */
+ if (udev_dev_dir[udev_dev_dir_len - 1] != '/')
+ dirs_diff = strncmp(dev_dir, udev_dev_dir, udev_dev_dir_len);
+ else
+ dirs_diff = strcmp(dev_dir, udev_dev_dir);
+
+ _udev_only = _udev_cookie && !dirs_diff;
+
+ 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, udev_dev_dir);
+ dm_udev_set_checking(0);
+ }
+
+ udev_unref(udev);
+ return 1;
+}
+
+static int _udevcreatecookie(int argc, char **argv,
+ void *data __attribute__((unused)))
+{
+ uint32_t cookie;
+
+ if (!dm_udev_create_cookie(&cookie))
+ return 0;
+
+ if (cookie)
+ printf("0x%08" PRIX32 "\n", cookie);
+
+ return 1;
+}
+
+static int _udevreleasecookie(int argc, char **argv,
+ void *data __attribute__((unused)))
+{
+ if (argv[1] && !(_udev_cookie = _get_cookie_value(argv[1])))
+ return 0;
+
+ if (!_udev_cookie) {
+ log_error("No udev transaction cookie given.");
+ return 0;
+ }
+
+ return dm_udev_wait(_udev_cookie);
+}
+
+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')
+ printf("\n");
+
+ return ret;
+}
+
+static int _udevcomplete_all(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+ int max_id, id, sid;
+ struct seminfo sinfo;
+ struct semid_ds sdata;
+ int counter = 0;
+
+ if (!_switches[YES_ARG]) {
+ log_warn("This operation will destroy all semaphores with keys "
+ "that have a prefix %" PRIu16 " (0x%" PRIx16 ").",
+ 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) {
+ if (semctl(sid, 0, IPC_RMID, 0) < 0) {
+ log_error("Could not cleanup notification semaphore "
+ "with semid %d and cookie value "
+ "%" PRIu32 " (0x%" PRIx32 ")", sid,
+ sdata.sem_perm.__key, sdata.sem_perm.__key);
+ continue;
+ }
+
+ counter++;
+ }
+ }
+
+ log_print("%d semaphores with keys prefixed by "
+ "%" PRIu16 " (0x%" PRIx16 ") destroyed.",
+ counter, DM_COOKIE_MAGIC, DM_COOKIE_MAGIC);
+
+ return 1;
+}
+
+static int _udevcookies(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+ int max_id, id, sid;
+ struct seminfo sinfo;
+ struct semid_ds sdata;
+ int val;
+ char *time_str;
+
+ 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\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;
+ }
+
+ time_str = ctime((const time_t *) &sdata.sem_otime);
+
+ printf("0x%-10x %-10d %-10d %s", sdata.sem_perm.__key,
+ sid, val, time_str ? time_str : "unknown\n");
+ }
+ }
+
+ return 1;
+}
+#endif /* UDEV_SYNC_SUPPORT */
+
+static int _version(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+ 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);
+
+ 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[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;
+
+ r = dm_task_run(dmt);
+
+ if (r && display && _switches[VERBOSE_ARG])
+ r = _display_info(dmt);
+
+ out:
+ if (!_udev_cookie && udev_wait_flag)
+ (void) dm_udev_wait(cookie);
+
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _suspend(int argc, char **argv, void *data __attribute__((unused)))
+{
+ return _simple(DM_DEVICE_SUSPEND, argc > 1 ? argv[1] : NULL, 0, 1);
+}
+
+static int _resume(int argc, char **argv, void *data __attribute__((unused)))
+{
+ return _simple(DM_DEVICE_RESUME, argc > 1 ? argv[1] : NULL, 0, 1);
+}
+
+static int _clear(int argc, char **argv, void *data __attribute__((unused)))
+{
+ return _simple(DM_DEVICE_CLEAR, argc > 1 ? argv[1] : NULL, 0, 1);
+}
+
+static int _wait(int argc, char **argv, void *data __attribute__((unused)))
+{
+ const char *name = NULL;
+
+ if (!_switches[UUID_ARG] && !_switches[MAJOR_ARG]) {
+ if (argc == 1) {
+ err("No device specified.");
+ return 0;
+ }
+ name = argv[1];
+ argc--, argv++;
+ }
+
+ return _simple(DM_DEVICE_WAITEVENT, name,
+ (argc > 1) ? (uint32_t) atoi(argv[argc - 1]) : 0, 1);
+}
+
+static int _process_all(int argc, char **argv, int silent,
+ int (*fn) (int argc, char **argv, void *data))
+{
+ 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 (!dm_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(argc, argv, names))
+ 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 (!dm_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(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data)
+{
+ struct dm_names *names = (struct dm_names *) data;
+ struct dm_task *dmt;
+ const char *name;
+ uint64_t size;
+ int r = 0;
+
+ if (data)
+ name = names->name;
+ else
+ name = argv[1];
+
+ size = _get_device_size(name);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_RELOAD)))
+ return 0;
+
+ if (!_set_task_device(dmt, name, 0))
+ goto error;
+
+ if (!dm_task_add_target(dmt, UINT64_C(0), size, "error", ""))
+ goto error;
+
+ if (_switches[READ_ONLY] && !dm_task_set_ro(dmt))
+ goto error;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto error;
+
+ if (_switches[INACTIVE_ARG] && !dm_task_query_inactive_table(dmt))
+ goto error;
+
+ if (!dm_task_run(dmt))
+ goto error;
+
+ if (!_simple(DM_DEVICE_RESUME, name, 0, 0)) {
+ _simple(DM_DEVICE_CLEAR, name, 0, 0);
+ goto error;
+ }
+
+ r = 1;
+
+error:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _remove(int argc, char **argv, void *data __attribute__((unused)))
+{
+ if (_switches[FORCE_ARG] && argc > 1)
+ (void) _error_device(argc, argv, NULL);
+
+ return _simple(DM_DEVICE_REMOVE, argc > 1 ? argv[1] : NULL, 0, 0);
+}
+
+static int _count_devices(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+ _num_devices++;
+
+ return 1;
+}
+
+static int _remove_all(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+ 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(argc, argv, 1, _count_devices);
+
+ /* No devices left? */
+ if (!_num_devices)
+ return r;
+
+ r |= _process_all(argc, argv, 1, _error_device);
+ r |= _simple(DM_DEVICE_REMOVE_ALL, "", 0, 0) | dm_mknodes(NULL);
+
+ _num_devices = 0;
+ r |= _process_all(argc, argv, 1, _count_devices);
+ if (!_num_devices)
+ return r;
+
+ fprintf(stderr, "Unable to remove %d device(s).\n", _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(int argc, char **argv, void *data __attribute__((unused)))
+{
+ return dm_mknodes(argc > 1 ? argv[1] : NULL);
+}
+
+static int _exec_command(const char *name)
+{
+ int n;
+ 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;
+
+ n = snprintf(path, sizeof(path), "%s/%s", dm_dir(), name);
+ if (n < 0 || n > (int) sizeof(path) - 1)
+ return 0;
+
+ if (!argc) {
+ c = _command;
+ 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) {
+ err("Too many args to --exec\n");
+ 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;
+}
+
+static int _status(int argc, char **argv, void *data)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ void *next = NULL;
+ uint64_t start, length;
+ char *target_type = NULL;
+ char *params, *c;
+ int cmd;
+ struct dm_names *names = (struct dm_names *) data;
+ const char *name = NULL;
+ int matched = 0;
+ int ls_only = 0;
+ struct dm_info info;
+
+ if (data)
+ name = names->name;
+ else {
+ if (argc == 1 && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
+ return _process_all(argc, argv, 0, _status);
+ if (argc == 2)
+ name = argv[1];
+ }
+
+ if (!strcmp(argv[0], "table"))
+ cmd = DM_DEVICE_TABLE;
+ else
+ cmd = DM_DEVICE_STATUS;
+
+ if (!strcmp(argv[0], "ls"))
+ ls_only = 1;
+
+ if (!(dmt = dm_task_create(cmd)))
+ 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 (!dm_task_run(dmt))
+ goto out;
+
+ if (!dm_task_get_info(dmt, &info) || !info.exists)
+ 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 ||
+ _switches[VERBOSE_ARG])
+ _display_dev(dmt, name);
+ next = NULL;
+ } else if (!_switches[EXEC_ARG] || !_command ||
+ _switches[VERBOSE_ARG]) {
+ if (!matched && _switches[VERBOSE_ARG])
+ _display_info(dmt);
+ if (data && !_switches[VERBOSE_ARG])
+ printf("%s: ", name);
+ if (target_type) {
+ /* Suppress encryption key */
+ if (!_switches[SHOWKEYS_ARG] &&
+ cmd == DM_DEVICE_TABLE &&
+ !strcmp(target_type, "crypt")) {
+ c = params;
+ while (*c && *c != ' ')
+ c++;
+ if (*c)
+ c++;
+ while (*c && *c != ' ')
+ *c++ = '0';
+ }
+ printf("%" PRIu64 " %" PRIu64 " %s %s",
+ start, length, target_type, params);
+ }
+ printf("\n");
+ }
+ matched = 1;
+ } while (next);
+
+ if (data && _switches[VERBOSE_ARG] && matched && !ls_only)
+ printf("\n");
+
+ if (matched && _switches[EXEC_ARG] && _command && !_exec_command(name))
+ goto out;
+
+ r = 1;
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+/* Show target names and their version numbers */
+static int _targets(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data __attribute__((unused)))
+{
+ 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 (!dm_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(int argc, char **argv, void *data)
+{
+ int r = 0;
+
+ struct dm_task *dmt;
+ struct dm_names *names = (struct dm_names *) data;
+ char *name = NULL;
+
+ if (data)
+ name = names->name;
+ else {
+ if (argc == 1 && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
+ return _process_all(argc, argv, 0, _info);
+ if (argc == 2)
+ name = argv[1];
+ }
+
+ 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 (!dm_task_run(dmt))
+ goto out;
+
+ r = _display_info(dmt);
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _deps(int argc, char **argv, void *data)
+{
+ int r = 0;
+ uint32_t i;
+ struct dm_deps *deps;
+ struct dm_task *dmt;
+ struct dm_info info;
+ struct dm_names *names = (struct dm_names *) data;
+ char *name = NULL;
+
+ if (data)
+ name = names->name;
+ else {
+ if (argc == 1 && !_switches[UUID_ARG] && !_switches[MAJOR_ARG])
+ return _process_all(argc, argv, 0, _deps);
+ if (argc == 2)
+ name = argv[1];
+ }
+
+ 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 (!dm_task_run(dmt))
+ goto out;
+
+ if (!dm_task_get_info(dmt, &info))
+ goto out;
+
+ if (!(deps = dm_task_get_deps(dmt)))
+ goto out;
+
+ if (!info.exists) {
+ printf("Device does not exist.\n");
+ r = 1;
+ goto out;
+ }
+
+ if (_switches[VERBOSE_ARG])
+ _display_info(dmt);
+
+ if (data && !_switches[VERBOSE_ARG])
+ printf("%s: ", name);
+ printf("%d dependencies\t:", deps->count);
+
+ for (i = 0; i < deps->count; i++)
+ printf(" (%d, %d)",
+ (int) MAJOR(deps->device[i]),
+ (int) MINOR(deps->device[i]));
+ printf("\n");
+
+ if (data && _switches[VERBOSE_ARG])
+ printf("\n");
+
+ r = 1;
+
+ out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static int _display_name(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data)
+{
+ struct dm_names *names = (struct dm_names *) data;
+
+ 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_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(']');
+}
+
+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;
+
+ /* 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])
+ 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_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;
+ // FIXME _display_tree_targets(name, 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(int argc __attribute__((unused)), char **argv __attribute__((unused)), void *data)
+{
+ struct dm_names *names = (struct dm_names *) data;
+
+ if (!dm_tree_add_dev(_dtree, (unsigned) MAJOR(names->dev), (unsigned) MINOR(names->dev)))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Create and walk dependency tree
+ */
+static int _build_whole_deptree(void)
+{
+ if (_dtree)
+ return 1;
+
+ if (!(_dtree = dm_tree_create()))
+ return 0;
+
+ if (!_process_all(0, NULL, 0, _add_dep))
+ return 0;
+
+ return 1;
+}
+
+static int _display_tree(int argc __attribute__((unused)),
+ char **argv __attribute__((unused)),
+ void *data __attribute__((unused)))
+{
+ if (!_build_whole_deptree())
+ return 0;
+
+ _display_tree_walk_children(dm_tree_find_node(_dtree, 0, 0), 0);
+
+ return 1;
+}
+
+/*
+ * 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 _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_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_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_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[DM_MAX_TYPE_NAME], *repstr;
+ const struct dm_info *info = data;
+
+ if (!dm_pool_begin_object(mem, 8)) {
+ log_error("dm_pool_begin_object failed");
+ return 0;
+ }
+
+ 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(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field, const void *data,
+ void *private)
+{
+ const struct dm_deps *deps = data;
+ int i;
+ char buf[DM_MAX_TYPE_NAME], *repstr;
+
+ if (!dm_pool_begin_object(mem, 16)) {
+ log_error("dm_pool_begin_object failed");
+ return 0;
+ }
+
+ for (i = 0; i < deps->count; i++) {
+ if (dm_snprintf(buf, sizeof(buf), "%d:%d",
+ (int) MAJOR(deps->device[i]),
+ (int) MINOR(deps->device[i])) < 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_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 **) 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 **) 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 **) 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 **) data);
+}
+
+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 const struct dm_report_object_type _report_types[] = {
+ { DR_TASK, "Mapped Device Name", "", _task_get_obj },
+ { DR_INFO, "Mapped Device Information", "", _info_get_obj },
+ { DR_DEPS, "Mapped Device Relationship Information", "", _deps_get_obj },
+ { DR_TREE, "Mapped Device Relationship Information", "", _tree_get_obj },
+ { DR_NAME, "Mapped Device Name Components", "", _split_name_get_obj },
+ { 0, "", "", NULL },
+};
+
+/* Column definitions */
+#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 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, "UUID", 32, dm_uuid, "uuid", "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 in sectors.")
+
+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, "DevNames", 8, dm_deps_names, "devs_used", "List of names of mapped devices used by this one.")
+FIELD_F(DEPS, STR, "DevNos", 6, dm_deps, "devnos_used", "List of device numbers of 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.")
+
+{0, 0, 0, 0, "", "", NULL, NULL},
+/* *INDENT-ON* */
+};
+
+#undef STR
+#undef NUM
+#undef FIELD_O
+#undef FIELD_F
+
+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";
+
+static int _report_init(struct command *c)
+{
+ char *options = (char *) default_report_options;
+ const char *keys = "";
+ const char *separator = " ";
+ 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 (c && !strcmp(c->name, "splitname"))
+ options = (char *) splitname_report_options;
+
+ /* 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]) {
+ if (*_string_args[OPTIONS_ARG] != '+')
+ options = _string_args[OPTIONS_ARG];
+ else {
+ len = strlen(default_report_options) +
+ strlen(_string_args[OPTIONS_ARG]) + 1;
+ if (!(options = dm_malloc(len))) {
+ err("Failed to allocate option string.");
+ return 0;
+ }
+ if (dm_snprintf(options, len, "%s,%s",
+ default_report_options,
+ &_string_args[OPTIONS_ARG][1]) < 0) {
+ err("snprintf failed");
+ goto out;
+ }
+ }
+ }
+
+ if (_switches[SORT_ARG] && _string_args[SORT_ARG]) {
+ keys = _string_args[SORT_ARG];
+ buffered = 1;
+ if (c && (!strcmp(c->name, "status") || !strcmp(c->name, "table"))) {
+ err("--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 (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(&_report_type,
+ _report_types, _report_fields,
+ options, separator, flags, keys, NULL)))
+ goto out;
+
+ if ((_report_type & DR_TREE) && !_build_whole_deptree()) {
+ err("Internal device dependency tree creation failed.");
+ goto out;
+ }
+
+ if (field_prefixes)
+ dm_report_set_output_field_name_prefix(_report, "dm_");
+
+ r = 1;
+
+out:
+ if (!strcasecmp(options, "help") || !strcmp(options, "?"))
+ r = 1;
+
+ if (len)
+ dm_free(options);
+
+ return r;
+}
+
+/*
+ * List devices
+ */
+static int _ls(int argc, char **argv, void *data)
+{
+ if ((_switches[TARGET_ARG] && _target) ||
+ (_switches[EXEC_ARG] && _command))
+ return _status(argc, argv, data);
+ else if ((_switches[TREE_ARG]))
+ return _display_tree(argc, argv, data);
+ else
+ return _process_all(argc, argv, 0, _display_name);
+}
+
+static int _help(int argc, char **argv, void *data);
+
+/*
+ * Dispatch table
+ */
+static struct command _commands[] = {
+ {"help", "[-c|-C|--columns]", 0, 0, _help},
+ {"create", "<dev_name> [-j|--major <major> -m|--minor <minor>]\n"
+ "\t [-U|--uid <uid>] [-G|--gid <gid>] [-M|--mode <octal_mode>]\n"
+ "\t [-u|uuid <uuid>]\n"
+ "\t [--notable | --table <table> | <table_file>]",
+ 1, 2, _create},
+ {"remove", "[-f|--force] <device>", 0, 1, _remove},
+ {"remove_all", "[-f|--force]", 0, 0, _remove_all},
+ {"suspend", "[--noflush] <device>", 0, 1, _suspend},
+ {"resume", "<device>", 0, 1, _resume},
+ {"load", "<device> [<table_file>]", 0, 2, _load},
+ {"clear", "<device>", 0, 1, _clear},
+ {"reload", "<device> [<table_file>]", 0, 2, _load},
+ {"rename", "<device> [--setuuid] <new_name_or_uuid>", 1, 2, _rename},
+ {"message", "<device> <sector> <message>", 2, -1, _message},
+ {"ls", "[--target <target_type>] [--exec <command>] [--tree [-o options]]", 0, 0, _ls},
+ {"info", "[<device>]", 0, 1, _info},
+ {"deps", "[<device>]", 0, 1, _deps},
+ {"status", "[<device>] [--target <target_type>]", 0, 1, _status},
+ {"table", "[<device>] [--target <target_type>] [--showkeys]", 0, 1, _status},
+ {"wait", "<device> [<event_nr>]", 0, 2, _wait},
+ {"mknodes", "[<device>]", 0, 1, _mknodes},
+ {"udevcreatecookie", "", 0, 0, _udevcreatecookie},
+ {"udevreleasecookie", "[<cookie>]", 0, 1, _udevreleasecookie},
+ {"udevflags", "<cookie>", 1, 1, _udevflags},
+ {"udevcomplete", "<cookie>", 1, 1, _udevcomplete},
+ {"udevcomplete_all", "", 0, 0, _udevcomplete_all},
+ {"udevcookies", "", 0, 0, _udevcookies},
+ {"targets", "", 0, 0, _targets},
+ {"version", "", 0, 0, _version},
+ {"setgeometry", "<device> <cyl> <head> <sect> <start>", 5, 5, _setgeometry},
+ {"splitname", "<device> [<subsystem>]", 1, 2, _splitname},
+ {NULL, NULL, 0, 0, NULL}
+};
+
+static void _usage(FILE *out)
+{
+ int i;
+
+ fprintf(out, "Usage:\n\n");
+ fprintf(out, "dmsetup [--version] [-h|--help [-c|-C|--columns]]\n"
+ " [-v|--verbose [-v|--verbose ...]]\n"
+ " [-r|--readonly] [--noopencount] [--nolockfs] [--inactive]\n"
+ " [--udevcookie] [--noudevrules] [--noudevsync] [-y|--yes]\n"
+ " [--readahead [+]<sectors>|auto|none]\n"
+ " [-c|-C|--columns] [-o <fields>] [-O|--sort <sort_fields>]\n"
+ " [--nameprefixes] [--noheadings] [--separator <separator>]\n\n");
+ for (i = 0; _commands[i].name; i++)
+ fprintf(out, "\t%s %s\n", _commands[i].name, _commands[i].help);
+ fprintf(out, "\n<device> may be device name or -u <uuid> or "
+ "-j <major> -m <minor>\n");
+ fprintf(out, "<fields> are comma-separated. Use 'help -c' for list.\n");
+ fprintf(out, "Table_file contents may be supplied on stdin.\n");
+ fprintf(out, "Tree options are: ascii, utf, vt100; compact, inverted, notrunc;\n"
+ " [no]device, active, open, rw and uuid.\n");
+ fprintf(out, "\n");
+}
+
+static void _losetup_usage(FILE *out)
+{
+ fprintf(out, "Usage:\n\n");
+ fprintf(out, "losetup [-d|-a] [-e encryption] "
+ "[-o offset] [-f|loop_device] [file]\n\n");
+}
+
+static int _help(int argc __attribute__((unused)),
+ char **argv __attribute__((unused)),
+ void *data __attribute__((unused)))
+{
+ _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(NULL);
+ }
+
+ return 1;
+}
+
+static struct command *_find_command(const char *name)
+{
+ int i;
+
+ for (i = 0; _commands[i].name; i++)
+ if (!strcmp(_commands[i].name, name))
+ return _commands + i;
+
+ return NULL;
+}
+
+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, "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 {
+ fprintf(stderr, "Tree options not recognised: %s\n", 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;
+}
+
+/*
+ * 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 */
+#endif
+ return _path;
+}
+
+static char *parse_loop_device_name(const char *dev, const char *dev_dir)
+{
+ char *buf;
+ char *device;
+
+ if (!(buf = dm_malloc(PATH_MAX)))
+ return NULL;
+
+ if (dev[0] == '/') {
+ if (!(device = _get_abspath(dev)))
+ goto error;
+
+ if (strncmp(device, dev_dir, strlen(dev_dir)))
+ goto error;
+
+ /* 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 error;
+
+ strncpy(buf, strrchr(device, '/') + 1, (size_t) PATH_MAX);
+ dm_free(device);
+
+ } else {
+ /* check for device number */
+ if (!strncmp(dev, "loop", strlen("loop")))
+ strncpy(buf, dev, (size_t) PATH_MAX);
+ else
+ goto error;
+ }
+
+ return buf;
+
+error:
+ dm_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 error;
+
+ if (fstat(fd, &fbuf))
+ goto error;
+
+ size = (fbuf.st_size - off);
+ sectors = size >> SECTOR_SHIFT;
+
+ if (_switches[VERBOSE_ARG])
+ fprintf(stderr, "losetup: set loop size to %llukB "
+ "(%llu sectors)\n", (long long unsigned) sectors >> 1,
+ (long long unsigned) sectors);
+
+#ifdef HAVE_SYS_STATVFS_H
+ if (fstatvfs(fd, &fsbuf))
+ goto error;
+
+ /* FIXME Fragment size currently unused */
+ blksize = fsbuf.f_frsize;
+#endif
+
+ close(fd);
+
+ 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)
+ fprintf(stderr, "Table: %s\n", table);
+
+ return 1;
+
+error:
+ if (fd > -1)
+ close(fd);
+ return 0;
+}
+
+static int _process_losetup_switches(const char *base, int *argc, char ***argv,
+ const char *dev_dir)
+{
+ static int ind;
+ 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 ((ind = -1, c = GETOPTLONG_FN(*argc, *argv, "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]++;
+ }
+
+ *argv += optind ;
+ *argc -= optind ;
+
+ if (encrypt_loop){
+ fprintf(stderr, "%s: Sorry, cryptoloop is not yet implemented "
+ "in this version.\n", base);
+ return 0;
+ }
+
+ if (show_all) {
+ fprintf(stderr, "%s: Sorry, show all is not yet implemented "
+ "in this version.\n", base);
+ return 0;
+ }
+
+ if (find) {
+ fprintf(stderr, "%s: Sorry, find is not yet implemented "
+ "in this version.\n", base);
+ if (!*argc)
+ return 0;
+ }
+
+ if (!*argc) {
+ fprintf(stderr, "%s: Please specify loop_device.\n", base);
+ _losetup_usage(stderr);
+ return 0;
+ }
+
+ if (!(device_name = parse_loop_device_name((*argv)[0], dev_dir))) {
+ fprintf(stderr, "%s: Could not parse loop_device %s\n",
+ base, (*argv)[0]);
+ _losetup_usage(stderr);
+ return 0;
+ }
+
+ if (delete) {
+ *argc = 2;
+
+ (*argv)[1] = device_name;
+ (*argv)[0] = (char *) "remove";
+
+ return 1;
+ }
+
+ if (*argc != 2) {
+ fprintf(stderr, "%s: Too few arguments\n", base);
+ _losetup_usage(stderr);
+ dm_free(device_name);
+ return 0;
+ }
+
+ /* FIXME move these to make them available to native dmsetup */
+ if (!(loop_file = _get_abspath((*argv)[(find) ? 0 : 1]))) {
+ fprintf(stderr, "%s: Could not parse loop file name %s\n",
+ base, (*argv)[1]);
+ _losetup_usage(stderr);
+ dm_free(device_name);
+ return 0;
+ }
+
+ /* FIXME Missing free */
+ _table = dm_malloc(LOOP_TABLE_SIZE);
+ if (!_loop_table(_table, (size_t) LOOP_TABLE_SIZE, loop_file, device_name, offset)) {
+ fprintf(stderr, "Could not build device-mapper table for %s\n", (*argv)[0]);
+ dm_free(device_name);
+ return 0;
+ }
+ _switches[TABLE_ARG]++;
+
+ (*argv)[0] = (char *) "create";
+ (*argv)[1] = device_name ;
+
+ return 1;
+}
+
+static int _process_switches(int *argc, char ***argv, const char *dev_dir)
+{
+ char *base, *namebase, *s;
+ static int ind;
+ int c, r;
+
+#ifdef HAVE_GETOPTLONG
+ static struct option long_options[] = {
+ {"readonly", 0, &ind, READ_ONLY},
+ {"columns", 0, &ind, COLS_ARG},
+ {"exec", 1, &ind, EXEC_ARG},
+ {"force", 0, &ind, FORCE_ARG},
+ {"gid", 1, &ind, GID_ARG},
+ {"help", 0, &ind, HELP_ARG},
+ {"inactive", 0, &ind, INACTIVE_ARG},
+ {"major", 1, &ind, MAJOR_ARG},
+ {"minor", 1, &ind, MINOR_ARG},
+ {"mode", 1, &ind, MODE_ARG},
+ {"nameprefixes", 0, &ind, NAMEPREFIXES_ARG},
+ {"noflush", 0, &ind, NOFLUSH_ARG},
+ {"noheadings", 0, &ind, NOHEADINGS_ARG},
+ {"nolockfs", 0, &ind, NOLOCKFS_ARG},
+ {"noopencount", 0, &ind, NOOPENCOUNT_ARG},
+ {"notable", 0, &ind, NOTABLE_ARG},
+ {"udevcookie", 1, &ind, UDEVCOOKIE_ARG},
+ {"noudevrules", 0, &ind, NOUDEVRULES_ARG},
+ {"noudevsync", 0, &ind, NOUDEVSYNC_ARG},
+ {"options", 1, &ind, OPTIONS_ARG},
+ {"readahead", 1, &ind, READAHEAD_ARG},
+ {"rows", 0, &ind, ROWS_ARG},
+ {"separator", 1, &ind, SEPARATOR_ARG},
+ {"setuuid", 0, &ind, SETUUID_ARG},
+ {"showkeys", 0, &ind, SHOWKEYS_ARG},
+ {"sort", 1, &ind, SORT_ARG},
+ {"table", 1, &ind, TABLE_ARG},
+ {"target", 1, &ind, TARGET_ARG},
+ {"tree", 0, &ind, TREE_ARG},
+ {"uid", 1, &ind, UID_ARG},
+ {"uuid", 1, &ind, UUID_ARG},
+ {"unbuffered", 0, &ind, UNBUFFERED_ARG},
+ {"unquoted", 0, &ind, UNQUOTED_ARG},
+ {"verbose", 1, &ind, VERBOSE_ARG},
+ {"version", 0, &ind, VERSION_ARG},
+ {"yes", 0, &ind, YES_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;
+
+ namebase = strdup((*argv)[0]);
+ base = basename(namebase);
+
+ if (!strcmp(base, "devmap_name")) {
+ free(namebase);
+ _switches[COLS_ARG]++;
+ _switches[NOHEADINGS_ARG]++;
+ _switches[OPTIONS_ARG]++;
+ _switches[MAJOR_ARG]++;
+ _switches[MINOR_ARG]++;
+ _string_args[OPTIONS_ARG] = (char *) "name";
+
+ if (*argc == 3) {
+ _int_args[MAJOR_ARG] = atoi((*argv)[1]);
+ _int_args[MINOR_ARG] = atoi((*argv)[2]);
+ *argc -= 2;
+ *argv += 2;
+ } else if ((*argc == 2) &&
+ (2 == sscanf((*argv)[1], "%i:%i",
+ &_int_args[MAJOR_ARG],
+ &_int_args[MINOR_ARG]))) {
+ *argc -= 1;
+ *argv += 1;
+ } else {
+ fprintf(stderr, "Usage: devmap_name <major> <minor>\n");
+ return 0;
+ }
+
+ (*argv)[0] = (char *) "info";
+ return 1;
+ }
+
+ if (!strcmp(base, "losetup") || !strcmp(base, "dmlosetup")){
+ r = _process_losetup_switches(base, argc, argv, dev_dir);
+ free(namebase);
+ return r;
+ }
+
+ free(namebase);
+
+ optarg = 0;
+ optind = OPTIND_INIT;
+ while ((ind = -1, c = GETOPTLONG_FN(*argc, *argv, "cCfG:hj:m:M:no:O:ru:U:vy",
+ long_options, NULL)) != -1) {
+ if (c == ':' || c == '?')
+ return 0;
+ if (c == 'h' || ind == HELP_ARG)
+ _switches[HELP_ARG]++;
+ if (c == 'c' || c == 'C' || ind == COLS_ARG)
+ _switches[COLS_ARG]++;
+ if (c == 'f' || ind == FORCE_ARG)
+ _switches[FORCE_ARG]++;
+ if (c == 'r' || ind == READ_ONLY)
+ _switches[READ_ONLY]++;
+ if (c == 'j' || ind == MAJOR_ARG) {
+ _switches[MAJOR_ARG]++;
+ _int_args[MAJOR_ARG] = atoi(optarg);
+ }
+ if (c == 'm' || ind == MINOR_ARG) {
+ _switches[MINOR_ARG]++;
+ _int_args[MINOR_ARG] = atoi(optarg);
+ }
+ if (c == 'n' || ind == NOTABLE_ARG)
+ _switches[NOTABLE_ARG]++;
+ if (c == 'o' || ind == OPTIONS_ARG) {
+ _switches[OPTIONS_ARG]++;
+ _string_args[OPTIONS_ARG] = optarg;
+ }
+ if (ind == SEPARATOR_ARG) {
+ _switches[SEPARATOR_ARG]++;
+ _string_args[SEPARATOR_ARG] = optarg;
+ }
+ if (c == 'O' || ind == SORT_ARG) {
+ _switches[SORT_ARG]++;
+ _string_args[SORT_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 == UDEVCOOKIE_ARG) {
+ _switches[UDEVCOOKIE_ARG]++;
+ _udev_cookie = _get_cookie_value(optarg);
+ }
+ if (ind == NOUDEVRULES_ARG)
+ _switches[NOUDEVRULES_ARG]++;
+ if (ind == NOUDEVSYNC_ARG)
+ _switches[NOUDEVSYNC_ARG]++;
+ if (c == 'G' || ind == GID_ARG) {
+ _switches[GID_ARG]++;
+ _int_args[GID_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 */
+ _int_args[MODE_ARG] = (int) strtol(optarg, NULL, 8);
+ }
+ if ((ind == EXEC_ARG)) {
+ _switches[EXEC_ARG]++;
+ _command = optarg;
+ }
+ if ((ind == TARGET_ARG)) {
+ _switches[TARGET_ARG]++;
+ _target = optarg;
+ }
+ if ((ind == INACTIVE_ARG))
+ _switches[INACTIVE_ARG]++;
+ if ((ind == NAMEPREFIXES_ARG))
+ _switches[NAMEPREFIXES_ARG]++;
+ if ((ind == NOFLUSH_ARG))
+ _switches[NOFLUSH_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 == 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]++;
+ _table = optarg;
+ }
+ 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[MAJOR_ARG] && !_switches[MINOR_ARG]) ||
+ (!_switches[MAJOR_ARG] && _switches[MINOR_ARG])) {
+ fprintf(stderr, "Please specify both major number and "
+ "minor number.\n");
+ return 0;
+ }
+
+ if (_switches[TREE_ARG] && !_process_tree_options(_string_args[OPTIONS_ARG]))
+ return 0;
+
+ if (_switches[TABLE_ARG] && _switches[NOTABLE_ARG]) {
+ fprintf(stderr, "--table and --notable are incompatible.\n");
+ return 0;
+ }
+
+ *argv += optind;
+ *argc -= optind;
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ struct command *c;
+ int r = 1;
+ const char *dev_dir;
+
+ (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)) {
+ fprintf(stderr, "Invalid DM_DEV_DIR environment variable value.\n");
+ goto out;
+ }
+ } else
+ dev_dir = DEFAULT_DM_DEV_DIR;
+
+ if (!_process_switches(&argc, &argv, dev_dir)) {
+ fprintf(stderr, "Couldn't process command line.\n");
+ goto out;
+ }
+
+ if (_switches[HELP_ARG]) {
+ c = _find_command("help");
+ goto doit;
+ }
+
+ if (_switches[VERSION_ARG]) {
+ c = _find_command("version");
+ goto doit;
+ }
+
+ if (argc == 0) {
+ _usage(stderr);
+ goto out;
+ }
+
+ if (!(c = _find_command(argv[0]))) {
+ fprintf(stderr, "Unknown command\n");
+ _usage(stderr);
+ goto out;
+ }
+
+ if (argc < c->min_args + 1 ||
+ (c->max_args >= 0 && argc > c->max_args + 1)) {
+ fprintf(stderr, "Incorrect number of arguments\n");
+ _usage(stderr);
+ goto out;
+ }
+
+ if (!_switches[COLS_ARG] && !strcmp(c->name, "splitname"))
+ _switches[COLS_ARG]++;
+
+ if (_switches[COLS_ARG]) {
+ if (!_report_init(c))
+ goto out;
+ if (!_report) {
+ if (!strcmp(c->name, "info"))
+ r = 0; /* info -c -o help */
+ goto out;
+ }
+ }
+
+ #ifdef UDEV_SYNC_SUPPORT
+ if (!_set_up_udev_support(dev_dir))
+ goto out;
+ #endif
+
+ doit:
+ if (!c->fn(argc, argv, NULL)) {
+ fprintf(stderr, "Command failed\n");
+ goto out;
+ }
+
+ r = 0;
+
+out:
+ if (_report) {
+ dm_report_output(_report);
+ dm_report_free(_report);
+ }
+
+ if (_dtree)
+ dm_tree_free(_dtree);
+
+ return r;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+int dumpconfig(struct cmd_context *cmd, int argc, char **argv)
+{
+ const char *file = arg_str_value(cmd, file_ARG, NULL);
+
+ if (!write_config_file(cmd->cft, file, argc, argv)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ return ECMD_PROCESSED;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+int formats(struct cmd_context *cmd, int argc __attribute__((unused)),
+ char **argv __attribute__((unused)))
+{
+ display_formats(cmd);
+
+ return ECMD_PROCESSED;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static int lvchange_permission(struct cmd_context *cmd,
+ struct logical_volume *lv)
+{
+ uint32_t lv_access;
+ struct lvinfo info;
+ int r = 0;
+
+ lv_access = arg_uint_value(cmd, permission_ARG, 0);
+
+ if ((lv_access & LVM_WRITE) && (lv->status & LVM_WRITE)) {
+ log_error("Logical volume \"%s\" is already writable",
+ lv->name);
+ return 0;
+ }
+
+ if (!(lv_access & LVM_WRITE) && !(lv->status & LVM_WRITE)) {
+ log_error("Logical volume \"%s\" is already read only",
+ lv->name);
+ return 0;
+ }
+
+ if ((lv->status & MIRRORED) && (vg_is_clustered(lv->vg)) &&
+ lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) {
+ log_error("Cannot change permissions of mirror \"%s\" "
+ "while active.", lv->name);
+ return 0;
+ }
+
+ if (lv_access & LVM_WRITE) {
+ lv->status |= LVM_WRITE;
+ log_verbose("Setting logical volume \"%s\" read/write",
+ lv->name);
+ } else {
+ lv->status &= ~LVM_WRITE;
+ log_verbose("Setting logical volume \"%s\" read-only",
+ lv->name);
+ }
+
+ log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+ if (!vg_write(lv->vg))
+ return_0;
+
+ if (!suspend_lv(cmd, lv)) {
+ log_error("Failed to lock %s", lv->name);
+ vg_revert(lv->vg);
+ goto out;
+ }
+
+ if (!vg_commit(lv->vg)) {
+ if (!resume_lv(cmd, lv))
+ stack;
+ goto_out;
+ }
+
+ log_very_verbose("Updating permissions for \"%s\" in kernel", lv->name);
+ if (!resume_lv(cmd, lv)) {
+ log_error("Problem reactivating %s", lv->name);
+ goto out;
+ }
+
+ r = 1;
+out:
+ backup(lv->vg);
+ return r;
+}
+
+static int lvchange_monitoring(struct cmd_context *cmd,
+ struct logical_volume *lv)
+{
+ struct lvinfo info;
+
+ if (!lv_info(cmd, lv, 0, &info, 0, 0) || !info.exists) {
+ log_error("Logical volume, %s, is not active", lv->name);
+ return 0;
+ }
+
+ /* do not monitor pvmove lv's */
+ if (lv->status & PVMOVE)
+ return 1;
+
+ if ((dmeventd_monitor_mode() != DMEVENTD_MONITOR_IGNORE) &&
+ !monitor_dev_for_events(cmd, lv, 0, dmeventd_monitor_mode()))
+ return_0;
+
+ return 1;
+}
+
+static int lvchange_background_polling(struct cmd_context *cmd,
+ struct logical_volume *lv)
+{
+ struct lvinfo info;
+
+ if (!lv_info(cmd, lv, 0, &info, 0, 0) || !info.exists) {
+ log_error("Logical volume, %s, is not active", lv->name);
+ return 0;
+ }
+
+ if (background_polling())
+ lv_spawn_background_polling(cmd, lv);
+
+ return 1;
+}
+
+static int lvchange_availability(struct cmd_context *cmd,
+ struct logical_volume *lv)
+{
+ int activate;
+
+ activate = arg_uint_value(cmd, available_ARG, 0);
+
+ if (activate == CHANGE_ALN) {
+ log_verbose("Deactivating logical volume \"%s\" locally",
+ lv->name);
+ if (!deactivate_lv_local(cmd, lv))
+ return_0;
+ } else if (activate == CHANGE_AN) {
+ log_verbose("Deactivating logical volume \"%s\"", lv->name);
+ if (!deactivate_lv(cmd, lv))
+ return_0;
+ } else {
+ if (lv_is_origin(lv) || (activate == CHANGE_AE)) {
+ log_verbose("Activating logical volume \"%s\" "
+ "exclusively", lv->name);
+ if (!activate_lv_excl(cmd, lv))
+ return_0;
+ } else if (activate == CHANGE_ALY) {
+ log_verbose("Activating logical volume \"%s\" locally",
+ lv->name);
+ if (!activate_lv_local(cmd, lv))
+ return_0;
+ } else {
+ log_verbose("Activating logical volume \"%s\"",
+ lv->name);
+ if (!activate_lv(cmd, lv))
+ return_0;
+ }
+
+ if (background_polling())
+ lv_spawn_background_polling(cmd, lv);
+ }
+
+ return 1;
+}
+
+static int lvchange_refresh(struct cmd_context *cmd, struct logical_volume *lv)
+{
+ log_verbose("Refreshing logical volume \"%s\" (if active)", lv->name);
+
+ return lv_refresh(cmd, lv);
+}
+
+static int lvchange_resync(struct cmd_context *cmd,
+ struct logical_volume *lv)
+{
+ int active = 0;
+ int monitored;
+ struct lvinfo info;
+ struct logical_volume *log_lv;
+
+ if (!(lv->status & MIRRORED)) {
+ log_error("Unable to resync %s because it is not mirrored.",
+ lv->name);
+ return 1;
+ }
+
+ if (lv->status & PVMOVE) {
+ log_error("Unable to resync pvmove volume %s", lv->name);
+ return 0;
+ }
+
+ if (lv->status & LOCKED) {
+ log_error("Unable to resync locked volume %s", lv->name);
+ return 0;
+ }
+
+ if (lv_info(cmd, lv, 0, &info, 1, 0)) {
+ if (info.open_count) {
+ log_error("Can't resync open logical volume \"%s\"",
+ lv->name);
+ return 0;
+ }
+
+ if (info.exists) {
+ if (!arg_count(cmd, yes_ARG) &&
+ yes_no_prompt("Do you really want to deactivate "
+ "logical volume %s to resync it? [y/n]: ",
+ lv->name) == 'n') {
+ log_error("Logical volume \"%s\" not resynced",
+ lv->name);
+ return 0;
+ }
+
+ if (sigint_caught())
+ return 0;
+
+ active = 1;
+ }
+ }
+
+ /* Activate exclusively to ensure no nodes still have LV active */
+ monitored = dmeventd_monitor_mode();
+ init_dmeventd_monitor(0);
+
+ if (!deactivate_lv(cmd, lv)) {
+ log_error("Unable to deactivate %s for resync", lv->name);
+ return 0;
+ }
+
+ if (vg_is_clustered(lv->vg) && lv_is_active(lv)) {
+ log_error("Can't get exclusive access to clustered volume %s",
+ lv->name);
+ return 0;
+ }
+
+ init_dmeventd_monitor(monitored);
+
+ log_lv = first_seg(lv)->log_lv;
+
+ log_very_verbose("Starting resync of %s%s%s mirror \"%s\"",
+ (active) ? "active " : "",
+ vg_is_clustered(lv->vg) ? "clustered " : "",
+ (log_lv) ? "disk-logged" : "core-logged",
+ lv->name);
+
+ /*
+ * If this mirror has a core log (i.e. !log_lv),
+ * then simply deactivating/activating will cause
+ * it to reset the sync status. We only need to
+ * worry about persistent logs.
+ */
+ if (!log_lv && !(lv->status & MIRROR_NOTSYNCED)) {
+ if (active && !activate_lv(cmd, lv)) {
+ log_error("Failed to reactivate %s to resynchronize "
+ "mirror", lv->name);
+ return 0;
+ }
+ return 1;
+ }
+
+ lv->status &= ~MIRROR_NOTSYNCED;
+
+ if (log_lv) {
+ /* Separate mirror log so we can clear it */
+ detach_mirror_log(first_seg(lv));
+
+ if (!vg_write(lv->vg)) {
+ log_error("Failed to write intermediate VG metadata.");
+ if (!attach_mirror_log(first_seg(lv), log_lv))
+ stack;
+ if (active && !activate_lv(cmd, lv))
+ stack;
+ return 0;
+ }
+
+ if (!vg_commit(lv->vg)) {
+ log_error("Failed to commit intermediate VG metadata.");
+ if (!attach_mirror_log(first_seg(lv), log_lv))
+ stack;
+ if (active && !activate_lv(cmd, lv))
+ stack;
+ return 0;
+ }
+
+ backup(lv->vg);
+
+ if (!activate_lv(cmd, log_lv)) {
+ log_error("Unable to activate %s for mirror log resync",
+ log_lv->name);
+ return 0;
+ }
+
+ log_very_verbose("Clearing log device %s", log_lv->name);
+ if (!set_lv(cmd, log_lv, log_lv->size, 0)) {
+ log_error("Unable to reset sync status for %s", lv->name);
+ if (!deactivate_lv(cmd, log_lv))
+ log_error("Failed to deactivate log LV after "
+ "wiping failed");
+ return 0;
+ }
+
+ if (!deactivate_lv(cmd, log_lv)) {
+ log_error("Unable to deactivate log LV %s after wiping "
+ "for resync", log_lv->name);
+ return 0;
+ }
+
+ /* Put mirror log back in place */
+ if (!attach_mirror_log(first_seg(lv), log_lv))
+ stack;
+ }
+
+ log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+ if (!vg_write(lv->vg) || !vg_commit(lv->vg)) {
+ log_error("Failed to update metadata on disk.");
+ return 0;
+ }
+
+ if (active && !activate_lv(cmd, lv)) {
+ log_error("Failed to reactivate %s after resync", lv->name);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int lvchange_alloc(struct cmd_context *cmd, struct logical_volume *lv)
+{
+ int want_contiguous = 0;
+ alloc_policy_t alloc;
+
+ want_contiguous = strcmp(arg_str_value(cmd, contiguous_ARG, "n"), "n");
+ alloc = want_contiguous ? ALLOC_CONTIGUOUS : ALLOC_INHERIT;
+ alloc = arg_uint_value(cmd, alloc_ARG, alloc);
+
+ if (alloc == lv->alloc) {
+ log_error("Allocation policy of logical volume \"%s\" is "
+ "already %s", lv->name, get_alloc_string(alloc));
+ return 0;
+ }
+
+ lv->alloc = alloc;
+
+ /* FIXME If contiguous, check existing extents already are */
+
+ log_verbose("Setting contiguous allocation policy for \"%s\" to %s",
+ lv->name, get_alloc_string(alloc));
+
+ log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+
+ /* No need to suspend LV for this change */
+ if (!vg_write(lv->vg) || !vg_commit(lv->vg))
+ return_0;
+
+ backup(lv->vg);
+
+ return 1;
+}
+
+static int lvchange_readahead(struct cmd_context *cmd,
+ struct logical_volume *lv)
+{
+ unsigned read_ahead = 0;
+ unsigned pagesize = (unsigned) lvm_getpagesize() >> SECTOR_SHIFT;
+ int r = 0;
+
+ read_ahead = arg_uint_value(cmd, readahead_ARG, 0);
+
+ if (read_ahead != DM_READ_AHEAD_AUTO &&
+ (lv->vg->fid->fmt->features & FMT_RESTRICTED_READAHEAD) &&
+ (read_ahead < 2 || read_ahead > 120)) {
+ log_error("Metadata only supports readahead values between 2 and 120.");
+ return 0;
+ }
+
+ if (read_ahead != DM_READ_AHEAD_AUTO &&
+ read_ahead != DM_READ_AHEAD_NONE && read_ahead % pagesize) {
+ if (read_ahead < pagesize)
+ read_ahead = pagesize;
+ else
+ read_ahead = (read_ahead / pagesize) * pagesize;
+ log_warn("WARNING: Overriding readahead to %u sectors, a multiple "
+ "of %uK page size.", read_ahead, pagesize >> 1);
+ }
+
+ if (lv->read_ahead == read_ahead) {
+ if (read_ahead == DM_READ_AHEAD_AUTO)
+ log_error("Read ahead is already auto for \"%s\"", lv->name);
+ else
+ log_error("Read ahead is already %u for \"%s\"",
+ read_ahead, lv->name);
+ return 0;
+ }
+
+ lv->read_ahead = read_ahead;
+
+ log_verbose("Setting read ahead to %u for \"%s\"", read_ahead,
+ lv->name);
+
+ log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+ if (!vg_write(lv->vg))
+ return_0;
+
+ if (!suspend_lv(cmd, lv)) {
+ log_error("Failed to lock %s", lv->name);
+ vg_revert(lv->vg);
+ goto out;
+ }
+
+ if (!vg_commit(lv->vg)) {
+ if (!resume_lv(cmd, lv))
+ stack;
+ goto_out;
+ }
+
+ log_very_verbose("Updating permissions for \"%s\" in kernel", lv->name);
+ if (!resume_lv(cmd, lv)) {
+ log_error("Problem reactivating %s", lv->name);
+ goto out;
+ }
+
+ r = 1;
+out:
+ backup(lv->vg);
+ return r;
+}
+
+static int lvchange_persistent(struct cmd_context *cmd,
+ struct logical_volume *lv)
+{
+ struct lvinfo info;
+ int active = 0;
+
+ if (!strcmp(arg_str_value(cmd, persistent_ARG, "n"), "n")) {
+ if (!(lv->status & FIXED_MINOR)) {
+ log_error("Minor number is already not persistent "
+ "for \"%s\"", lv->name);
+ return 0;
+ }
+ lv->status &= ~FIXED_MINOR;
+ lv->minor = -1;
+ lv->major = -1;
+ log_verbose("Disabling persistent device number for \"%s\"",
+ lv->name);
+ } else {
+ if (!arg_count(cmd, minor_ARG) && lv->minor < 0) {
+ log_error("Minor number must be specified with -My");
+ return 0;
+ }
+ if (!arg_count(cmd, major_ARG) && lv->major < 0) {
+ log_error("Major number must be specified with -My");
+ return 0;
+ }
+ if (lv_info(cmd, lv, 0, &info, 0, 0) && info.exists)
+ active = 1;
+ if (active && !arg_count(cmd, force_ARG) &&
+ yes_no_prompt("Logical volume %s will be "
+ "deactivated temporarily. "
+ "Continue? [y/n]: ", lv->name) == 'n') {
+ log_error("%s device number not changed.",
+ lv->name);
+ return 0;
+ }
+
+ if (sigint_caught())
+ return 0;
+
+ log_verbose("Ensuring %s is inactive.", lv->name);
+ if (!deactivate_lv(cmd, lv)) {
+ log_error("%s: deactivation failed", lv->name);
+ return 0;
+ }
+ lv->status |= FIXED_MINOR;
+ lv->minor = arg_int_value(cmd, minor_ARG, lv->minor);
+ lv->major = arg_int_value(cmd, major_ARG, lv->major);
+ log_verbose("Setting persistent device number to (%d, %d) "
+ "for \"%s\"", lv->major, lv->minor, lv->name);
+
+ }
+
+ log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+ if (!vg_write(lv->vg) || !vg_commit(lv->vg))
+ return_0;
+
+ backup(lv->vg);
+
+ if (active) {
+ log_verbose("Re-activating logical volume \"%s\"", lv->name);
+ if (!activate_lv(cmd, lv)) {
+ log_error("%s: reactivation failed", lv->name);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int lvchange_tag(struct cmd_context *cmd, struct logical_volume *lv,
+ int arg)
+{
+ const char *tag;
+ struct arg_value_group_list *current_group;
+
+ dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
+ if (!grouped_arg_is_set(current_group->arg_values, arg))
+ continue;
+
+ if (!(tag = grouped_arg_str_value(current_group->arg_values, arg, NULL))) {
+ log_error("Failed to get tag");
+ return 0;
+ }
+
+ if (!lv_change_tag(lv, tag, arg == addtag_ARG))
+ return_0;
+ }
+
+ log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+
+ /* No need to suspend LV for this change */
+ if (!vg_write(lv->vg) || !vg_commit(lv->vg))
+ return_0;
+
+ backup(lv->vg);
+
+ return 1;
+}
+
+static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv,
+ void *handle __attribute__((unused)))
+{
+ int doit = 0, docmds = 0;
+ int dmeventd_mode, archived = 0;
+ struct logical_volume *origin;
+
+ if (!(lv->vg->status & LVM_WRITE) &&
+ (arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) ||
+ arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG) ||
+ arg_count(cmd, alloc_ARG))) {
+ log_error("Only -a permitted with read-only volume "
+ "group \"%s\"", lv->vg->name);
+ return EINVALID_CMD_LINE;
+ }
+
+ if (lv_is_origin(lv) &&
+ (arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) ||
+ arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG) ||
+ arg_count(cmd, alloc_ARG))) {
+ log_error("Can't change logical volume \"%s\" under snapshot",
+ lv->name);
+ return ECMD_FAILED;
+ }
+
+ if (lv_is_cow(lv) && !lv_is_virtual_origin(origin_from_cow(lv)) &&
+ arg_count(cmd, available_ARG)) {
+ log_error("Can't change snapshot logical volume \"%s\"",
+ lv->name);
+ return ECMD_FAILED;
+ }
+
+ if (lv->status & PVMOVE) {
+ log_error("Unable to change pvmove LV %s", lv->name);
+ if (arg_count(cmd, available_ARG))
+ log_error("Use 'pvmove --abort' to abandon a pvmove");
+ return ECMD_FAILED;
+ }
+
+ if (lv->status & MIRROR_LOG) {
+ log_error("Unable to change mirror log LV %s directly", lv->name);
+ return ECMD_FAILED;
+ }
+
+ if (lv->status & MIRROR_IMAGE) {
+ log_error("Unable to change mirror image LV %s directly",
+ lv->name);
+ return ECMD_FAILED;
+ }
+
+ /* If LV is sparse, activate origin instead */
+ if (arg_count(cmd, available_ARG) && lv_is_cow(lv) &&
+ lv_is_virtual_origin(origin = origin_from_cow(lv)))
+ lv = origin;
+
+ if (!(lv_is_visible(lv)) && !lv_is_virtual_origin(lv)) {
+ log_error("Unable to change internal LV %s directly",
+ lv->name);
+ return ECMD_FAILED;
+ }
+
+ if (!get_activation_monitoring_mode(cmd, lv->vg, &dmeventd_mode))
+ return ECMD_FAILED;
+
+ init_dmeventd_monitor(dmeventd_mode);
+
+ /*
+ * FIXME: DEFAULT_BACKGROUND_POLLING should be "unspecified".
+ * If --poll is explicitly provided use it; otherwise polling
+ * should only be started if the LV is not already active. So:
+ * 1) change the activation code to say if the LV was actually activated
+ * 2) make polling of an LV tightly coupled with LV activation
+ *
+ * Do not initiate any polling if --sysinit option is used.
+ */
+ init_background_polling(arg_count(cmd, sysinit_ARG) ? 0 :
+ arg_int_value(cmd, poll_ARG,
+ DEFAULT_BACKGROUND_POLLING));
+
+ /* access permission change */
+ if (arg_count(cmd, permission_ARG)) {
+ if (!archive(lv->vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ archived = 1;
+ doit += lvchange_permission(cmd, lv);
+ docmds++;
+ }
+
+ /* allocation policy change */
+ if (arg_count(cmd, contiguous_ARG) || arg_count(cmd, alloc_ARG)) {
+ if (!archived && !archive(lv->vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ archived = 1;
+ doit += lvchange_alloc(cmd, lv);
+ docmds++;
+ }
+
+ /* read ahead sector change */
+ if (arg_count(cmd, readahead_ARG)) {
+ if (!archived && !archive(lv->vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ archived = 1;
+ doit += lvchange_readahead(cmd, lv);
+ docmds++;
+ }
+
+ /* persistent device number change */
+ if (arg_count(cmd, persistent_ARG)) {
+ if (!archived && !archive(lv->vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ archived = 1;
+ doit += lvchange_persistent(cmd, lv);
+ docmds++;
+ if (sigint_caught()) {
+ stack;
+ return ECMD_FAILED;
+ }
+ }
+
+ /* add tag */
+ if (arg_count(cmd, addtag_ARG)) {
+ if (!archived && !archive(lv->vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ archived = 1;
+ doit += lvchange_tag(cmd, lv, addtag_ARG);
+ docmds++;
+ }
+
+ /* del tag */
+ if (arg_count(cmd, deltag_ARG)) {
+ if (!archived && !archive(lv->vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ archived = 1;
+ doit += lvchange_tag(cmd, lv, deltag_ARG);
+ docmds++;
+ }
+
+ if (doit)
+ log_print("Logical volume \"%s\" changed", lv->name);
+
+ if (arg_count(cmd, resync_ARG))
+ if (!lvchange_resync(cmd, lv)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ /* availability change */
+ if (arg_count(cmd, available_ARG)) {
+ if (!lvchange_availability(cmd, lv)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ }
+
+ if (arg_count(cmd, refresh_ARG))
+ if (!lvchange_refresh(cmd, lv)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (!arg_count(cmd, available_ARG) &&
+ !arg_count(cmd, refresh_ARG) &&
+ arg_count(cmd, monitor_ARG)) {
+ if (!lvchange_monitoring(cmd, lv)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ }
+
+ if (!arg_count(cmd, available_ARG) &&
+ !arg_count(cmd, refresh_ARG) &&
+ arg_count(cmd, poll_ARG)) {
+ if (!lvchange_background_polling(cmd, lv)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ }
+
+ if (doit != docmds) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ return ECMD_PROCESSED;
+}
+
+int lvchange(struct cmd_context *cmd, int argc, char **argv)
+{
+ int update = /* options other than -a, --refresh, --monitor or --poll */
+ arg_count(cmd, contiguous_ARG) || arg_count(cmd, permission_ARG) ||
+ arg_count(cmd, readahead_ARG) || arg_count(cmd, persistent_ARG) ||
+ arg_count(cmd, addtag_ARG) || arg_count(cmd, deltag_ARG) ||
+ arg_count(cmd, resync_ARG) || arg_count(cmd, alloc_ARG);
+
+ if (!update &&
+ !arg_count(cmd, available_ARG) && !arg_count(cmd, refresh_ARG) &&
+ !arg_count(cmd, monitor_ARG) && !arg_count(cmd, poll_ARG) &&
+ /* for persistent_ARG */
+ !arg_count(cmd, minor_ARG) && !arg_count(cmd, major_ARG)) {
+ log_error("Need 1 or more of -a, -C, -j, -m, -M, -p, -r, "
+ "--resync, --refresh, --alloc, --addtag, --deltag, "
+ "--monitor or --poll");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, available_ARG) && arg_count(cmd, refresh_ARG)) {
+ log_error("Only one of -a and --refresh permitted.");
+ return EINVALID_CMD_LINE;
+ }
+
+ if ((arg_count(cmd, ignorelockingfailure_ARG) ||
+ arg_count(cmd, sysinit_ARG)) && update) {
+ log_error("Only -a permitted with --ignorelockingfailure and --sysinit");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (!update)
+ cmd->handles_missing_pvs = 1;
+
+ if (!argc) {
+ log_error("Please give logical volume path(s)");
+ return EINVALID_CMD_LINE;
+ }
+
+ if ((arg_count(cmd, minor_ARG) || arg_count(cmd, major_ARG)) &&
+ !arg_count(cmd, persistent_ARG)) {
+ log_error("--major and --minor require -My");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, minor_ARG) && argc != 1) {
+ log_error("Only give one logical volume when specifying minor");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, contiguous_ARG) && arg_count(cmd, alloc_ARG)) {
+ log_error("Only one of --alloc and --contiguous permitted");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, poll_ARG) && arg_count(cmd, sysinit_ARG)) {
+ log_error("Only one of --poll and --sysinit permitted");
+ return EINVALID_CMD_LINE;
+ }
+
+ return process_each_lv(cmd, argc, argv,
+ update ? READ_FOR_UPDATE : 0, NULL,
+ &lvchange_single);
+}
--- /dev/null
+/*
+ * Copyright (C) 2005-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "polldaemon.h"
+#include "lv_alloc.h"
+#include "metadata.h"
+
+struct lvconvert_params {
+ int snapshot;
+ int merge;
+ int zero;
+
+ const char *origin;
+ const char *lv_name;
+ const char *lv_split_name;
+ const char *lv_name_full;
+ const char *vg_name;
+ int wait_completion;
+ int need_polling;
+
+ uint32_t chunk_size;
+ uint32_t region_size;
+
+ uint32_t mirrors;
+ sign_t mirrors_sign;
+ uint32_t keep_mimages;
+ uint32_t stripes;
+ uint32_t stripe_size;
+
+ struct segment_type *segtype;
+
+ alloc_policy_t alloc;
+
+ int pv_count;
+ char **pvs;
+ struct dm_list *pvh;
+ struct dm_list *failed_pvs;
+
+ struct logical_volume *lv_to_poll;
+};
+
+static int _lvconvert_name_params(struct lvconvert_params *lp,
+ struct cmd_context *cmd,
+ int *pargc, char ***pargv)
+{
+ char *ptr;
+ const char *vg_name = NULL;
+
+ if (lp->merge)
+ return 1;
+
+ if (lp->snapshot) {
+ if (!*pargc) {
+ log_error("Please specify a logical volume to act as "
+ "the snapshot origin.");
+ return 0;
+ }
+
+ lp->origin = *pargv[0];
+ (*pargv)++, (*pargc)--;
+ if (!(lp->vg_name = extract_vgname(cmd, lp->origin))) {
+ log_error("The origin name should include the "
+ "volume group.");
+ return 0;
+ }
+
+ /* Strip the volume group from the origin */
+ if ((ptr = strrchr(lp->origin, (int) '/')))
+ lp->origin = ptr + 1;
+ }
+
+ if (!*pargc) {
+ log_error("Please provide logical volume path");
+ return 0;
+ }
+
+ lp->lv_name = lp->lv_name_full = (*pargv)[0];
+ (*pargv)++, (*pargc)--;
+
+ if (strchr(lp->lv_name_full, '/') &&
+ (vg_name = extract_vgname(cmd, lp->lv_name_full)) &&
+ lp->vg_name && strcmp(vg_name, lp->vg_name)) {
+ log_error("Please use a single volume group name "
+ "(\"%s\" or \"%s\")", vg_name, lp->vg_name);
+ return 0;
+ }
+
+ if (!lp->vg_name)
+ lp->vg_name = vg_name;
+
+ if (!validate_name(lp->vg_name)) {
+ log_error("Please provide a valid volume group name");
+ return 0;
+ }
+
+ if ((ptr = strrchr(lp->lv_name_full, '/')))
+ lp->lv_name = ptr + 1;
+
+ if (!apply_lvname_restrictions(lp->lv_name))
+ return_0;
+
+ if (*pargc && lp->snapshot) {
+ log_error("Too many arguments provided for snapshots");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd,
+ int argc, char **argv)
+{
+ int region_size;
+ int pagesize = lvm_getpagesize();
+
+ memset(lp, 0, sizeof(*lp));
+
+ if ((arg_count(cmd, snapshot_ARG) || arg_count(cmd, merge_ARG)) &&
+ (arg_count(cmd, mirrorlog_ARG) || arg_count(cmd, mirrors_ARG) ||
+ arg_count(cmd, repair_ARG))) {
+ log_error("--snapshot or --merge argument cannot be mixed "
+ "with --mirrors, --repair or --log");
+ return 0;
+ }
+
+ if (!arg_count(cmd, background_ARG))
+ lp->wait_completion = 1;
+
+ if (arg_count(cmd, snapshot_ARG))
+ lp->snapshot = 1;
+
+ if (arg_count(cmd, snapshot_ARG) && arg_count(cmd, merge_ARG)) {
+ log_error("--snapshot and --merge are mutually exclusive");
+ return 0;
+ }
+
+ if (arg_count(cmd, splitmirrors_ARG) && arg_count(cmd, mirrors_ARG)) {
+ log_error("--mirrors and --splitmirrors are "
+ "mutually exclusive");
+ return 0;
+ }
+
+ /*
+ * The '--splitmirrors n' argument is equivalent to '--mirrors -n'
+ * (note the minus sign), except that it signifies the additional
+ * intent to keep the mimage that is detached, rather than
+ * discarding it.
+ */
+ if (arg_count(cmd, splitmirrors_ARG)) {
+ if (!arg_count(cmd, name_ARG)) {
+ log_error("Please name the new logical volume using '--name'");
+ return 0;
+ }
+
+ lp->lv_split_name = arg_value(cmd, name_ARG);
+ if (!apply_lvname_restrictions(lp->lv_split_name))
+ return_0;
+
+ lp->keep_mimages = 1;
+ if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) {
+ log_error("Argument to --splitmirrors"
+ " cannot be negative");
+ return 0;
+ }
+ lp->mirrors = arg_uint_value(cmd, splitmirrors_ARG, 0);
+ lp->mirrors_sign = SIGN_MINUS;
+ } else if (arg_count(cmd, name_ARG)) {
+ log_error("The 'name' argument is only valid"
+ " with --splitmirrors");
+ return 0;
+ }
+
+ if (arg_count(cmd, merge_ARG))
+ lp->merge = 1;
+
+ if (arg_count(cmd, mirrors_ARG)) {
+ /*
+ * --splitmirrors has been chosen as the mechanism for
+ * specifying the intent of detaching and keeping a mimage
+ * versus an additional qualifying argument being added here.
+ */
+ lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0);
+ lp->mirrors_sign = arg_sign_value(cmd, mirrors_ARG, 0);
+ }
+
+ lp->alloc = arg_uint_value(cmd, alloc_ARG, ALLOC_INHERIT);
+
+ /* There are three types of lvconvert. */
+ if (lp->merge) { /* Snapshot merge */
+ if (arg_count(cmd, regionsize_ARG) || arg_count(cmd, chunksize_ARG) ||
+ arg_count(cmd, zero_ARG) || arg_count(cmd, regionsize_ARG) ||
+ arg_count(cmd, stripes_long_ARG) || arg_count(cmd, stripesize_ARG)) {
+ log_error("Only --background and --interval are valid "
+ "arguments for snapshot merge");
+ return 0;
+ }
+
+ if (!(lp->segtype = get_segtype_from_string(cmd, "snapshot")))
+ return_0;
+
+ } else if (lp->snapshot) { /* Snapshot creation from pre-existing cow */
+ if (arg_count(cmd, regionsize_ARG)) {
+ log_error("--regionsize is only available with mirrors");
+ return 0;
+ }
+
+ if (arg_count(cmd, stripesize_ARG) || arg_count(cmd, stripes_long_ARG)) {
+ log_error("--stripes and --stripesize are only available with striped mirrors");
+ return 0;
+ }
+
+ if (arg_sign_value(cmd, chunksize_ARG, 0) == SIGN_MINUS) {
+ log_error("Negative chunk size is invalid");
+ return 0;
+ }
+ lp->chunk_size = arg_uint_value(cmd, chunksize_ARG, 8);
+ if (lp->chunk_size < 8 || lp->chunk_size > 1024 ||
+ (lp->chunk_size & (lp->chunk_size - 1))) {
+ log_error("Chunk size must be a power of 2 in the "
+ "range 4K to 512K");
+ return 0;
+ }
+ log_verbose("Setting chunksize to %d sectors.", lp->chunk_size);
+
+ if (!(lp->segtype = get_segtype_from_string(cmd, "snapshot")))
+ return_0;
+
+ lp->zero = strcmp(arg_str_value(cmd, zero_ARG,
+ (lp->segtype->flags &
+ SEG_CANNOT_BE_ZEROED) ?
+ "n" : "y"), "n");
+
+ } else { /* Mirrors */
+ if (arg_count(cmd, chunksize_ARG)) {
+ log_error("--chunksize is only available with "
+ "snapshots");
+ return 0;
+ }
+
+ if (arg_count(cmd, zero_ARG)) {
+ log_error("--zero is only available with snapshots");
+ return 0;
+ }
+
+ /*
+ * --regionsize is only valid if converting an LV into a mirror.
+ * Checked when we know the state of the LV being converted.
+ */
+
+ if (arg_count(cmd, regionsize_ARG)) {
+ if (arg_sign_value(cmd, regionsize_ARG, 0) ==
+ SIGN_MINUS) {
+ log_error("Negative regionsize is invalid");
+ return 0;
+ }
+ lp->region_size = arg_uint_value(cmd, regionsize_ARG, 0);
+ } else {
+ region_size = 2 * find_config_tree_int(cmd,
+ "activation/mirror_region_size",
+ DEFAULT_MIRROR_REGION_SIZE);
+ if (region_size < 0) {
+ log_error("Negative regionsize in "
+ "configuration file is invalid");
+ return 0;
+ }
+ lp->region_size = region_size;
+ }
+
+ if (lp->region_size % (pagesize >> SECTOR_SHIFT)) {
+ log_error("Region size (%" PRIu32 ") must be "
+ "a multiple of machine memory "
+ "page size (%d)",
+ lp->region_size, pagesize >> SECTOR_SHIFT);
+ return 0;
+ }
+
+ if (lp->region_size & (lp->region_size - 1)) {
+ log_error("Region size (%" PRIu32
+ ") must be a power of 2", lp->region_size);
+ return 0;
+ }
+
+ if (!lp->region_size) {
+ log_error("Non-zero region size must be supplied.");
+ return 0;
+ }
+
+ /* Default is never striped, regardless of existing LV configuration. */
+ if (!get_stripe_params(cmd, &lp->stripes, &lp->stripe_size)) {
+ stack;
+ return 0;
+ }
+
+ if (!(lp->segtype = get_segtype_from_string(cmd, "mirror")))
+ return_0;
+ }
+
+ if (activation() && lp->segtype->ops->target_present &&
+ !lp->segtype->ops->target_present(cmd, NULL, NULL)) {
+ log_error("%s: Required device-mapper target(s) not "
+ "detected in your kernel", lp->segtype->name);
+ return 0;
+ }
+
+ if (!_lvconvert_name_params(lp, cmd, &argc, &argv))
+ return_0;
+
+ lp->pv_count = argc;
+ lp->pvs = argv;
+ lp->failed_pvs = NULL;
+
+ return 1;
+}
+
+static struct volume_group *_get_lvconvert_vg(struct cmd_context *cmd,
+ const char *name,
+ const char *uuid __attribute__((unused)))
+{
+ dev_close_all();
+
+ if (name && !strchr(name, '/'))
+ return vg_read_for_update(cmd, name, NULL, 0);
+
+ /* 'name' is the full LV name; must extract_vgname() */
+ return vg_read_for_update(cmd, extract_vgname(cmd, name),
+ NULL, 0);
+}
+
+static struct logical_volume *_get_lvconvert_lv(struct cmd_context *cmd __attribute__((unused)),
+ struct volume_group *vg,
+ const char *name,
+ const char *uuid,
+ uint32_t lv_type __attribute__((unused)))
+{
+ struct logical_volume *lv = find_lv(vg, name);
+
+ if (!lv || (uuid && strcmp(uuid, (char *)&lv->lvid)))
+ return NULL;
+
+ return lv;
+}
+
+static int _finish_lvconvert_mirror(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct logical_volume *lv,
+ struct dm_list *lvs_changed __attribute__((unused)))
+{
+ int r = 0;
+
+ if (!(lv->status & CONVERTING))
+ return 1;
+
+ if (!collapse_mirrored_lv(lv)) {
+ log_error("Failed to remove temporary sync layer.");
+ return 0;
+ }
+
+ lv->status &= ~CONVERTING;
+
+ log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+
+ if (!vg_write(vg))
+ return_0;
+
+ if (!suspend_lv(cmd, lv)) {
+ log_error("Failed to lock %s", lv->name);
+ vg_revert(vg);
+ goto out;
+ }
+
+ if (!vg_commit(vg)) {
+ resume_lv(cmd, lv);
+ goto_out;
+ }
+
+ log_very_verbose("Updating \"%s\" in kernel", lv->name);
+
+ if (!resume_lv(cmd, lv)) {
+ log_error("Problem reactivating %s", lv->name);
+ goto out;
+ }
+
+ r = 1;
+ log_print("Logical volume %s converted.", lv->name);
+out:
+ backup(vg);
+ return r;
+}
+
+static int _finish_lvconvert_merge(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct logical_volume *lv,
+ struct dm_list *lvs_changed __attribute__((unused)))
+{
+ struct lv_segment *snap_seg = find_merging_cow(lv);
+ if (!snap_seg) {
+ log_error("Logical volume %s has no merging snapshot.", lv->name);
+ return 0;
+ }
+
+ log_print("Merge of snapshot into logical volume %s has finished.", lv->name);
+ if (!lv_remove_single(cmd, snap_seg->cow, DONT_PROMPT)) {
+ log_error("Could not remove snapshot %s merged into %s.",
+ snap_seg->cow->name, lv->name);
+ return 0;
+ }
+
+ return 1;
+}
+
+static progress_t _poll_merge_progress(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ const char *name __attribute__((unused)),
+ struct daemon_parms *parms)
+{
+ percent_t percent = PERCENT_0;
+
+ if (!lv_snapshot_percent(lv, &percent)) {
+ log_error("%s: Failed query for merging percentage. Aborting merge.", lv->name);
+ return PROGRESS_CHECK_FAILED;
+ } else if (percent == PERCENT_INVALID) {
+ log_error("%s: Merging snapshot invalidated. Aborting merge.", lv->name);
+ return PROGRESS_CHECK_FAILED;
+ }
+
+ if (parms->progress_display)
+ log_print("%s: %s: %.1f%%", lv->name, parms->progress_title,
+ percent_to_float(percent));
+ else
+ log_verbose("%s: %s: %.1f%%", lv->name, parms->progress_title,
+ percent_to_float(percent));
+
+ if (percent == PERCENT_0)
+ return PROGRESS_FINISHED_ALL;
+
+ return PROGRESS_UNFINISHED;
+}
+
+static struct poll_functions _lvconvert_mirror_fns = {
+ .get_copy_vg = _get_lvconvert_vg,
+ .get_copy_lv = _get_lvconvert_lv,
+ .poll_progress = poll_mirror_progress,
+ .finish_copy = _finish_lvconvert_mirror,
+};
+
+static struct poll_functions _lvconvert_merge_fns = {
+ .get_copy_vg = _get_lvconvert_vg,
+ .get_copy_lv = _get_lvconvert_lv,
+ .poll_progress = _poll_merge_progress,
+ .finish_copy = _finish_lvconvert_merge,
+};
+
+int lvconvert_poll(struct cmd_context *cmd, struct logical_volume *lv,
+ unsigned background)
+{
+ /*
+ * FIXME allocate an "object key" structure with split
+ * out members (vg_name, lv_name, uuid, etc) and pass that
+ * around the lvconvert and polldaemon code
+ * - will avoid needless work, e.g. extract_vgname()
+ * - unfortunately there are enough overloaded "name" dragons in
+ * the polldaemon, lvconvert, pvmove code that a comprehensive
+ * audit/rework is needed
+ */
+ int len = strlen(lv->vg->name) + strlen(lv->name) + 2;
+ char *uuid = alloca(sizeof(lv->lvid));
+ char *lv_full_name = alloca(len);
+
+ if (!uuid || !lv_full_name)
+ return_0;
+
+ if (!dm_snprintf(lv_full_name, len, "%s/%s", lv->vg->name, lv->name))
+ return_0;
+
+ memcpy(uuid, &lv->lvid, sizeof(lv->lvid));
+
+ if (!lv_is_merging_origin(lv))
+ return poll_daemon(cmd, lv_full_name, uuid, background, 0,
+ &_lvconvert_mirror_fns, "Converted");
+ else
+ return poll_daemon(cmd, lv_full_name, uuid, background, 0,
+ &_lvconvert_merge_fns, "Merged");
+}
+
+static int _insert_lvconvert_layer(struct cmd_context *cmd,
+ struct logical_volume *lv)
+{
+ char *format, *layer_name;
+ size_t len;
+ int i;
+
+ /*
+ * We would like to give the same number for this layer
+ * and the newly added mimage.
+ * However, LV name of newly added mimage is determined *after*
+ * the LV name of this layer is determined.
+ *
+ * So, use generate_lv_name() to generate mimage name first
+ * and take the number from it.
+ */
+
+ len = strlen(lv->name) + 32;
+ if (!(format = alloca(len)) ||
+ !(layer_name = alloca(len)) ||
+ dm_snprintf(format, len, "%s_mimage_%%d", lv->name) < 0) {
+ log_error("lvconvert: layer name allocation failed.");
+ return 0;
+ }
+
+ if (!generate_lv_name(lv->vg, format, layer_name, len) ||
+ sscanf(layer_name, format, &i) != 1) {
+ log_error("lvconvert: layer name generation failed.");
+ return 0;
+ }
+
+ if (dm_snprintf(layer_name, len, MIRROR_SYNC_LAYER "_%d", i) < 0) {
+ log_error("layer name allocation failed.");
+ return 0;
+ }
+
+ if (!insert_layer_for_lv(cmd, lv, 0, layer_name)) {
+ log_error("Failed to insert resync layer");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _area_missing(struct lv_segment *lvseg, int s)
+{
+ if (seg_type(lvseg, s) == AREA_LV) {
+ if (seg_lv(lvseg, s)->status & PARTIAL_LV)
+ return 1;
+ } else if ((seg_type(lvseg, s) == AREA_PV) &&
+ (is_missing_pv(seg_pv(lvseg, s))))
+ return 1;
+
+ return 0;
+}
+
+/* FIXME we want to handle mirror stacks here... */
+static int _failed_mirrors_count(struct logical_volume *lv)
+{
+ struct lv_segment *lvseg;
+ int ret = 0;
+ int s;
+
+ dm_list_iterate_items(lvseg, &lv->segments) {
+ if (!seg_is_mirrored(lvseg))
+ return -1;
+ for (s = 0; s < lvseg->area_count; s++)
+ if (_area_missing(lvseg, s))
+ ret++;
+ }
+
+ return ret;
+}
+
+static struct dm_list *_failed_pv_list(struct volume_group *vg)
+{
+ struct dm_list *failed_pvs;
+ struct pv_list *pvl, *new_pvl;
+
+ if (!(failed_pvs = dm_pool_alloc(vg->vgmem, sizeof(*failed_pvs)))) {
+ log_error("Allocation of list of failed_pvs failed.");
+ return_NULL;
+ }
+
+ dm_list_init(failed_pvs);
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (!is_missing_pv(pvl->pv))
+ continue;
+
+ /*
+ * Finally, --repair will remove empty PVs.
+ * But we only want remove these which are output of repair,
+ * Do not count these which are already empty here.
+ * FIXME: code should traverse PV in LV not in whole VG.
+ * FIXME: layer violation? should it depend on vgreduce --removemising?
+ */
+ if (pvl->pv->pe_alloc_count == 0)
+ continue;
+
+ if (!(new_pvl = dm_pool_alloc(vg->vgmem, sizeof(*new_pvl)))) {
+ log_error("Allocation of failed_pvs list entry failed.");
+ return_NULL;
+ }
+ new_pvl->pv = pvl->pv;
+ dm_list_add(failed_pvs, &new_pvl->list);
+ }
+
+ return failed_pvs;
+}
+
+static int _is_partial_lv(struct logical_volume *lv,
+ void *baton __attribute__((unused)))
+{
+ return lv->status & PARTIAL_LV;
+}
+
+/*
+ * Walk down the stacked mirror LV to the original mirror LV.
+ */
+static struct logical_volume *_original_lv(struct logical_volume *lv)
+{
+ struct logical_volume *next_lv = lv, *tmp_lv;
+
+ while ((tmp_lv = find_temporary_mirror(next_lv)))
+ next_lv = tmp_lv;
+
+ return next_lv;
+}
+
+static void _lvconvert_mirrors_repair_ask(struct cmd_context *cmd,
+ int failed_log, int failed_mirrors,
+ int *replace_log, int *replace_mirrors)
+{
+ const char *leg_policy = NULL, *log_policy = NULL;
+
+ int force = arg_count(cmd, force_ARG);
+ int yes = arg_count(cmd, yes_ARG);
+
+ *replace_log = *replace_mirrors = 1;
+
+ if (arg_count(cmd, use_policies_ARG)) {
+ leg_policy = find_config_tree_str(cmd,
+ "activation/mirror_image_fault_policy", NULL);
+ if (!leg_policy)
+ leg_policy = find_config_tree_str(cmd,
+ "activation/mirror_device_fault_policy",
+ DEFAULT_MIRROR_DEVICE_FAULT_POLICY);
+ log_policy = find_config_tree_str(cmd,
+ "activation/mirror_log_fault_policy",
+ DEFAULT_MIRROR_LOG_FAULT_POLICY);
+ *replace_mirrors = strcmp(leg_policy, "remove");
+ *replace_log = strcmp(log_policy, "remove");
+ return;
+ }
+
+ if (yes)
+ return;
+
+ if (force != PROMPT) {
+ *replace_log = *replace_mirrors = 0;
+ return;
+ }
+
+ if (failed_log &&
+ yes_no_prompt("Attempt to replace failed mirror log? [y/n]: ") == 'n') {
+ *replace_log = 0;
+ }
+
+ if (failed_mirrors &&
+ yes_no_prompt("Attempt to replace failed mirror images "
+ "(requires full device resync)? [y/n]: ") == 'n') {
+ *replace_mirrors = 0;
+ }
+}
+
+/*
+ * _get_log_count
+ * @lv: the mirror LV
+ *
+ * Get the number of on-disk copies of the log.
+ * 0 = 'core'
+ * 1 = 'disk'
+ * 2+ = 'mirrored'
+ */
+static int _get_log_count(struct logical_volume *lv)
+{
+ struct logical_volume *log_lv;
+
+ log_lv = first_seg(_original_lv(lv))->log_lv;
+ if (!log_lv)
+ return 0;
+
+ return lv_mirror_count(log_lv);
+}
+
+static int _lv_update_mirrored_log(struct logical_volume *lv,
+ struct dm_list *operable_pvs,
+ int log_count)
+{
+ int old_log_count;
+ struct logical_volume *log_lv;
+
+ /*
+ * When log_count is 0, mirrored log doesn't need to be
+ * updated here but it will be removed later.
+ */
+ if (!log_count)
+ return 1;
+
+ log_lv = first_seg(_original_lv(lv))->log_lv;
+ if (!log_lv || !(log_lv->status & MIRRORED))
+ return 1;
+
+ old_log_count = _get_log_count(lv);
+ if (old_log_count == log_count)
+ return 1;
+
+ /* Reducing redundancy of the log */
+ return remove_mirror_images(log_lv, log_count,
+ is_mirror_image_removable,
+ operable_pvs, 0U);
+}
+
+static int _lv_update_log_type(struct cmd_context *cmd,
+ struct lvconvert_params *lp,
+ struct logical_volume *lv,
+ struct dm_list *operable_pvs,
+ int log_count)
+{
+ uint32_t region_size;
+ int old_log_count;
+ struct logical_volume *original_lv;
+ struct logical_volume *log_lv;
+
+ old_log_count = _get_log_count(lv);
+ if (old_log_count == log_count)
+ return 1;
+
+ original_lv = _original_lv(lv);
+ region_size = adjusted_mirror_region_size(lv->vg->extent_size,
+ lv->le_count,
+ lp->region_size);
+
+ /* Add a log where there is none */
+ if (!old_log_count) {
+ if (!add_mirror_log(cmd, original_lv, log_count,
+ region_size, operable_pvs, lp->alloc))
+ return_0;
+ return 1;
+ }
+
+ /* Remove an existing log completely */
+ if (!log_count) {
+ if (!remove_mirror_log(cmd, original_lv, operable_pvs,
+ arg_count(cmd, yes_ARG) ||
+ arg_count(cmd, force_ARG)))
+ return_0;
+ return 1;
+ }
+
+ log_lv = first_seg(original_lv)->log_lv;
+
+ /* Adding redundancy to the log */
+ if (old_log_count < log_count) {
+ log_error("Adding log redundancy not supported yet.");
+ log_error("Try converting the log to 'core' first.");
+ return_0;
+ }
+
+ /* Reducing redundancy of the log */
+ return remove_mirror_images(log_lv, log_count, is_mirror_image_removable, operable_pvs, 1U);
+}
+
+/*
+ * Reomove missing and empty PVs from VG, if are also in provided list
+ */
+static void _remove_missing_empty_pv(struct volume_group *vg, struct dm_list *remove_pvs)
+{
+ struct pv_list *pvl, *pvl_vg, *pvlt;
+ int removed = 0;
+
+ if (!remove_pvs)
+ return;
+
+ dm_list_iterate_items(pvl, remove_pvs) {
+ dm_list_iterate_items_safe(pvl_vg, pvlt, &vg->pvs) {
+ if (!id_equal(&pvl->pv->id, &pvl_vg->pv->id) ||
+ !is_missing_pv(pvl_vg->pv) ||
+ pvl_vg->pv->pe_alloc_count != 0)
+ continue;
+
+ /* FIXME: duplication of vgreduce code, move this to library */
+ vg->free_count -= pvl_vg->pv->pe_count;
+ vg->extent_count -= pvl_vg->pv->pe_count;
+ del_pvl_from_vgs(vg, pvl_vg);
+
+ removed++;
+ }
+ }
+
+ if (removed) {
+ if (!vg_write(vg) || !vg_commit(vg)) {
+ stack;
+ return;
+ }
+ log_warn("%d missing and now unallocated Physical Volumes removed from VG.", removed);
+ }
+}
+
+/*
+ * _lvconvert_mirrors_parse_params
+ *
+ * This function performs the following:
+ * 1) Gets the old values of mimage and log counts
+ * 2) Parses the CLI args to find the new desired values
+ * 3) Adjusts 'lp->mirrors' to the appropriate absolute value.
+ * (Remember, 'lp->mirrors' is specified in terms of the number of "copies"
+ * vs. the number of mimages. It can also be a relative value.)
+ * 4) Sets 'lp->need_polling' if collapsing
+ * 5) Validates other mirror params
+ *
+ * Returns: 1 on success, 0 on error
+ */
+static int _lvconvert_mirrors_parse_params(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ struct lvconvert_params *lp,
+ uint32_t *old_mimage_count,
+ uint32_t *old_log_count,
+ uint32_t *new_mimage_count,
+ uint32_t *new_log_count)
+{
+ int repair = arg_count(cmd, repair_ARG);
+ const char *mirrorlog;
+ *old_mimage_count = lv_mirror_count(lv);
+ *old_log_count = _get_log_count(lv);
+
+ /*
+ * Collapsing a stack of mirrors:
+ *
+ * If called with no argument, try collapsing the resync layers
+ */
+ if (!arg_count(cmd, mirrors_ARG) && !arg_count(cmd, mirrorlog_ARG) &&
+ !arg_count(cmd, corelog_ARG) && !arg_count(cmd, regionsize_ARG) &&
+ !arg_count(cmd, splitmirrors_ARG) && !repair) {
+ *new_mimage_count = *old_mimage_count;
+ *new_log_count = *old_log_count;
+
+ if (find_temporary_mirror(lv) || (lv->status & CONVERTING))
+ lp->need_polling = 1;
+ return 1;
+ }
+
+ if ((arg_count(cmd, mirrors_ARG) && repair) ||
+ (arg_count(cmd, mirrorlog_ARG) && repair) ||
+ (arg_count(cmd, corelog_ARG) && repair)) {
+ log_error("--repair cannot be used with --mirrors, --mirrorlog,"
+ " or --corelog");
+ return 0;
+ }
+
+ if (arg_count(cmd, mirrorlog_ARG) && arg_count(cmd, corelog_ARG)) {
+ log_error("--mirrorlog and --corelog are incompatible");
+ return 0;
+ }
+
+ /*
+ * Adjusting mimage count?
+ */
+ if (!arg_count(cmd, mirrors_ARG) && !arg_count(cmd, splitmirrors_ARG))
+ lp->mirrors = *old_mimage_count;
+ else if (lp->mirrors_sign == SIGN_PLUS)
+ lp->mirrors = *old_mimage_count + lp->mirrors;
+ else if (lp->mirrors_sign == SIGN_MINUS)
+ lp->mirrors = (*old_mimage_count > lp->mirrors) ?
+ *old_mimage_count - lp->mirrors: 0;
+ else
+ lp->mirrors += 1;
+
+ *new_mimage_count = lp->mirrors;
+
+ /* Too many mimages? */
+ if (lp->mirrors > DEFAULT_MIRROR_MAX_IMAGES) {
+ log_error("Only up to %d images in mirror supported currently.",
+ DEFAULT_MIRROR_MAX_IMAGES);
+ return 0;
+ }
+
+ /* Did the user try to subtract more legs than available? */
+ if (lp->mirrors < 1) {
+ log_error("Unable to reduce images by specified amount - only %d in %s",
+ *old_mimage_count, lv->name);
+ return 0;
+ }
+
+ /*
+ * FIXME: It would be nice to say what we are adjusting to, but
+ * I really don't know whether to specify the # of copies or mimages.
+ */
+ if (*old_mimage_count != *new_mimage_count)
+ log_verbose("Adjusting mirror image count of %s", lv->name);
+
+ /*
+ * Adjust log type
+ *
+ * If we are converting from a mirror to another mirror or simply
+ * changing the log type, we start by assuming they want the log
+ * type the same and then parse the given args. OTOH, If we are
+ * converting from linear to mirror, then we start from the default
+ * position that the user would like a 'disk' log.
+ */
+ *new_log_count = (*old_mimage_count > 1) ? *old_log_count : 1;
+ if (!arg_count(cmd, corelog_ARG) && !arg_count(cmd, mirrorlog_ARG))
+ return 1;
+
+ if (arg_count(cmd, corelog_ARG))
+ *new_log_count = 0;
+
+ mirrorlog = arg_str_value(cmd, mirrorlog_ARG,
+ !*new_log_count ? "core" : DEFAULT_MIRRORLOG);
+
+ if (!strcmp("mirrored", mirrorlog))
+ *new_log_count = 2;
+ else if (!strcmp("disk", mirrorlog))
+ *new_log_count = 1;
+ else if (!strcmp("core", mirrorlog))
+ *new_log_count = 0;
+ else {
+ log_error("Unknown mirrorlog type: %s", mirrorlog);
+ return 0;
+ }
+
+ /*
+ * No mirrored logs for cluster mirrors until
+ * log daemon is multi-threaded.
+ */
+ if ((*new_log_count == 2) && vg_is_clustered(lv->vg)) {
+ log_error("Log type, \"mirrored\", is unavailable to cluster mirrors");
+ return 0;
+ }
+
+ log_verbose("Setting logging type to %s", mirrorlog);
+
+ /*
+ * Region size must not change on existing mirrors
+ */
+ if (arg_count(cmd, regionsize_ARG) && (lv->status & MIRRORED) &&
+ (lp->region_size != first_seg(lv)->region_size)) {
+ log_error("Mirror log region size cannot be changed on "
+ "an existing mirror.");
+ return 0;
+ }
+
+ /*
+ * For the most part, we cannot handle multi-segment mirrors. Bail out
+ * early if we have encountered one.
+ */
+ if ((lv->status & MIRRORED) && dm_list_size(&lv->segments) != 1) {
+ log_error("Logical volume %s has multiple "
+ "mirror segments.", lv->name);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _reload_lv(struct cmd_context *cmd, struct logical_volume *lv)
+{
+ log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
+
+ if (!vg_write(lv->vg))
+ return_0;
+
+ if (!suspend_lv(cmd, lv)) {
+ log_error("Failed to lock %s", lv->name);
+ vg_revert(lv->vg);
+ return 0;
+ }
+
+ if (!vg_commit(lv->vg)) {
+ if (!resume_lv(cmd, lv))
+ stack;
+ return_0;
+ }
+
+ log_very_verbose("Updating \"%s\" in kernel", lv->name);
+
+ if (!resume_lv(cmd, lv)) {
+ log_error("Problem reactivating %s", lv->name);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * _lvconvert_mirrors_aux
+ *
+ * Add/remove mirror images and adjust log type. 'operable_pvs'
+ * are the set of PVs open to removal or allocation - depending
+ * on the operation being performed.
+ */
+static int _lvconvert_mirrors_aux(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ struct lvconvert_params *lp,
+ struct dm_list *operable_pvs,
+ uint32_t new_mimage_count,
+ uint32_t new_log_count)
+{
+ uint32_t region_size;
+ struct lv_segment *seg;
+ struct logical_volume *layer_lv;
+ uint32_t old_mimage_count = lv_mirror_count(lv);
+ uint32_t old_log_count = _get_log_count(lv);
+
+ if ((lp->mirrors == 1) && !(lv->status & MIRRORED)) {
+ log_error("Logical volume %s is already not mirrored.",
+ lv->name);
+ return 1;
+ }
+
+ region_size = adjusted_mirror_region_size(lv->vg->extent_size,
+ lv->le_count,
+ lp->region_size);
+
+ if (!operable_pvs)
+ operable_pvs = lp->pvh;
+
+ seg = first_seg(lv);
+
+ /*
+ * Up-convert from linear to mirror
+ */
+ if (!(lv->status & MIRRORED)) {
+ /* FIXME Share code with lvcreate */
+
+ /*
+ * FIXME should we give not only lp->pvh, but also all PVs
+ * currently taken by the mirror? Would make more sense from
+ * user perspective.
+ */
+ if (!lv_add_mirrors(cmd, lv, new_mimage_count - 1, lp->stripes,
+ lp->stripe_size, region_size, new_log_count, operable_pvs,
+ lp->alloc, MIRROR_BY_LV)) {
+ stack;
+ return 0;
+ }
+ if (lp->wait_completion)
+ lp->need_polling = 1;
+
+ goto out;
+ }
+
+ /*
+ * Up-convert m-way mirror to n-way mirror
+ */
+ if (new_mimage_count > old_mimage_count) {
+ if (lv->status & MIRROR_NOTSYNCED) {
+ log_error("Can't add mirror to out-of-sync mirrored "
+ "LV: use lvchange --resync first.");
+ return 0;
+ }
+
+ /*
+ * We allow snapshots of mirrors, but for now, we
+ * do not allow up converting mirrors that are under
+ * snapshots. The layering logic is somewhat complex,
+ * and preliminary test show that the conversion can't
+ * seem to get the correct %'age of completion.
+ */
+ if (lv_is_origin(lv)) {
+ log_error("Can't add additional mirror images to "
+ "mirrors that are under snapshots");
+ return 0;
+ }
+
+ /*
+ * Is there already a convert in progress? We do not
+ * currently allow more than one.
+ */
+ if (find_temporary_mirror(lv) || (lv->status & CONVERTING)) {
+ log_error("%s is already being converted. Unable to start another conversion.",
+ lv->name);
+ return 0;
+ }
+
+ /*
+ * Is there already a convert in progress? We do not
+ * currently allow more than one.
+ */
+ if (find_temporary_mirror(lv) || (lv->status & CONVERTING)) {
+ log_error("%s is already being converted. Unable to start another conversion.",
+ lv->name);
+ return 0;
+ }
+
+ /*
+ * Log addition/removal should be done before the layer
+ * insertion to make the end result consistent with
+ * linear-to-mirror conversion.
+ */
+ if (!_lv_update_log_type(cmd, lp, lv,
+ operable_pvs, new_log_count)) {
+ stack;
+ return 0;
+ }
+
+ /* Insert a temporary layer for syncing,
+ * only if the original lv is using disk log. */
+ if (seg->log_lv && !_insert_lvconvert_layer(cmd, lv)) {
+ log_error("Failed to insert resync layer");
+ return 0;
+ }
+
+ /* FIXME: can't have multiple mlogs. force corelog. */
+ if (!lv_add_mirrors(cmd, lv,
+ new_mimage_count - old_mimage_count, lp->stripes, lp->stripe_size,
+ region_size, 0U, operable_pvs, lp->alloc,
+ MIRROR_BY_LV)) {
+ layer_lv = seg_lv(first_seg(lv), 0);
+ if (!remove_layer_from_lv(lv, layer_lv) ||
+ !deactivate_lv(cmd, layer_lv) ||
+ !lv_remove(layer_lv) || !vg_write(lv->vg) ||
+ !vg_commit(lv->vg)) {
+ log_error("ABORTING: Failed to remove "
+ "temporary mirror layer %s.",
+ layer_lv->name);
+ log_error("Manual cleanup with vgcfgrestore "
+ "and dmsetup may be required.");
+ return 0;
+ }
+ stack;
+ return 0;
+ }
+ if (seg->log_lv)
+ lv->status |= CONVERTING;
+ lp->need_polling = 1;
+
+ goto out_skip_log_convert;
+ }
+
+ /*
+ * Down-convert (reduce # of mimages).
+ */
+ if (new_mimage_count < old_mimage_count) {
+ uint32_t nmc = old_mimage_count - new_mimage_count;
+ uint32_t nlc = (!new_log_count || lp->mirrors == 1) ? 1U : 0U;
+
+ /* FIXME: Why did nlc used to be calculated that way? */
+
+ /* Reduce number of mirrors */
+ if (lp->keep_mimages) {
+ if (!lv_split_mirror_images(lv, lp->lv_split_name,
+ nmc, operable_pvs))
+ return 0;
+ } else if (!lv_remove_mirrors(cmd, lv, nmc, nlc,
+ is_mirror_image_removable, operable_pvs, 0))
+ return_0;
+
+ goto out; /* Just in case someone puts code between */
+ }
+
+out:
+ /*
+ * Converting the log type
+ */
+ if ((lv->status & MIRRORED) && (old_log_count != new_log_count)) {
+ if (!_lv_update_log_type(cmd, lp, lv,
+ operable_pvs, new_log_count)) {
+ stack;
+ return 0;
+ }
+ }
+
+out_skip_log_convert:
+
+ if (!_reload_lv(cmd, lv))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * _lvconvert_mirrors_repair
+ *
+ * This function operates in two phases. First, all of the bad
+ * devices are removed from the mirror. Then, if desired by the
+ * user, the devices are replaced.
+ *
+ * 'old_mimage_count' and 'old_log_count' are there so we know
+ * what to convert to after the removal of devices.
+ */
+static int _lvconvert_mirrors_repair(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ struct lvconvert_params *lp,
+ uint32_t old_mimage_count,
+ uint32_t old_log_count)
+{
+ int failed_log = 0;
+ int failed_mirrors = 0;
+ int replace_log = 0;
+ int replace_mirrors = 0;
+ uint32_t new_log_count, log_count;
+ struct logical_volume *log_lv;
+
+ cmd->handles_missing_pvs = 1;
+ cmd->partial_activation = 1;
+ lp->need_polling = 0;
+
+ lv_check_transient(lv); /* TODO check this in lib for all commands? */
+
+ if (!(lv->status & PARTIAL_LV)) {
+ log_error("%s is consistent. Nothing to repair.", lv->name);
+ return 1;
+ }
+
+ /*
+ * Count the failed mimages - negative if 'lv' is not a mirror
+ */
+ if ((failed_mirrors = _failed_mirrors_count(lv)) < 0)
+ return_0;
+
+ lp->mirrors = old_mimage_count - failed_mirrors;
+
+ if (lp->mirrors != old_mimage_count)
+ log_error("Mirror status: %d of %d images failed.",
+ failed_mirrors, old_mimage_count);
+
+ /*
+ * Count the failed log devices
+ */
+ new_log_count = old_log_count;
+ log_lv = first_seg(lv)->log_lv;
+ if (log_lv) {
+ new_log_count = lv_mirror_count(log_lv);
+ if (log_lv->status & PARTIAL_LV) {
+ failed_log = 1;
+ if (log_lv->status & MIRRORED)
+ new_log_count -= _failed_mirrors_count(log_lv);
+ else
+ new_log_count = 0;
+ }
+ }
+ if (old_log_count != new_log_count)
+ log_error("Mirror log status: %d of %d images failed%s",
+ old_log_count - new_log_count, old_log_count,
+ (!new_log_count) ? " - switching to core" : "");
+
+ /*
+ * Find out our policies
+ */
+ _lvconvert_mirrors_repair_ask(cmd, failed_log, failed_mirrors,
+ &replace_log, &replace_mirrors);
+
+ /*
+ * First phase - remove faulty devices
+ */
+ if (!(lp->failed_pvs = _failed_pv_list(lv->vg)))
+ return_0;
+
+ log_count = new_log_count;
+
+ /*
+ * We must adjust the log first, or the entire mirror
+ * will get stuck during a suspend.
+ */
+ if (!_lv_update_mirrored_log(lv, lp->failed_pvs, log_count))
+ return 0;
+
+ if (lp->mirrors == 1)
+ log_count = 0;
+
+ if (failed_mirrors) {
+ if (!lv_remove_mirrors(cmd, lv, failed_mirrors,
+ log_count ? 0U : 1U,
+ _is_partial_lv, NULL, 0))
+ return 0;
+ }
+
+ if (!_lv_update_log_type(cmd, lp, lv, lp->failed_pvs,
+ log_count))
+ return 0;
+
+ if (!_reload_lv(cmd, lv))
+ return 0;
+
+ /*
+ * Second phase - replace faulty devices
+ */
+
+ if (replace_mirrors)
+ lp->mirrors = old_mimage_count;
+
+ /*
+ * It does not make sense to replace the log if the volume is no longer
+ * a mirror.
+ */
+ if (!replace_mirrors && lp->mirrors == 1)
+ replace_log = 0;
+
+ log_count = replace_log ? old_log_count : new_log_count;
+
+ while (replace_mirrors || replace_log) {
+ log_warn("Trying to up-convert to %d images, %d logs.", lp->mirrors, log_count);
+ if (_lvconvert_mirrors_aux(cmd, lv, lp, NULL,
+ lp->mirrors, log_count))
+ break;
+ else {
+ if (lp->mirrors > 2)
+ -- lp->mirrors;
+ else if (log_count > 0)
+ -- log_count;
+ else
+ break; /* nowhere to go, anymore... */
+ }
+ }
+
+ if (replace_mirrors && lp->mirrors != old_mimage_count)
+ log_warn("WARNING: Failed to replace %d of %d images in volume %s",
+ old_mimage_count - lp->mirrors, old_mimage_count, lv->name);
+ if (replace_log && log_count != old_log_count)
+ log_warn("WARNING: Failed to replace %d of %d logs in volume %s",
+ old_log_count - log_count, old_log_count, lv->name);
+
+ /* if (!arg_count(cmd, use_policies_ARG) && (lp->mirrors != old_mimage_count
+ || log_count != old_log_count))
+ return 0; */
+
+ return 1;
+}
+
+/*
+ * _lvconvert_mirrors
+ *
+ * Determine what is being done. Are we doing a conversion, repair, or
+ * collapsing a stack? Once determined, call helper functions.
+ */
+static int _lvconvert_mirrors(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ struct lvconvert_params *lp)
+{
+ int repair = arg_count(cmd, repair_ARG);
+ uint32_t old_mimage_count;
+ uint32_t old_log_count;
+ uint32_t new_mimage_count;
+ uint32_t new_log_count;
+
+ /* Adjust mimage and/or log count */
+ if (!_lvconvert_mirrors_parse_params(cmd, lv, lp,
+ &old_mimage_count, &old_log_count,
+ &new_mimage_count, &new_log_count))
+ return 0;
+
+ if (((old_mimage_count < new_mimage_count && old_log_count > new_log_count) ||
+ (old_mimage_count > new_mimage_count && old_log_count < new_log_count)) &&
+ lp->pv_count) {
+ log_error("Cannot both allocate and free extents when specifying physical"
+ " volumes to use.");
+ log_error("Please specify the operation in two steps.");
+ return 0;
+ }
+
+ /* Nothing to do? (Probably finishing collapse.) */
+ if ((old_mimage_count == new_mimage_count) &&
+ (old_log_count == new_log_count) && !repair)
+ return 1;
+
+ if (repair)
+ return _lvconvert_mirrors_repair(cmd, lv, lp,
+ old_mimage_count,
+ old_log_count);
+
+ if (!_lvconvert_mirrors_aux(cmd, lv, lp, NULL,
+ new_mimage_count, new_log_count))
+ return 0;
+
+ if (!lp->need_polling)
+ log_print("Logical volume %s converted.", lv->name);
+
+ backup(lv->vg);
+ return 1;
+}
+
+static int lvconvert_snapshot(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ struct lvconvert_params *lp)
+{
+ struct logical_volume *org;
+ int r = 0;
+
+ if (!(org = find_lv(lv->vg, lp->origin))) {
+ log_error("Couldn't find origin volume '%s'.", lp->origin);
+ return 0;
+ }
+
+ if (org == lv) {
+ log_error("Unable to use \"%s\" as both snapshot and origin.",
+ lv->name);
+ return 0;
+ }
+
+ if (org->status & (LOCKED|PVMOVE|MIRRORED) || lv_is_cow(org)) {
+ log_error("Unable to create a snapshot of a %s LV.",
+ org->status & LOCKED ? "locked" :
+ org->status & PVMOVE ? "pvmove" :
+ org->status & MIRRORED ? "mirrored" :
+ "snapshot");
+ return 0;
+ }
+
+ if (!lp->zero || !(lv->status & LVM_WRITE))
+ log_warn("WARNING: \"%s\" not zeroed", lv->name);
+ else if (!set_lv(cmd, lv, UINT64_C(0), 0)) {
+ log_error("Aborting. Failed to wipe snapshot "
+ "exception store.");
+ return 0;
+ }
+
+ if (!deactivate_lv(cmd, lv)) {
+ log_error("Couldn't deactivate LV %s.", lv->name);
+ return 0;
+ }
+
+ if (!vg_add_snapshot(org, lv, NULL, org->le_count, lp->chunk_size)) {
+ log_error("Couldn't create snapshot.");
+ return 0;
+ }
+
+ /* store vg on disk(s) */
+ if (!vg_write(lv->vg))
+ return_0;
+
+ if (!suspend_lv(cmd, org)) {
+ log_error("Failed to suspend origin %s", org->name);
+ vg_revert(lv->vg);
+ goto out;
+ }
+
+ if (!vg_commit(lv->vg))
+ goto_out;
+
+ if (!resume_lv(cmd, org)) {
+ log_error("Problem reactivating origin %s", org->name);
+ goto out;
+ }
+
+ log_print("Logical volume %s converted to snapshot.", lv->name);
+ r = 1;
+out:
+ backup(lv->vg);
+ return r;
+}
+
+static int lvconvert_merge(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ struct lvconvert_params *lp)
+{
+ int r = 0;
+ int merge_on_activate = 0;
+ struct logical_volume *origin = origin_from_cow(lv);
+ struct lv_segment *cow_seg = find_cow(lv);
+ struct lvinfo info;
+
+ /* Check if merge is possible */
+ if (lv_is_merging_cow(lv)) {
+ log_error("Snapshot %s is already merging", lv->name);
+ return 0;
+ }
+ if (lv_is_merging_origin(origin)) {
+ log_error("Snapshot %s is already merging into the origin",
+ find_merging_cow(origin)->cow->name);
+ return 0;
+ }
+
+ /*
+ * Prevent merge with open device(s) as it would likely lead
+ * to application/filesystem failure. Merge on origin's next
+ * activation if either the origin or snapshot LV are currently
+ * open.
+ *
+ * FIXME testing open_count is racey; snapshot-merge target's
+ * constructor and DM should prevent appropriate devices from
+ * being open.
+ */
+ if (lv_info(cmd, origin, 0, &info, 1, 0)) {
+ if (info.open_count) {
+ log_error("Can't merge over open origin volume");
+ merge_on_activate = 1;
+ }
+ }
+ if (lv_info(cmd, lv, 0, &info, 1, 0)) {
+ if (info.open_count) {
+ log_print("Can't merge when snapshot is open");
+ merge_on_activate = 1;
+ }
+ }
+
+ init_snapshot_merge(cow_seg, origin);
+
+ /* store vg on disk(s) */
+ if (!vg_write(lv->vg))
+ return_0;
+
+ if (merge_on_activate) {
+ /* commit vg but skip starting the merge */
+ if (!vg_commit(lv->vg))
+ return_0;
+ r = 1;
+ log_print("Merging of snapshot %s will start "
+ "next activation.", lv->name);
+ goto out;
+ }
+
+ /* Perform merge */
+ if (!suspend_lv(cmd, origin)) {
+ log_error("Failed to suspend origin %s", origin->name);
+ vg_revert(lv->vg);
+ goto out;
+ }
+
+ if (!vg_commit(lv->vg)) {
+ if (!resume_lv(cmd, origin))
+ stack;
+ goto_out;
+ }
+
+ if (!resume_lv(cmd, origin)) {
+ log_error("Failed to reactivate origin %s", origin->name);
+ goto out;
+ }
+
+ lp->need_polling = 1;
+ lp->lv_to_poll = origin;
+
+ r = 1;
+ log_print("Merging of volume %s started.", lv->name);
+out:
+ backup(lv->vg);
+ return r;
+}
+
+static int _lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv,
+ void *handle)
+{
+ struct lvconvert_params *lp = handle;
+
+ if (lv->status & LOCKED) {
+ log_error("Cannot convert locked LV %s", lv->name);
+ return ECMD_FAILED;
+ }
+
+ if (lv_is_cow(lv) && !lp->merge) {
+ log_error("Can't convert snapshot logical volume \"%s\"",
+ lv->name);
+ return ECMD_FAILED;
+ }
+
+ if (lv->status & PVMOVE) {
+ log_error("Unable to convert pvmove LV %s", lv->name);
+ return ECMD_FAILED;
+ }
+
+ if (arg_count(cmd, repair_ARG) && !(lv->status & MIRRORED)) {
+ if (arg_count(cmd, use_policies_ARG))
+ return ECMD_PROCESSED; /* nothing to be done here */
+ log_error("Can't repair non-mirrored LV \"%s\".", lv->name);
+ return ECMD_FAILED;
+ }
+
+ if (lp->merge) {
+ if (!lv_is_cow(lv)) {
+ log_error("Logical volume \"%s\" is not a snapshot",
+ lv->name);
+ return ECMD_FAILED;
+ }
+ if (!archive(lv->vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ if (!lvconvert_merge(cmd, lv, lp)) {
+ log_error("Unable to merge LV \"%s\" into its origin.", lv->name);
+ return ECMD_FAILED;
+ }
+ } else if (lp->snapshot) {
+ if (lv->status & MIRRORED) {
+ log_error("Unable to convert mirrored LV \"%s\" into a snapshot.", lv->name);
+ return ECMD_FAILED;
+ }
+ if (!archive(lv->vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ if (!lvconvert_snapshot(cmd, lv, lp)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ } else if (arg_count(cmd, mirrors_ARG) ||
+ arg_count(cmd, splitmirrors_ARG) ||
+ (lv->status & MIRRORED)) {
+ if (!archive(lv->vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ if (!_lvconvert_mirrors(cmd, lv, lp)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ /* If repairing and using policies, remove missing PVs from VG */
+ if (arg_count(cmd, repair_ARG) && arg_count(cmd, use_policies_ARG))
+ _remove_missing_empty_pv(lv->vg, lp->failed_pvs);
+ }
+
+ return ECMD_PROCESSED;
+}
+
+/*
+ * FIXME move to toollib along with the rest of the drop/reacquire
+ * VG locking that is used by lvconvert_merge_single()
+ */
+static struct logical_volume *get_vg_lock_and_logical_volume(struct cmd_context *cmd,
+ const char *vg_name,
+ const char *lv_name)
+{
+ /*
+ * Returns NULL if the requested LV doesn't exist;
+ * otherwise the caller must free_vg(lv->vg)
+ * - it is also up to the caller to unlock_vg() as needed
+ */
+ struct volume_group *vg;
+ struct logical_volume* lv = NULL;
+
+ vg = _get_lvconvert_vg(cmd, vg_name, NULL);
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ return_NULL;
+ }
+
+ if (!(lv = _get_lvconvert_lv(cmd, vg, lv_name, NULL, 0))) {
+ log_error("Can't find LV %s in VG %s", lv_name, vg_name);
+ unlock_and_free_vg(cmd, vg, vg_name);
+ return NULL;
+ }
+
+ return lv;
+}
+
+static int poll_logical_volume(struct cmd_context *cmd, struct logical_volume *lv,
+ int wait_completion)
+{
+ struct lvinfo info;
+
+ if (!lv_info(cmd, lv, 0, &info, 1, 0) || !info.exists) {
+ log_print("Conversion starts after activation.");
+ return ECMD_PROCESSED;
+ }
+ return lvconvert_poll(cmd, lv, wait_completion ? 0 : 1U);
+}
+
+static int lvconvert_single(struct cmd_context *cmd, struct lvconvert_params *lp)
+{
+ struct logical_volume *lv = NULL;
+ int ret = ECMD_FAILED;
+ int saved_ignore_suspended_devices = ignore_suspended_devices();
+
+ if (arg_count(cmd, repair_ARG)) {
+ init_ignore_suspended_devices(1);
+ cmd->handles_missing_pvs = 1;
+ }
+
+ lv = get_vg_lock_and_logical_volume(cmd, lp->vg_name, lp->lv_name);
+ if (!lv)
+ goto_out;
+
+ /*
+ * lp->pvh holds the list of PVs available for allocation or removal
+ */
+ if (lp->pv_count) {
+ if (!(lp->pvh = create_pv_list(cmd->mem, lv->vg, lp->pv_count,
+ lp->pvs, 0)))
+ goto_bad;
+ } else
+ lp->pvh = &lv->vg->pvs;
+
+ lp->lv_to_poll = lv;
+ ret = _lvconvert_single(cmd, lv, lp);
+bad:
+ unlock_vg(cmd, lp->vg_name);
+
+ if (ret == ECMD_PROCESSED && lp->need_polling)
+ ret = poll_logical_volume(cmd, lp->lv_to_poll,
+ lp->wait_completion);
+
+ free_vg(lv->vg);
+out:
+ init_ignore_suspended_devices(saved_ignore_suspended_devices);
+ return ret;
+}
+
+static int lvconvert_merge_single(struct cmd_context *cmd, struct logical_volume *lv,
+ void *handle)
+{
+ struct lvconvert_params *lp = handle;
+ const char *vg_name = NULL;
+ struct logical_volume *refreshed_lv = NULL;
+ int ret;
+
+ /*
+ * FIXME can't trust lv's VG to be current given that caller
+ * is process_each_lv() -- poll_logical_volume() may have
+ * already updated the VG's metadata in an earlier iteration.
+ * - preemptively drop the VG lock, as is needed for
+ * poll_logical_volume(), refresh LV (and VG in the process).
+ */
+ vg_name = lv->vg->name;
+ unlock_vg(cmd, vg_name);
+ refreshed_lv = get_vg_lock_and_logical_volume(cmd, vg_name, lv->name);
+ if (!refreshed_lv) {
+ log_error("ABORTING: Can't reread LV %s/%s", vg_name, lv->name);
+ return ECMD_FAILED;
+ }
+
+ lp->lv_to_poll = refreshed_lv;
+ ret = _lvconvert_single(cmd, refreshed_lv, lp);
+
+ if (ret == ECMD_PROCESSED && lp->need_polling) {
+ /*
+ * Must drop VG lock, because lvconvert_poll() needs it,
+ * then reacquire it after polling completes
+ */
+ unlock_vg(cmd, vg_name);
+
+ ret = poll_logical_volume(cmd, lp->lv_to_poll,
+ lp->wait_completion);
+
+ /* use LCK_VG_WRITE to match lvconvert()'s READ_FOR_UPDATE */
+ if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) {
+ log_error("ABORTING: Can't relock VG for %s "
+ "after polling finished", vg_name);
+ ret = ECMD_FAILED;
+ }
+ }
+
+ free_vg(refreshed_lv->vg);
+
+ return ret;
+}
+
+int lvconvert(struct cmd_context * cmd, int argc, char **argv)
+{
+ struct lvconvert_params lp;
+
+ if (!_read_params(&lp, cmd, argc, argv)) {
+ stack;
+ return EINVALID_CMD_LINE;
+ }
+
+ if (lp.merge) {
+ if (!argc) {
+ log_error("Please provide logical volume path");
+ return EINVALID_CMD_LINE;
+ }
+ return process_each_lv(cmd, argc, argv, READ_FOR_UPDATE, &lp,
+ &lvconvert_merge_single);
+ }
+
+ return lvconvert_single(cmd, &lp);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "lv_alloc.h"
+
+#include <fcntl.h>
+
+struct lvcreate_cmdline_params {
+ percent_type_t percent;
+ uint64_t size;
+ char **pvs;
+ int pv_count;
+};
+
+static int _lvcreate_name_params(struct lvcreate_params *lp,
+ struct cmd_context *cmd,
+ int *pargc, char ***pargv)
+{
+ int argc = *pargc;
+ char **argv = *pargv, *ptr;
+ char *vg_name;
+
+ lp->lv_name = arg_str_value(cmd, name_ARG, NULL);
+
+ if (lp->snapshot && !arg_count(cmd, virtualsize_ARG)) {
+ if (!argc) {
+ log_error("Please specify a logical volume to act as "
+ "the snapshot origin.");
+ return 0;
+ }
+
+ lp->origin = argv[0];
+ (*pargv)++, (*pargc)--;
+ if (!(lp->vg_name = extract_vgname(cmd, lp->origin))) {
+ log_error("The origin name should include the "
+ "volume group.");
+ return 0;
+ }
+
+ /* Strip the volume group from the origin */
+ if ((ptr = strrchr(lp->origin, (int) '/')))
+ lp->origin = ptr + 1;
+
+ } else {
+ /*
+ * If VG not on command line, try -n arg and then
+ * environment.
+ */
+ if (!argc) {
+ if (!(lp->vg_name = extract_vgname(cmd, lp->lv_name))) {
+ log_error("Please provide a volume group name");
+ return 0;
+ }
+
+ } else {
+ vg_name = skip_dev_dir(cmd, argv[0], NULL);
+ if (strrchr(vg_name, '/')) {
+ log_error("Volume group name expected "
+ "(no slash)");
+ return 0;
+ }
+
+ /*
+ * Ensure lv_name doesn't contain a
+ * different VG.
+ */
+ if (lp->lv_name && strchr(lp->lv_name, '/')) {
+ if (!(lp->vg_name =
+ extract_vgname(cmd, lp->lv_name)))
+ return 0;
+
+ if (strcmp(lp->vg_name, vg_name)) {
+ log_error("Inconsistent volume group "
+ "names "
+ "given: \"%s\" and \"%s\"",
+ lp->vg_name, vg_name);
+ return 0;
+ }
+ }
+
+ lp->vg_name = vg_name;
+ (*pargv)++, (*pargc)--;
+ }
+ }
+
+ if (!validate_name(lp->vg_name)) {
+ log_error("Volume group name %s has invalid characters",
+ lp->vg_name);
+ return 0;
+ }
+
+ if (lp->lv_name) {
+ if ((ptr = strrchr(lp->lv_name, '/')))
+ lp->lv_name = ptr + 1;
+
+ if (!apply_lvname_restrictions(lp->lv_name))
+ return_0;
+
+ if (!validate_name(lp->lv_name)) {
+ log_error("Logical volume name \"%s\" is invalid",
+ lp->lv_name);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Update extents parameters based on other parameters which affect the size
+ * calcuation.
+ * NOTE: We must do this here because of the percent_t typedef and because we
+ * need the vg.
+ */
+static int _update_extents_params(struct volume_group *vg,
+ struct lvcreate_params *lp,
+ struct lvcreate_cmdline_params *lcp)
+{
+ uint32_t pv_extent_count;
+ struct logical_volume *origin = NULL;
+
+ if (lcp->size &&
+ !(lp->extents = extents_from_size(vg->cmd, lcp->size,
+ vg->extent_size)))
+ return_0;
+
+ if (lp->voriginsize &&
+ !(lp->voriginextents = extents_from_size(vg->cmd, lp->voriginsize,
+ vg->extent_size)))
+ return_0;
+
+ /*
+ * Create the pv list before we parse lcp->percent - might be
+ * PERCENT_PVSs
+ */
+ if (lcp->pv_count) {
+ if (!(lp->pvh = create_pv_list(vg->cmd->mem, vg,
+ lcp->pv_count, lcp->pvs, 1)))
+ return_0;
+ } else
+ lp->pvh = &vg->pvs;
+
+ switch(lcp->percent) {
+ case PERCENT_VG:
+ lp->extents = lp->extents * vg->extent_count / 100;
+ break;
+ case PERCENT_FREE:
+ lp->extents = lp->extents * vg->free_count / 100;
+ break;
+ case PERCENT_PVS:
+ if (!lcp->pv_count)
+ lp->extents = lp->extents * vg->extent_count / 100;
+ else {
+ pv_extent_count = pv_list_extents_free(lp->pvh);
+ lp->extents = lp->extents * pv_extent_count / 100;
+ }
+ break;
+ case PERCENT_LV:
+ log_error("Please express size as %%VG, %%PVS, or "
+ "%%FREE.");
+ return 0;
+ case PERCENT_ORIGIN:
+ if (lp->snapshot && lp->origin &&
+ !(origin = find_lv(vg, lp->origin))) {
+ log_error("Couldn't find origin volume '%s'.",
+ lp->origin);
+ return 0;
+ }
+ if (!origin) {
+ log_error(INTERNAL_ERROR "Couldn't find origin volume.");
+ return 0;
+ }
+ lp->extents = lp->extents * origin->le_count / 100;
+ break;
+ case PERCENT_NONE:
+ break;
+ }
+ return 1;
+}
+
+static int _read_size_params(struct lvcreate_params *lp,
+ struct lvcreate_cmdline_params *lcp,
+ struct cmd_context *cmd)
+{
+ if (arg_count(cmd, extents_ARG) + arg_count(cmd, size_ARG) != 1) {
+ log_error("Please specify either size or extents (not both)");
+ return 0;
+ }
+
+ if (arg_count(cmd, extents_ARG)) {
+ if (arg_sign_value(cmd, extents_ARG, 0) == SIGN_MINUS) {
+ log_error("Negative number of extents is invalid");
+ return 0;
+ }
+ lp->extents = arg_uint_value(cmd, extents_ARG, 0);
+ lcp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE);
+ }
+
+ /* Size returned in kilobyte units; held in sectors */
+ if (arg_count(cmd, size_ARG)) {
+ if (arg_sign_value(cmd, size_ARG, 0) == SIGN_MINUS) {
+ log_error("Negative size is invalid");
+ return 0;
+ }
+ lcp->size = arg_uint64_value(cmd, size_ARG, UINT64_C(0));
+ lcp->percent = PERCENT_NONE;
+ }
+
+ /* Size returned in kilobyte units; held in sectors */
+ if (arg_count(cmd, virtualsize_ARG)) {
+ if (arg_sign_value(cmd, virtualsize_ARG, 0) == SIGN_MINUS) {
+ log_error("Negative virtual origin size is invalid");
+ return 0;
+ }
+ lp->voriginsize = arg_uint64_value(cmd, virtualsize_ARG,
+ UINT64_C(0));
+ if (!lp->voriginsize) {
+ log_error("Virtual origin size may not be zero");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Generic mirror parameter checks.
+ * FIXME: Should eventually be moved into lvm library.
+ */
+static int _validate_mirror_params(const struct cmd_context *cmd __attribute__((unused)),
+ const struct lvcreate_params *lp)
+{
+ int pagesize = lvm_getpagesize();
+
+ if (lp->region_size & (lp->region_size - 1)) {
+ log_error("Region size (%" PRIu32 ") must be a power of 2",
+ lp->region_size);
+ return 0;
+ }
+
+ if (lp->region_size % (pagesize >> SECTOR_SHIFT)) {
+ log_error("Region size (%" PRIu32 ") must be a multiple of "
+ "machine memory page size (%d)",
+ lp->region_size, pagesize >> SECTOR_SHIFT);
+ return 0;
+ }
+
+ if (!lp->region_size) {
+ log_error("Non-zero region size must be supplied.");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _read_mirror_params(struct lvcreate_params *lp,
+ struct cmd_context *cmd)
+{
+ int region_size;
+ const char *mirrorlog;
+ int corelog = arg_count(cmd, corelog_ARG);
+
+ mirrorlog = arg_str_value(cmd, mirrorlog_ARG,
+ corelog ? "core" : DEFAULT_MIRRORLOG);
+
+ if (strcmp("core", mirrorlog) && corelog) {
+ log_error("Please use only one of --mirrorlog or --corelog");
+ return 0;
+ }
+
+ if (!strcmp("mirrored", mirrorlog)) {
+ lp->log_count = 2;
+ } else if (!strcmp("disk", mirrorlog)) {
+ lp->log_count = 1;
+ } else if (!strcmp("core", mirrorlog))
+ lp->log_count = 0;
+ else {
+ log_error("Unknown mirrorlog type: %s", mirrorlog);
+ return 0;
+ }
+
+ log_verbose("Setting logging type to %s", mirrorlog);
+
+ lp->nosync = arg_is_set(cmd, nosync_ARG);
+
+ if (arg_count(cmd, regionsize_ARG)) {
+ if (arg_sign_value(cmd, regionsize_ARG, 0) == SIGN_MINUS) {
+ log_error("Negative regionsize is invalid");
+ return 0;
+ }
+ lp->region_size = arg_uint_value(cmd, regionsize_ARG, 0);
+ } else {
+ region_size = 2 * find_config_tree_int(cmd,
+ "activation/mirror_region_size",
+ DEFAULT_MIRROR_REGION_SIZE);
+ if (region_size < 0) {
+ log_error("Negative regionsize in configuration file "
+ "is invalid");
+ return 0;
+ }
+ lp->region_size = region_size;
+ }
+
+ if (!_validate_mirror_params(cmd, lp))
+ return 0;
+
+ return 1;
+}
+
+static int _lvcreate_params(struct lvcreate_params *lp,
+ struct lvcreate_cmdline_params *lcp,
+ struct cmd_context *cmd,
+ int argc, char **argv)
+{
+ int contiguous;
+ unsigned pagesize;
+ struct arg_value_group_list *current_group;
+ const char *tag;
+
+ memset(lp, 0, sizeof(*lp));
+ memset(lcp, 0, sizeof(*lcp));
+ dm_list_init(&lp->tags);
+
+ /*
+ * Check selected options are compatible and determine segtype
+ */
+ lp->segtype = get_segtype_from_string(cmd, arg_str_value(cmd, type_ARG, "striped"));
+
+ if (arg_count(cmd, snapshot_ARG) || seg_is_snapshot(lp) ||
+ arg_count(cmd, virtualsize_ARG))
+ lp->snapshot = 1;
+
+ lp->mirrors = 1;
+
+ /* Default to 2 mirrored areas if --type mirror */
+ if (seg_is_mirrored(lp))
+ lp->mirrors = 2;
+
+ if (arg_count(cmd, mirrors_ARG)) {
+ lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0) + 1;
+ if (lp->mirrors == 1)
+ log_print("Redundant mirrors argument: default is 0");
+ if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) {
+ log_error("Mirrors argument may not be negative");
+ return 0;
+ }
+ }
+
+ if (lp->snapshot) {
+ if (arg_count(cmd, zero_ARG)) {
+ log_error("-Z is incompatible with snapshots");
+ return 0;
+ }
+ if (arg_sign_value(cmd, chunksize_ARG, 0) == SIGN_MINUS) {
+ log_error("Negative chunk size is invalid");
+ return 0;
+ }
+ lp->chunk_size = arg_uint_value(cmd, chunksize_ARG, 8);
+ if (lp->chunk_size < 8 || lp->chunk_size > 1024 ||
+ (lp->chunk_size & (lp->chunk_size - 1))) {
+ log_error("Chunk size must be a power of 2 in the "
+ "range 4K to 512K");
+ return 0;
+ }
+ log_verbose("Setting chunksize to %d sectors.", lp->chunk_size);
+
+ if (!(lp->segtype = get_segtype_from_string(cmd, "snapshot")))
+ return_0;
+ } else {
+ if (arg_count(cmd, chunksize_ARG)) {
+ log_error("-c is only available with snapshots");
+ return 0;
+ }
+ }
+
+ if (lp->mirrors > 1) {
+ if (lp->snapshot) {
+ log_error("mirrors and snapshots are currently "
+ "incompatible");
+ return 0;
+ }
+
+ if (!(lp->segtype = get_segtype_from_string(cmd, "striped")))
+ return_0;
+ } else {
+ if (arg_count(cmd, corelog_ARG)) {
+ log_error("--corelog is only available with mirrors");
+ return 0;
+ }
+
+ if (arg_count(cmd, mirrorlog_ARG)) {
+ log_error("--mirrorlog is only available with mirrors");
+ return 0;
+ }
+
+ if (arg_count(cmd, nosync_ARG)) {
+ log_error("--nosync is only available with mirrors");
+ return 0;
+ }
+ }
+
+ if (activation() && lp->segtype->ops->target_present &&
+ !lp->segtype->ops->target_present(cmd, NULL, NULL)) {
+ log_error("%s: Required device-mapper target(s) not "
+ "detected in your kernel", lp->segtype->name);
+ return 0;
+ }
+
+ if (!get_activation_monitoring_mode(cmd, NULL,
+ &lp->activation_monitoring))
+ return_0;
+
+ if (!_lvcreate_name_params(lp, cmd, &argc, &argv) ||
+ !_read_size_params(lp, lcp, cmd) ||
+ !get_stripe_params(cmd, &lp->stripes, &lp->stripe_size) ||
+ !_read_mirror_params(lp, cmd))
+ return_0;
+
+ /*
+ * Should we zero the lv.
+ */
+ lp->zero = strcmp(arg_str_value(cmd, zero_ARG,
+ (lp->segtype->flags & SEG_CANNOT_BE_ZEROED) ? "n" : "y"), "n");
+
+ /*
+ * Alloc policy
+ */
+ contiguous = strcmp(arg_str_value(cmd, contiguous_ARG, "n"), "n");
+
+ lp->alloc = contiguous ? ALLOC_CONTIGUOUS : ALLOC_INHERIT;
+
+ lp->alloc = arg_uint_value(cmd, alloc_ARG, lp->alloc);
+
+ if (contiguous && (lp->alloc != ALLOC_CONTIGUOUS)) {
+ log_error("Conflicting contiguous and alloc arguments");
+ return 0;
+ }
+
+ if (lp->mirrors > DEFAULT_MIRROR_MAX_IMAGES) {
+ log_error("Only up to %d images in mirror supported currently.",
+ DEFAULT_MIRROR_MAX_IMAGES);
+ return 0;
+ }
+
+ /*
+ * Read ahead.
+ */
+ lp->read_ahead = arg_uint_value(cmd, readahead_ARG,
+ cmd->default_settings.read_ahead);
+ pagesize = lvm_getpagesize() >> SECTOR_SHIFT;
+ if (lp->read_ahead != DM_READ_AHEAD_AUTO &&
+ lp->read_ahead != DM_READ_AHEAD_NONE &&
+ lp->read_ahead % pagesize) {
+ if (lp->read_ahead < pagesize)
+ lp->read_ahead = pagesize;
+ else
+ lp->read_ahead = (lp->read_ahead / pagesize) * pagesize;
+ log_warn("WARNING: Overriding readahead to %u sectors, a multiple "
+ "of %uK page size.", lp->read_ahead, pagesize >> 1);
+ }
+
+ /*
+ * Permissions.
+ */
+ lp->permission = arg_uint_value(cmd, permission_ARG,
+ LVM_READ | LVM_WRITE);
+
+ /* Must not zero read only volume */
+ if (!(lp->permission & LVM_WRITE))
+ lp->zero = 0;
+
+ lp->minor = arg_int_value(cmd, minor_ARG, -1);
+ lp->major = arg_int_value(cmd, major_ARG, -1);
+
+ /* Persistent minor */
+ if (arg_count(cmd, persistent_ARG)) {
+ if (!strcmp(arg_str_value(cmd, persistent_ARG, "n"), "y")) {
+ if (lp->minor == -1) {
+ log_error("Please specify minor number with "
+ "--minor when using -My");
+ return 0;
+ }
+ if (lp->major == -1) {
+ log_error("Please specify major number with "
+ "--major when using -My");
+ return 0;
+ }
+ } else {
+ if ((lp->minor != -1) || (lp->major != -1)) {
+ log_error("--major and --minor incompatible "
+ "with -Mn");
+ return 0;
+ }
+ }
+ } else if (arg_count(cmd, minor_ARG) || arg_count(cmd, major_ARG)) {
+ log_error("--major and --minor require -My");
+ return 0;
+ }
+
+ dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
+ if (!grouped_arg_is_set(current_group->arg_values, addtag_ARG))
+ continue;
+
+ if (!(tag = grouped_arg_str_value(current_group->arg_values, addtag_ARG, NULL))) {
+ log_error("Failed to get tag");
+ return 0;
+ }
+
+ if (!str_list_add(cmd->mem, &lp->tags, tag)) {
+ log_error("Unable to allocate memory for tag %s", tag);
+ return 0;
+ }
+ }
+
+ lcp->pv_count = argc;
+ lcp->pvs = argv;
+
+ return 1;
+}
+
+int lvcreate(struct cmd_context *cmd, int argc, char **argv)
+{
+ int r = ECMD_PROCESSED;
+ struct lvcreate_params lp;
+ struct lvcreate_cmdline_params lcp;
+ struct volume_group *vg;
+
+ memset(&lp, 0, sizeof(lp));
+
+ if (!_lvcreate_params(&lp, &lcp, cmd, argc, argv))
+ return EINVALID_CMD_LINE;
+
+ log_verbose("Finding volume group \"%s\"", lp.vg_name);
+ vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0);
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (!_update_extents_params(vg, &lp, &lcp)) {
+ r = ECMD_FAILED;
+ goto_out;
+ }
+
+ if (!lv_create_single(vg, &lp)) {
+ stack;
+ r = ECMD_FAILED;
+ }
+out:
+ unlock_and_free_vg(cmd, vg, lp.vg_name);
+ return r;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static int _lvdisplay_single(struct cmd_context *cmd, struct logical_volume *lv,
+ void *handle)
+{
+ if (!arg_count(cmd, all_ARG) && !lv_is_visible(lv))
+ return ECMD_PROCESSED;
+
+ if (arg_count(cmd, colon_ARG))
+ lvdisplay_colons(lv);
+ else {
+ lvdisplay_full(cmd, lv, handle);
+ if (arg_count(cmd, maps_ARG))
+ lvdisplay_segments(lv);
+ }
+
+ return ECMD_PROCESSED;
+}
+
+int lvdisplay(struct cmd_context *cmd, int argc, char **argv)
+{
+ if (arg_count(cmd, columns_ARG)) {
+ if (arg_count(cmd, colon_ARG) || arg_count(cmd, maps_ARG)) {
+ log_error("Incompatible options selected");
+ return EINVALID_CMD_LINE;
+ }
+ return lvs(cmd, argc, argv);
+ } else if (arg_count(cmd, aligned_ARG) ||
+ arg_count(cmd, noheadings_ARG) ||
+ arg_count(cmd, options_ARG) ||
+ arg_count(cmd, separator_ARG) ||
+ arg_count(cmd, sort_ARG) || arg_count(cmd, unbuffered_ARG)) {
+ log_error("Incompatible options selected");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, colon_ARG) && arg_count(cmd, verbose_ARG)) {
+ log_error("Options -v and -c are incompatible");
+ return EINVALID_CMD_LINE;
+ }
+
+ return process_each_lv(cmd, argc, argv, 0, NULL,
+ &_lvdisplay_single);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+int lvextend(struct cmd_context *cmd, int argc, char **argv)
+{
+ return lvresize(cmd, argc, argv);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "lvm2cmdline.h"
+
+int main(int argc, char **argv)
+{
+ init_is_static(1);
+ return lvm2_main(argc, argv);
+}
+
+int lvm_shell(struct cmd_context *cmd __attribute__((unused)),
+ struct cmdline_context *cmdline __attribute__((unused)))
+{
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "lvm2cmdline.h"
+
+int main(int argc, char **argv)
+{
+ return lvm2_main(argc, argv);
+}
+
+#ifdef READLINE_SUPPORT
+
+# include <readline/readline.h>
+# include <readline/history.h>
+# ifndef HAVE_RL_COMPLETION_MATCHES
+# define rl_completion_matches(a, b) completion_matches((char *)a, b)
+# endif
+
+static struct cmdline_context *_cmdline;
+
+/* List matching commands */
+static char *_list_cmds(const char *text, int state)
+{
+ static int i = 0;
+ static size_t len = 0;
+
+ /* Initialise if this is a new completion attempt */
+ if (!state) {
+ i = 0;
+ len = strlen(text);
+ }
+
+ while (i < _cmdline->num_commands)
+ if (!strncmp(text, _cmdline->commands[i++].name, len))
+ return strdup(_cmdline->commands[i - 1].name);
+
+ return NULL;
+}
+
+/* List matching arguments */
+static char *_list_args(const char *text, int state)
+{
+ static int match_no = 0;
+ static size_t len = 0;
+ static struct command *com;
+
+ /* Initialise if this is a new completion attempt */
+ if (!state) {
+ char *s = rl_line_buffer;
+ int j = 0;
+
+ match_no = 0;
+ com = NULL;
+ len = strlen(text);
+
+ /* Find start of first word in line buffer */
+ while (isspace(*s))
+ s++;
+
+ /* Look for word in list of commands */
+ for (j = 0; j < _cmdline->num_commands; j++) {
+ const char *p;
+ char *q = s;
+
+ p = _cmdline->commands[j].name;
+ while (*p == *q) {
+ p++;
+ q++;
+ }
+ if ((!*p) && *q == ' ') {
+ com = _cmdline->commands + j;
+ break;
+ }
+ }
+ }
+
+ if (!com)
+ return NULL;
+
+ /* Short form arguments */
+ if (len < 3) {
+ while (match_no < com->num_args) {
+ char s[3];
+ char c;
+ if (!(c = (_cmdline->arg_props +
+ com->valid_args[match_no++])->short_arg))
+ continue;
+
+ sprintf(s, "-%c", c);
+ if (!strncmp(text, s, len))
+ return strdup(s);
+ }
+ }
+
+ /* Long form arguments */
+ if (match_no < com->num_args)
+ match_no = com->num_args;
+
+ while (match_no - com->num_args < com->num_args) {
+ const char *l;
+ l = (_cmdline->arg_props +
+ com->valid_args[match_no++ - com->num_args])->long_arg;
+ if (*(l + 2) && !strncmp(text, l, len))
+ return strdup(l);
+ }
+
+ return NULL;
+}
+
+/* Custom completion function */
+static char **_completion(const char *text, int start_pos,
+ int end_pos __attribute__((unused)))
+{
+ char **match_list = NULL;
+ int p = 0;
+
+ while (isspace((int) *(rl_line_buffer + p)))
+ p++;
+
+ /* First word should be one of our commands */
+ if (start_pos == p)
+ match_list = rl_completion_matches(text, _list_cmds);
+
+ else if (*text == '-')
+ match_list = rl_completion_matches(text, _list_args);
+ /* else other args */
+
+ /* No further completion */
+ rl_attempted_completion_over = 1;
+ return match_list;
+}
+
+static int _hist_file(char *buffer, size_t size)
+{
+ char *e = getenv("HOME");
+
+ if (dm_snprintf(buffer, size, "%s/.lvm_history", e) < 0) {
+ log_error("$HOME/.lvm_history: path too long");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void _read_history(struct cmd_context *cmd)
+{
+ char hist_file[PATH_MAX];
+
+ if (!_hist_file(hist_file, sizeof(hist_file)))
+ return;
+
+ if (read_history(hist_file))
+ log_very_verbose("Couldn't read history from %s.", hist_file);
+
+ stifle_history(find_config_tree_int(cmd, "shell/history_size",
+ DEFAULT_MAX_HISTORY));
+
+}
+
+static void _write_history(void)
+{
+ char hist_file[PATH_MAX];
+
+ if (!_hist_file(hist_file, sizeof(hist_file)))
+ return;
+
+ if (write_history(hist_file))
+ log_very_verbose("Couldn't write history to %s.", hist_file);
+}
+
+int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline)
+{
+ int argc, ret;
+ char *input = NULL, *args[MAX_ARGS], **argv;
+
+ rl_readline_name = "lvm";
+ rl_attempted_completion_function = (CPPFunction *) _completion;
+
+ _read_history(cmd);
+
+ _cmdline = cmdline;
+
+ _cmdline->interactive = 1;
+ while (1) {
+ free(input);
+ input = readline("lvm> ");
+
+ /* EOF */
+ if (!input) {
+ printf("\n");
+ break;
+ }
+
+ /* empty line */
+ if (!*input)
+ continue;
+
+ add_history(input);
+
+ argv = args;
+
+ if (lvm_split(input, &argc, argv, MAX_ARGS) == MAX_ARGS) {
+ log_error("Too many arguments, sorry.");
+ continue;
+ }
+
+ if (!argc)
+ continue;
+
+ if (!strcmp(argv[0], "lvm")) {
+ argv++;
+ argc--;
+ }
+
+ if (!argc)
+ continue;
+
+ if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit")) {
+ remove_history(history_length - 1);
+ log_error("Exiting.");
+ break;
+ }
+
+ ret = lvm_run_command(cmd, argc, argv);
+ if (ret == ENO_SUCH_CMD)
+ log_error("No such command '%s'. Try 'help'.",
+ argv[0]);
+
+ if ((ret != ECMD_PROCESSED) && !error_message_produced()) {
+ log_debug(INTERNAL_ERROR "Failed command did not use log_error");
+ log_error("Command failed with status code %d.", ret);
+ }
+ _write_history();
+ }
+
+ free(input);
+ return 0;
+}
+
+#endif /* READLINE_SUPPORT */
--- /dev/null
+/*
+ * Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lvm2cmdline.h"
+#include "lvm2cmd.h"
+
+void *lvm2_init(void)
+{
+ return cmdlib_lvm2_init(1);
+}
--- /dev/null
+/*
+ * Copyright (C) 2006-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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lvm2cmdline.h"
+#include "lvm2cmd.h"
+
+void *lvm2_init(void)
+{
+ return cmdlib_lvm2_init(0);
+}
+
+int lvm_shell(struct cmd_context *cmd __attribute__((unused)),
+ struct cmdline_context *cmdline __attribute__((unused)))
+{
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_CMDLIB_H
+#define _LVM_CMDLIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _LVM_LOG_H
+typedef void (*lvm2_log_fn_t) (int level, const char *file, int line,
+ int dm_errno, const char *message);
+
+#endif
+
+#define LVM2_LOG_SUPPRESS 0
+
+/* Logging levels */
+#define LVM2_LOG_FATAL 2
+#define LVM2_LOG_ERROR 3
+#define LVM2_LOG_PRINT 4
+#define LVM2_LOG_VERBOSE 5
+#define LVM2_LOG_VERY_VERBOSE 6
+#define LVM2_LOG_DEBUG 7
+
+/*
+ * Define external function to replace the built-in logging function.
+ * It receives output line-by-line.
+ *
+ * level is the logging level (see above)
+ * file & line refer to the source code where the message originates.
+ */
+void lvm2_log_fn(lvm2_log_fn_t log_fn);
+
+/*
+ * Initialise library.
+ * Returns a handle so repeated use of lvm2_run is more efficient.
+ */
+void *lvm2_init(void);
+
+/*
+ * Set log level (as above) if using built-in logging function.
+ * Default is LVM2_LOG_PRINT. Use LVM2_LOG_SUPPRESS to suppress output.
+ */
+void lvm2_log_level(void *handle, int level);
+
+/*
+ * Run an LVM2 command.
+ * Use NULL handle if the call is a one-off and you don't want to bother
+ * calling lvm2_init/lvm2_exit.
+ */
+int lvm2_run(void *handle, const char *cmdline);
+
+/* Release handle */
+void lvm2_exit(void *handle);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_CMDLINE_H
+#define _LVM_CMDLINE_H
+
+struct cmd_context;
+
+struct cmdline_context {
+ struct arg_props *arg_props;
+ struct command *commands;
+ int num_commands;
+ int commands_size;
+ int interactive;
+};
+
+int lvm2_main(int argc, char **argv);
+
+void *cmdlib_lvm2_init(unsigned static_compile);
+void lvm_fin(struct cmd_context *cmd);
+
+struct cmd_context *init_lvm(void);
+void lvm_register_commands(void);
+int lvm_split(char *str, int *argc, char **argv, int max);
+int lvm_run_command(struct cmd_context *cmd, int argc, char **argv);
+int lvm_return_code(int ret);
+int lvm_shell(struct cmd_context *cmd, struct cmdline_context *cmdline);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+int lvmchange(struct cmd_context *cmd __attribute__((unused)),
+ int argc __attribute__((unused)), char **argv __attribute__((unused)))
+{
+ log_error("With LVM2 and the device mapper, this program is obsolete.");
+ return ECMD_FAILED;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "lvm2cmdline.h"
+#include "label.h"
+#include "memlock.h"
+#include "lvm-version.h"
+
+#include "lvm2cmd.h"
+
+#include <signal.h>
+#include <syslog.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <sys/resource.h>
+
+void *cmdlib_lvm2_init(unsigned static_compile)
+{
+ struct cmd_context *cmd;
+
+ lvm_register_commands();
+
+ init_is_static(static_compile);
+ if (!(cmd = init_lvm()))
+ return NULL;
+
+ return (void *) cmd;
+}
+
+int lvm2_run(void *handle, const char *cmdline)
+{
+ int argc, ret, oneoff = 0;
+ char *args[MAX_ARGS], **argv, *cmdcopy = NULL;
+ struct cmd_context *cmd;
+
+ argv = args;
+
+ if (!handle) {
+ oneoff = 1;
+ if (!(handle = lvm2_init())) {
+ log_error("Handle initialisation failed.");
+ return ECMD_FAILED;
+ }
+ }
+
+ cmd = (struct cmd_context *) handle;
+
+ cmd->argv = argv;
+
+ if (!(cmdcopy = dm_strdup(cmdline))) {
+ log_error("Cmdline copy failed.");
+ ret = ECMD_FAILED;
+ goto out;
+ }
+
+ if (lvm_split(cmdcopy, &argc, argv, MAX_ARGS) == MAX_ARGS) {
+ log_error("Too many arguments. Limit is %d.", MAX_ARGS);
+ ret = EINVALID_CMD_LINE;
+ goto out;
+ }
+
+ if (!argc) {
+ log_error("No command supplied");
+ ret = EINVALID_CMD_LINE;
+ goto out;
+ }
+
+ /* FIXME Temporary - move to libdevmapper */
+ ret = ECMD_PROCESSED;
+ if (!strcmp(cmdline, "_memlock_inc"))
+ memlock_inc_daemon(cmd);
+ else if (!strcmp(cmdline, "_memlock_dec"))
+ memlock_dec_daemon(cmd);
+ else
+ ret = lvm_run_command(cmd, argc, argv);
+
+ out:
+ dm_free(cmdcopy);
+
+ if (oneoff)
+ lvm2_exit(handle);
+
+ return ret;
+}
+
+void lvm2_log_level(void *handle, int level)
+{
+ struct cmd_context *cmd = (struct cmd_context *) handle;
+
+ cmd->default_settings.verbose = level - VERBOSE_BASE_LEVEL;
+}
+
+void lvm2_log_fn(lvm2_log_fn_t log_fn)
+{
+ init_log_fn(log_fn);
+}
+
+void lvm2_exit(void *handle)
+{
+ struct cmd_context *cmd = (struct cmd_context *) handle;
+
+ lvm_fin(cmd);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "lvm2cmdline.h"
+#include "label.h"
+#include "lvm-version.h"
+
+#include "stub.h"
+#include "lvm2cmd.h"
+#include "last-path-component.h"
+
+#include <signal.h>
+#include <syslog.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <sys/resource.h>
+
+#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
+
+#ifdef UDEV_SYNC_SUPPORT
+# define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
+# include <libudev.h>
+#endif
+
+/*
+ * Table of valid switches
+ */
+static struct arg_props _arg_props[ARG_COUNT + 1] = {
+#define arg(a, b, c, d, e) {b, "", "--" c, d, e},
+#include "args.h"
+#undef arg
+};
+
+static struct cmdline_context _cmdline;
+
+/* Command line args */
+unsigned arg_count(const struct cmd_context *cmd, int a)
+{
+ return cmd->arg_values[a].count;
+}
+
+unsigned grouped_arg_count(const struct arg_values *av, int a)
+{
+ return av[a].count;
+}
+
+unsigned arg_is_set(const struct cmd_context *cmd, int a)
+{
+ return arg_count(cmd, a) ? 1 : 0;
+}
+
+unsigned grouped_arg_is_set(const struct arg_values *av, int a)
+{
+ return grouped_arg_count(av, a) ? 1 : 0;
+}
+
+const char *arg_value(struct cmd_context *cmd, int a)
+{
+ return cmd->arg_values[a].value;
+}
+
+const char *arg_str_value(struct cmd_context *cmd, int a, const char *def)
+{
+ return arg_count(cmd, a) ? cmd->arg_values[a].value : def;
+}
+
+const char *grouped_arg_str_value(const struct arg_values *av, int a, const char *def)
+{
+ return grouped_arg_count(av, a) ? av[a].value : def;
+}
+
+int32_t arg_int_value(struct cmd_context *cmd, int a, const int32_t def)
+{
+ return arg_count(cmd, a) ? cmd->arg_values[a].i_value : def;
+}
+
+uint32_t arg_uint_value(struct cmd_context *cmd, int a, const uint32_t def)
+{
+ return arg_count(cmd, a) ? cmd->arg_values[a].ui_value : def;
+}
+
+int64_t arg_int64_value(struct cmd_context *cmd, int a, const int64_t def)
+{
+ return arg_count(cmd, a) ? cmd->arg_values[a].i64_value : def;
+}
+
+uint64_t arg_uint64_value(struct cmd_context *cmd, int a, const uint64_t def)
+{
+ return arg_count(cmd, a) ? cmd->arg_values[a].ui64_value : def;
+}
+
+/* No longer used.
+const void *arg_ptr_value(struct cmd_context *cmd, int a, const void *def)
+{
+ return arg_count(cmd, a) ? cmd->arg_values[a].ptr : def;
+}
+*/
+
+sign_t arg_sign_value(struct cmd_context *cmd, int a, const sign_t def)
+{
+ return arg_count(cmd, a) ? cmd->arg_values[a].sign : def;
+}
+
+percent_type_t arg_percent_value(struct cmd_context *cmd, int a, const percent_type_t def)
+{
+ return arg_count(cmd, a) ? cmd->arg_values[a].percent : def;
+}
+
+int arg_count_increment(struct cmd_context *cmd, int a)
+{
+ return cmd->arg_values[a].count++;
+}
+
+int yes_no_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+ av->sign = SIGN_NONE;
+ av->percent = PERCENT_NONE;
+
+ if (!strcmp(av->value, "y")) {
+ av->i_value = 1;
+ av->ui_value = 1;
+ }
+
+ else if (!strcmp(av->value, "n")) {
+ av->i_value = 0;
+ av->ui_value = 0;
+ }
+
+ else
+ return 0;
+
+ return 1;
+}
+
+int yes_no_excl_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+ av->sign = SIGN_NONE;
+ av->percent = PERCENT_NONE;
+
+ if (!strcmp(av->value, "e") || !strcmp(av->value, "ey") ||
+ !strcmp(av->value, "ye")) {
+ av->i_value = CHANGE_AE;
+ av->ui_value = CHANGE_AE;
+ }
+
+ else if (!strcmp(av->value, "y")) {
+ av->i_value = CHANGE_AY;
+ av->ui_value = CHANGE_AY;
+ }
+
+ else if (!strcmp(av->value, "n") || !strcmp(av->value, "en") ||
+ !strcmp(av->value, "ne")) {
+ av->i_value = CHANGE_AN;
+ av->ui_value = CHANGE_AN;
+ }
+
+ else if (!strcmp(av->value, "ln") || !strcmp(av->value, "nl")) {
+ av->i_value = CHANGE_ALN;
+ av->ui_value = CHANGE_ALN;
+ }
+
+ else if (!strcmp(av->value, "ly") || !strcmp(av->value, "yl")) {
+ av->i_value = CHANGE_ALY;
+ av->ui_value = CHANGE_ALY;
+ }
+
+ else
+ return 0;
+
+ return 1;
+}
+
+int metadatatype_arg(struct cmd_context *cmd, struct arg_values *av)
+{
+ return get_format_by_name(cmd, av->value) ? 1 : 0;
+}
+
+static int _get_int_arg(struct arg_values *av, char **ptr)
+{
+ char *val;
+ long v;
+
+ av->percent = PERCENT_NONE;
+
+ val = av->value;
+ switch (*val) {
+ case '+':
+ av->sign = SIGN_PLUS;
+ val++;
+ break;
+ case '-':
+ av->sign = SIGN_MINUS;
+ val++;
+ break;
+ default:
+ av->sign = SIGN_NONE;
+ }
+
+ if (!isdigit(*val))
+ return 0;
+
+ v = strtol(val, ptr, 10);
+
+ if (*ptr == val)
+ return 0;
+
+ av->i_value = (int32_t) v;
+ av->ui_value = (uint32_t) v;
+ av->i64_value = (int64_t) v;
+ av->ui64_value = (uint64_t) v;
+
+ return 1;
+}
+
+/* Size stored in sectors */
+static int _size_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av, int factor)
+{
+ char *ptr;
+ int i;
+ static const char *suffixes = "kmgtpebs";
+ char *val;
+ double v;
+ uint64_t v_tmp, adjustment;
+
+ av->percent = PERCENT_NONE;
+
+ val = av->value;
+ switch (*val) {
+ case '+':
+ av->sign = SIGN_PLUS;
+ val++;
+ break;
+ case '-':
+ av->sign = SIGN_MINUS;
+ val++;
+ break;
+ default:
+ av->sign = SIGN_NONE;
+ }
+
+ if (!isdigit(*val))
+ return 0;
+
+ v = strtod(val, &ptr);
+
+ if (ptr == val)
+ return 0;
+
+ if (*ptr) {
+ for (i = strlen(suffixes) - 1; i >= 0; i--)
+ if (suffixes[i] == tolower((int) *ptr))
+ break;
+
+ if (i < 0) {
+ return 0;
+ } else if (i == 7) {
+ /* sectors */
+ v = v;
+ } else if (i == 6) {
+ /* bytes */
+ v_tmp = (uint64_t) v;
+ adjustment = v_tmp % 512;
+ if (adjustment) {
+ v_tmp += (512 - adjustment);
+ log_error("Size is not a multiple of 512. "
+ "Try using %"PRIu64" or %"PRIu64".",
+ v_tmp - 512, v_tmp);
+ return 0;
+ }
+ v /= 512;
+ } else {
+ /* all other units: kmgtpe */
+ while (i-- > 0)
+ v *= 1024;
+ v *= 2;
+ }
+ } else
+ v *= factor;
+
+ av->i_value = (int32_t) v;
+ av->ui_value = (uint32_t) v;
+ av->i64_value = (int64_t) v;
+ av->ui64_value = (uint64_t) v;
+
+ return 1;
+}
+
+int size_kb_arg(struct cmd_context *cmd, struct arg_values *av)
+{
+ return _size_arg(cmd, av, 2);
+}
+
+int size_mb_arg(struct cmd_context *cmd, struct arg_values *av)
+{
+ return _size_arg(cmd, av, 2048);
+}
+
+int int_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+ char *ptr;
+
+ if (!_get_int_arg(av, &ptr) || (*ptr) || (av->sign == SIGN_MINUS))
+ return 0;
+
+ return 1;
+}
+
+int int_arg_with_sign(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+ char *ptr;
+
+ if (!_get_int_arg(av, &ptr) || (*ptr))
+ return 0;
+
+ return 1;
+}
+
+int int_arg_with_sign_and_percent(struct cmd_context *cmd __attribute__((unused)),
+ struct arg_values *av)
+{
+ char *ptr;
+
+ if (!_get_int_arg(av, &ptr))
+ return 0;
+
+ if (!*ptr)
+ return 1;
+
+ if (*ptr++ != '%')
+ return 0;
+
+ if (!strcasecmp(ptr, "V") || !strcasecmp(ptr, "VG"))
+ av->percent = PERCENT_VG;
+ else if (!strcasecmp(ptr, "L") || !strcasecmp(ptr, "LV"))
+ av->percent = PERCENT_LV;
+ else if (!strcasecmp(ptr, "P") || !strcasecmp(ptr, "PV") ||
+ !strcasecmp(ptr, "PVS"))
+ av->percent = PERCENT_PVS;
+ else if (!strcasecmp(ptr, "F") || !strcasecmp(ptr, "FR") ||
+ !strcasecmp(ptr, "FREE"))
+ av->percent = PERCENT_FREE;
+ else if (!strcasecmp(ptr, "O") || !strcasecmp(ptr, "OR") ||
+ !strcasecmp(ptr, "ORIGIN"))
+ av->percent = PERCENT_ORIGIN;
+ else
+ return 0;
+
+ return 1;
+}
+
+int minor_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+ char *ptr;
+
+ if (!_get_int_arg(av, &ptr) || (*ptr) || (av->sign == SIGN_MINUS))
+ return 0;
+
+ if (av->i_value > 255) {
+ log_error("Minor number outside range 0-255");
+ return 0;
+ }
+
+ return 1;
+}
+
+int major_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+ char *ptr;
+
+ if (!_get_int_arg(av, &ptr) || (*ptr) || (av->sign == SIGN_MINUS))
+ return 0;
+
+ if (av->i_value > 255) {
+ log_error("Major number outside range 0-255");
+ return 0;
+ }
+
+ /* FIXME Also Check against /proc/devices */
+
+ return 1;
+}
+
+int string_arg(struct cmd_context *cmd __attribute__((unused)),
+ struct arg_values *av __attribute__((unused)))
+{
+ return 1;
+}
+
+int tag_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+ char *pos = av->value;
+
+ if (*pos == '@')
+ pos++;
+
+ if (!validate_tag(pos))
+ return 0;
+
+ av->value = pos;
+
+ return 1;
+}
+
+int permission_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+ av->sign = SIGN_NONE;
+
+ if ((!strcmp(av->value, "rw")) || (!strcmp(av->value, "wr")))
+ av->ui_value = LVM_READ | LVM_WRITE;
+
+ else if (!strcmp(av->value, "r"))
+ av->ui_value = LVM_READ;
+
+ else
+ return 0;
+
+ return 1;
+}
+
+int alloc_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+ alloc_policy_t alloc;
+
+ av->sign = SIGN_NONE;
+
+ alloc = get_alloc_from_string(av->value);
+ if (alloc == ALLOC_INVALID)
+ return 0;
+
+ av->ui_value = (uint32_t) alloc;
+
+ return 1;
+}
+
+int segtype_arg(struct cmd_context *cmd, struct arg_values *av)
+{
+ return get_segtype_from_string(cmd, av->value) ? 1 : 0;
+}
+
+/*
+ * Positive integer, zero or "auto".
+ */
+int readahead_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av)
+{
+ if (!strcasecmp(av->value, "auto")) {
+ av->ui_value = DM_READ_AHEAD_AUTO;
+ return 1;
+ }
+
+ if (!strcasecmp(av->value, "none")) {
+ av->ui_value = DM_READ_AHEAD_NONE;
+ return 1;
+ }
+
+ if (!_size_arg(cmd, av, 1))
+ return 0;
+
+ if (av->sign == SIGN_MINUS)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Non-zero, positive integer, "all", or "unmanaged"
+ */
+int metadatacopies_arg(struct cmd_context *cmd, struct arg_values *av)
+{
+ if (!strncmp(cmd->command->name, "vg", 2)) {
+ if (!strcasecmp(av->value, "all")) {
+ av->ui_value = VGMETADATACOPIES_ALL;
+ return 1;
+ }
+
+ if (!strcasecmp(av->value, "unmanaged")) {
+ av->ui_value = VGMETADATACOPIES_UNMANAGED;
+ return 1;
+ }
+ }
+
+ return int_arg(cmd, av);
+}
+
+static void __alloc(int size)
+{
+ if (!(_cmdline.commands = dm_realloc(_cmdline.commands, sizeof(*_cmdline.commands) * size))) {
+ log_fatal("Couldn't allocate memory.");
+ exit(ECMD_FAILED);
+ }
+
+ _cmdline.commands_size = size;
+}
+
+static void _alloc_command(void)
+{
+ if (!_cmdline.commands_size)
+ __alloc(32);
+
+ if (_cmdline.commands_size <= _cmdline.num_commands)
+ __alloc(2 * _cmdline.commands_size);
+}
+
+static void _create_new_command(const char *name, command_fn command,
+ unsigned flags,
+ const char *desc, const char *usagestr,
+ int nargs, int *args)
+{
+ struct command *nc;
+
+ _alloc_command();
+
+ nc = _cmdline.commands + _cmdline.num_commands++;
+
+ nc->name = name;
+ nc->desc = desc;
+ nc->usage = usagestr;
+ nc->fn = command;
+ nc->flags = flags;
+ nc->num_args = nargs;
+ nc->valid_args = args;
+}
+
+static void _register_command(const char *name, command_fn fn, const char *desc,
+ unsigned flags, const char *usagestr, ...)
+{
+ int nargs = 0, i;
+ int *args;
+ va_list ap;
+
+ /* count how many arguments we have */
+ va_start(ap, usagestr);
+ while (va_arg(ap, int) >= 0)
+ nargs++;
+ va_end(ap);
+
+ /* allocate space for them */
+ if (!(args = dm_malloc(sizeof(*args) * nargs))) {
+ log_fatal("Out of memory.");
+ exit(ECMD_FAILED);
+ }
+
+ /* fill them in */
+ va_start(ap, usagestr);
+ for (i = 0; i < nargs; i++)
+ args[i] = va_arg(ap, int);
+ va_end(ap);
+
+ /* enter the command in the register */
+ _create_new_command(name, fn, flags, desc, usagestr, nargs, args);
+}
+
+void lvm_register_commands(void)
+{
+#define xx(a, b, c, d...) _register_command(# a, a, b, c, ## d, \
+ driverloaded_ARG, \
+ debug_ARG, help_ARG, help2_ARG, \
+ version_ARG, verbose_ARG, \
+ quiet_ARG, config_ARG, -1);
+#include "commands.h"
+#undef xx
+}
+
+static struct command *_find_command(const char *name)
+{
+ int i;
+ const char *base;
+
+ base = last_path_component(name);
+
+ for (i = 0; i < _cmdline.num_commands; i++) {
+ if (!strcmp(base, _cmdline.commands[i].name))
+ break;
+ }
+
+ if (i >= _cmdline.num_commands)
+ return 0;
+
+ return _cmdline.commands + i;
+}
+
+static void _short_usage(const char *name)
+{
+ log_error("Run `%s --help' for more information.", name);
+}
+
+static int _usage(const char *name)
+{
+ struct command *com = _find_command(name);
+
+ if (!com) {
+ log_print("%s: no such command.", name);
+ return 0;
+ }
+
+ log_print("%s: %s\n\n%s", com->name, com->desc, com->usage);
+ return 1;
+}
+
+/*
+ * Sets up the short and long argument. If there
+ * is no short argument then the index of the
+ * argument in the the_args array is set as the
+ * long opt value. Yuck. Of course this means we
+ * can't have more than 'a' long arguments.
+ */
+static void _add_getopt_arg(int arg, char **ptr, struct option **o)
+{
+ struct arg_props *a = _cmdline.arg_props + arg;
+
+ if (a->short_arg) {
+ *(*ptr)++ = a->short_arg;
+
+ if (a->fn)
+ *(*ptr)++ = ':';
+ }
+#ifdef HAVE_GETOPTLONG
+ if (*(a->long_arg + 2)) {
+ (*o)->name = a->long_arg + 2;
+ (*o)->has_arg = a->fn ? 1 : 0;
+ (*o)->flag = NULL;
+ if (a->short_arg)
+ (*o)->val = a->short_arg;
+ else
+ (*o)->val = arg;
+ (*o)++;
+ }
+#endif
+}
+
+static int _find_arg(struct command *com, int opt)
+{
+ struct arg_props *a;
+ int i, arg;
+
+ for (i = 0; i < com->num_args; i++) {
+ arg = com->valid_args[i];
+ a = _cmdline.arg_props + arg;
+
+ /*
+ * opt should equal either the
+ * short arg, or the index into
+ * the_args.
+ */
+ if ((a->short_arg && (opt == a->short_arg)) ||
+ (!a->short_arg && (opt == arg)))
+ return arg;
+ }
+
+ return -1;
+}
+
+static int _process_command_line(struct cmd_context *cmd, int *argc,
+ char ***argv)
+{
+ int i, opt, arg;
+ char str[((ARG_COUNT + 1) * 2) + 1], *ptr = str;
+ struct option opts[ARG_COUNT + 1], *o = opts;
+ struct arg_props *a;
+ struct arg_values *av;
+ struct arg_value_group_list *current_group = NULL;
+
+ if (!(cmd->arg_values = dm_pool_zalloc(cmd->mem, sizeof(*cmd->arg_values) * ARG_COUNT))) {
+ log_fatal("Unable to allocate memory for command line arguments.");
+ return 0;
+ }
+
+ /* fill in the short and long opts */
+ for (i = 0; i < cmd->command->num_args; i++)
+ _add_getopt_arg(cmd->command->valid_args[i], &ptr, &o);
+
+ *ptr = '\0';
+ memset(o, 0, sizeof(*o));
+
+ /* initialise getopt_long & scan for command line switches */
+ optarg = 0;
+ optind = OPTIND_INIT;
+ while ((opt = GETOPTLONG_FN(*argc, *argv, str, opts, NULL)) >= 0) {
+
+ if (opt == '?')
+ return 0;
+
+ if ((arg = _find_arg(cmd->command, opt)) < 0) {
+ log_fatal("Unrecognised option.");
+ return 0;
+ }
+
+ a = _cmdline.arg_props + arg;
+
+ av = &cmd->arg_values[arg];
+
+ if (a->flags & ARG_GROUPABLE) {
+ /* Start a new group of arguments the first time or if a non-countable argument is repeated. */
+ if (!current_group || (current_group->arg_values[arg].count && !(a->flags & ARG_COUNTABLE))) {
+ /* FIXME Reduce size including only groupable args */
+ if (!(current_group = dm_pool_zalloc(cmd->mem, sizeof(struct arg_value_group_list) + sizeof(*cmd->arg_values) * ARG_COUNT))) {
+ log_fatal("Unable to allocate memory for command line arguments.");
+ return 0;
+ }
+
+ dm_list_add(&cmd->arg_value_groups, ¤t_group->list);
+ }
+ /* Maintain total argument count as well as count within each group */
+ av->count++;
+ av = ¤t_group->arg_values[arg];
+ }
+
+ if (av->count && !(a->flags & ARG_COUNTABLE)) {
+ log_error("Option%s%c%s%s may not be repeated.",
+ a->short_arg ? " -" : "",
+ a->short_arg ? : ' ',
+ (a->short_arg && a->long_arg) ?
+ "/" : "", a->long_arg ? : "");
+ return 0;
+ }
+
+ if (a->fn) {
+ if (!optarg) {
+ log_error("Option requires argument.");
+ return 0;
+ }
+
+ av->value = optarg;
+
+ if (!a->fn(cmd, av)) {
+ log_error("Invalid argument for %s: %s", a->long_arg, optarg);
+ return 0;
+ }
+ }
+
+ av->count++;
+ }
+
+ *argc -= optind;
+ *argv += optind;
+ return 1;
+}
+
+static int _merge_synonym(struct cmd_context *cmd, int oldarg, int newarg)
+{
+ const struct arg_values *old;
+ struct arg_values *new;
+
+ if (arg_count(cmd, oldarg) && arg_count(cmd, newarg)) {
+ log_error("%s and %s are synonyms. Please only supply one.",
+ _cmdline.arg_props[oldarg].long_arg, _cmdline.arg_props[newarg].long_arg);
+ return 0;
+ }
+
+ if (!arg_count(cmd, oldarg))
+ return 1;
+
+ old = cmd->arg_values + oldarg;
+ new = cmd->arg_values + newarg;
+
+ new->count = old->count;
+ new->value = old->value;
+ new->i_value = old->i_value;
+ new->ui_value = old->ui_value;
+ new->i64_value = old->i64_value;
+ new->ui64_value = old->ui64_value;
+ new->sign = old->sign;
+
+ return 1;
+}
+
+int version(struct cmd_context *cmd __attribute__((unused)),
+ int argc __attribute__((unused)),
+ char **argv __attribute__((unused)))
+{
+ char vsn[80];
+
+ log_print("LVM version: %s", LVM_VERSION);
+ if (library_version(vsn, sizeof(vsn)))
+ log_print("Library version: %s", vsn);
+ if (driver_version(vsn, sizeof(vsn)))
+ log_print("Driver version: %s", vsn);
+
+ return ECMD_PROCESSED;
+}
+
+static int _get_settings(struct cmd_context *cmd)
+{
+ cmd->current_settings = cmd->default_settings;
+
+ if (arg_count(cmd, debug_ARG))
+ cmd->current_settings.debug = _LOG_FATAL +
+ (arg_count(cmd, debug_ARG) - 1);
+
+ if (arg_count(cmd, verbose_ARG))
+ cmd->current_settings.verbose = arg_count(cmd, verbose_ARG);
+
+ if (arg_count(cmd, quiet_ARG)) {
+ cmd->current_settings.debug = 0;
+ cmd->current_settings.verbose = 0;
+ }
+
+ if (arg_count(cmd, test_ARG))
+ cmd->current_settings.test = arg_count(cmd, test_ARG);
+
+ if (arg_count(cmd, driverloaded_ARG)) {
+ cmd->current_settings.activation =
+ arg_int_value(cmd, driverloaded_ARG,
+ cmd->default_settings.activation);
+ }
+
+ cmd->current_settings.archive = arg_int_value(cmd, autobackup_ARG, cmd->current_settings.archive);
+ cmd->current_settings.backup = arg_int_value(cmd, autobackup_ARG, cmd->current_settings.backup);
+ cmd->current_settings.cache_vgmetadata = cmd->command->flags & CACHE_VGMETADATA ? 1 : 0;
+ cmd->partial_activation = 0;
+
+ if (arg_count(cmd, partial_ARG)) {
+ cmd->partial_activation = 1;
+ log_print("Partial mode. Incomplete logical volumes will be processed.");
+ }
+
+ if (arg_count(cmd, ignorelockingfailure_ARG) || arg_count(cmd, sysinit_ARG))
+ init_ignorelockingfailure(1);
+ else
+ init_ignorelockingfailure(0);
+
+ if (arg_count(cmd, nosuffix_ARG))
+ cmd->current_settings.suffix = 0;
+
+ if (arg_count(cmd, units_ARG))
+ if (!(cmd->current_settings.unit_factor =
+ units_to_bytes(arg_str_value(cmd, units_ARG, ""),
+ &cmd->current_settings.unit_type))) {
+ log_error("Invalid units specification");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, trustcache_ARG)) {
+ if (arg_count(cmd, all_ARG)) {
+ log_error("--trustcache is incompatible with --all");
+ return EINVALID_CMD_LINE;
+ }
+ init_trust_cache(1);
+ log_warn("WARNING: Cache file of PVs will be trusted. "
+ "New devices holding PVs may get ignored.");
+ } else
+ init_trust_cache(0);
+
+ if (arg_count(cmd, noudevsync_ARG))
+ cmd->current_settings.udev_sync = 0;
+
+ /* Handle synonyms */
+ if (!_merge_synonym(cmd, resizable_ARG, resizeable_ARG) ||
+ !_merge_synonym(cmd, allocation_ARG, allocatable_ARG) ||
+ !_merge_synonym(cmd, allocation_ARG, resizeable_ARG) ||
+ !_merge_synonym(cmd, virtualoriginsize_ARG, virtualsize_ARG))
+ return EINVALID_CMD_LINE;
+
+ if ((!strncmp(cmd->command->name, "pv", 2) &&
+ !_merge_synonym(cmd, metadatacopies_ARG, pvmetadatacopies_ARG)) ||
+ (!strncmp(cmd->command->name, "vg", 2) &&
+ !_merge_synonym(cmd, metadatacopies_ARG, vgmetadatacopies_ARG)))
+ return EINVALID_CMD_LINE;
+
+ /* Zero indicates success */
+ return 0;
+}
+
+static int _process_common_commands(struct cmd_context *cmd)
+{
+ if (arg_count(cmd, help_ARG) || arg_count(cmd, help2_ARG)) {
+ _usage(cmd->command->name);
+ return ECMD_PROCESSED;
+ }
+
+ if (arg_count(cmd, version_ARG)) {
+ return version(cmd, 0, (char **) NULL);
+ }
+
+ /* Zero indicates it's OK to continue processing this command */
+ return 0;
+}
+
+static void _display_help(void)
+{
+ int i;
+
+ log_error("Available lvm commands:");
+ log_error("Use 'lvm help <command>' for more information");
+ log_error(" ");
+
+ for (i = 0; i < _cmdline.num_commands; i++) {
+ struct command *com = _cmdline.commands + i;
+
+ log_error("%-16.16s%s", com->name, com->desc);
+ }
+}
+
+int help(struct cmd_context *cmd __attribute__((unused)), int argc, char **argv)
+{
+ int ret = ECMD_PROCESSED;
+
+ if (!argc)
+ _display_help();
+ else {
+ int i;
+ for (i = 0; i < argc; i++)
+ if (!_usage(argv[i]))
+ ret = EINVALID_CMD_LINE;
+ }
+
+ return ret;
+}
+
+static void _apply_settings(struct cmd_context *cmd)
+{
+ init_debug(cmd->current_settings.debug);
+ init_verbose(cmd->current_settings.verbose + VERBOSE_BASE_LEVEL);
+ init_test(cmd->current_settings.test);
+ init_full_scan_done(0);
+ init_mirror_in_sync(0);
+
+ init_msg_prefix(cmd->default_settings.msg_prefix);
+ init_cmd_name(cmd->default_settings.cmd_name);
+
+ archive_enable(cmd, cmd->current_settings.archive);
+ backup_enable(cmd, cmd->current_settings.backup);
+
+ set_activation(cmd->current_settings.activation);
+
+ cmd->fmt = get_format_by_name(cmd, arg_str_value(cmd, metadatatype_ARG,
+ cmd->current_settings.fmt_name));
+
+ cmd->handles_missing_pvs = 0;
+}
+
+static int _set_udev_checking(struct cmd_context *cmd)
+{
+#ifdef UDEV_SYNC_SUPPORT
+ struct udev *udev;
+ const char *udev_dev_dir;
+ size_t udev_dev_dir_len;
+ int dirs_diff;
+
+ if (!(udev = udev_new()) ||
+ !(udev_dev_dir = udev_get_dev_path(udev)) ||
+ !*udev_dev_dir) {
+ log_error("Could not get udev dev path.");
+ return 0;
+ }
+ udev_dev_dir_len = strlen(udev_dev_dir);
+
+ /* There's always a slash at the end of dev_dir. But check udev_dev_dir! */
+ if (udev_dev_dir[udev_dev_dir_len - 1] != '/')
+ dirs_diff = strncmp(cmd->dev_dir, udev_dev_dir,
+ udev_dev_dir_len);
+ else
+ dirs_diff = strcmp(cmd->dev_dir, udev_dev_dir);
+
+ if (dirs_diff) {
+ log_debug("The path %s used for creating device nodes and "
+ "symlinks that is set in the configuration differs "
+ "from the path %s that is used by udev. All warnings "
+ "about udev not working correctly while processing "
+ "particular nodes and symlinks will be suppressed. "
+ "These nodes and symlinks will be managed in each "
+ "directory separately.",
+ cmd->dev_dir, udev_dev_dir);
+ dm_udev_set_checking(0);
+ init_udev_checking(0);
+ }
+
+ udev_unref(udev);
+#endif
+ return 1;
+}
+
+static const char *_copy_command_line(struct cmd_context *cmd, int argc, char **argv)
+{
+ int i, space;
+
+ /*
+ * Build up the complete command line, used as a
+ * description for backups.
+ */
+ if (!dm_pool_begin_object(cmd->mem, 128))
+ goto_bad;
+
+ for (i = 0; i < argc; i++) {
+ space = strchr(argv[i], ' ') ? 1 : 0;
+
+ if (space && !dm_pool_grow_object(cmd->mem, "'", 1))
+ goto_bad;
+
+ if (!dm_pool_grow_object(cmd->mem, argv[i], strlen(argv[i])))
+ goto_bad;
+
+ if (space && !dm_pool_grow_object(cmd->mem, "'", 1))
+ goto_bad;
+
+ if (i < (argc - 1))
+ if (!dm_pool_grow_object(cmd->mem, " ", 1))
+ goto_bad;
+ }
+
+ /*
+ * Terminate.
+ */
+ if (!dm_pool_grow_object(cmd->mem, "\0", 1))
+ goto_bad;
+
+ return dm_pool_end_object(cmd->mem);
+
+ bad:
+ log_error("Couldn't copy command line.");
+ dm_pool_abandon_object(cmd->mem);
+ return NULL;
+}
+
+int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
+{
+ int ret = 0;
+ int locking_type;
+
+ init_error_message_produced(0);
+
+ /* each command should start out with sigint flag cleared */
+ sigint_clear();
+
+ if (!(cmd->cmd_line = _copy_command_line(cmd, argc, argv))) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ log_debug("Parsing: %s", cmd->cmd_line);
+
+ if (!(cmd->command = _find_command(argv[0])))
+ return ENO_SUCH_CMD;
+
+ if (!_process_command_line(cmd, &argc, &argv)) {
+ log_error("Error during parsing of command line.");
+ return EINVALID_CMD_LINE;
+ }
+
+ set_cmd_name(cmd->command->name);
+
+ if (arg_count(cmd, config_ARG))
+ if (override_config_tree_from_string(cmd,
+ arg_str_value(cmd, config_ARG, ""))) {
+ ret = EINVALID_CMD_LINE;
+ goto_out;
+ }
+
+ if (arg_count(cmd, config_ARG) || !cmd->config_valid || config_files_changed(cmd)) {
+ /* Reinitialise various settings inc. logging, filters */
+ if (!refresh_toolcontext(cmd)) {
+ if (cmd->cft_override) {
+ destroy_config_tree(cmd->cft_override);
+ cmd->cft_override = NULL;
+ }
+ log_error("Updated config file invalid. Aborting.");
+ return ECMD_FAILED;
+ }
+ }
+
+ if ((ret = _get_settings(cmd)))
+ goto_out;
+ _apply_settings(cmd);
+
+ log_debug("Processing: %s", cmd->cmd_line);
+
+#ifdef O_DIRECT_SUPPORT
+ log_debug("O_DIRECT will be used");
+#endif
+
+ if (!_set_udev_checking(cmd))
+ goto_out;
+
+ if ((ret = _process_common_commands(cmd)))
+ goto_out;
+
+ if (cmd->metadata_read_only &&
+ !(cmd->command->flags & PERMITTED_READ_ONLY)) {
+ log_error("%s: Command not permitted while global/metadata_read_only "
+ "is set.", cmd->cmd_line);
+ goto out;
+ }
+
+ if (arg_count(cmd, nolocking_ARG))
+ locking_type = 0;
+ else
+ locking_type = -1;
+
+ if (!init_locking(locking_type, cmd, arg_count(cmd, sysinit_ARG))) {
+ ret = ECMD_FAILED;
+ goto out;
+ }
+
+ ret = cmd->command->fn(cmd, argc, argv);
+
+ fin_locking();
+
+ out:
+ if (test_mode()) {
+ log_verbose("Test mode: Wiping internal cache");
+ lvmcache_destroy(cmd, 1);
+ }
+
+ if (cmd->cft_override) {
+ destroy_config_tree(cmd->cft_override);
+ cmd->cft_override = NULL;
+ /* Move this? */
+ if (!refresh_toolcontext(cmd))
+ stack;
+ }
+
+ /* FIXME Move this? */
+ cmd->current_settings = cmd->default_settings;
+ _apply_settings(cmd);
+
+ if (ret == EINVALID_CMD_LINE && !_cmdline.interactive)
+ _short_usage(cmd->command->name);
+
+ log_debug("Completed: %s", cmd->cmd_line);
+
+ /*
+ * free off any memory the command used.
+ */
+ dm_list_init(&cmd->arg_value_groups);
+ dm_pool_empty(cmd->mem);
+
+ reset_lvm_errno(1);
+ reset_log_duplicated();
+
+ return ret;
+}
+
+int lvm_return_code(int ret)
+{
+ return (ret == ECMD_PROCESSED ? 0 : ret);
+}
+
+int lvm_split(char *str, int *argc, char **argv, int max)
+{
+ char *b = str, *e;
+ *argc = 0;
+
+ while (*b) {
+ while (*b && isspace(*b))
+ b++;
+
+ if ((!*b) || (*b == '#'))
+ break;
+
+ e = b;
+ while (*e && !isspace(*e))
+ e++;
+
+ argv[(*argc)++] = b;
+ if (!*e)
+ break;
+ *e++ = '\0';
+ b = e;
+ if (*argc == max)
+ break;
+ }
+
+ return *argc;
+}
+
+static const char *_get_cmdline(pid_t pid)
+{
+ static char _proc_cmdline[32];
+ char buf[256];
+ int fd, n = 0;
+
+ snprintf(buf, sizeof(buf), DEFAULT_PROC_DIR "/%u/cmdline", pid);
+ /* FIXME Use generic read code. */
+ if ((fd = open(buf, O_RDONLY)) > 0) {
+ if ((n = read(fd, _proc_cmdline, sizeof(_proc_cmdline) - 1)) < 0) {
+ log_sys_error("read", buf);
+ n = 0;
+ }
+ if (close(fd))
+ log_sys_error("close", buf);
+ }
+ _proc_cmdline[n] = '\0';
+
+ return _proc_cmdline;
+}
+
+static const char *_get_filename(int fd)
+{
+ static char filename[PATH_MAX];
+ char buf[32]; /* Assumes short DEFAULT_PROC_DIR */
+ int size;
+
+ snprintf(buf, sizeof(buf), DEFAULT_PROC_DIR "/self/fd/%u", fd);
+
+ if ((size = readlink(buf, filename, sizeof(filename) - 1)) == -1)
+ filename[0] = '\0';
+ else
+ filename[size] = '\0';
+
+ return filename;
+}
+
+static void _close_descriptor(int fd, unsigned suppress_warnings,
+ const char *command, pid_t ppid,
+ const char *parent_cmdline)
+{
+ int r;
+ const char *filename;
+
+ /* Ignore bad file descriptors */
+ if (fcntl(fd, F_GETFD) == -1 && errno == EBADF)
+ return;
+
+ if (!suppress_warnings)
+ filename = _get_filename(fd);
+
+ r = close(fd);
+ if (suppress_warnings)
+ return;
+
+ if (!r)
+ fprintf(stderr, "File descriptor %d (%s) leaked on "
+ "%s invocation.", fd, filename, command);
+ else if (errno == EBADF)
+ return;
+ else
+ fprintf(stderr, "Close failed on stray file descriptor "
+ "%d (%s): %s", fd, filename, strerror(errno));
+
+ fprintf(stderr, " Parent PID %" PRIpid_t ": %s\n", ppid, parent_cmdline);
+}
+
+static void _close_stray_fds(const char *command)
+{
+ struct rlimit rlim;
+ int fd;
+ unsigned suppress_warnings = 0;
+ pid_t ppid = getppid();
+ const char *parent_cmdline = _get_cmdline(ppid);
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+ fprintf(stderr, "getrlimit(RLIMIT_NOFILE) failed: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ if (getenv("LVM_SUPPRESS_FD_WARNINGS"))
+ suppress_warnings = 1;
+
+ for (fd = 3; fd < rlim.rlim_cur; fd++)
+ _close_descriptor(fd, suppress_warnings, command, ppid,
+ parent_cmdline);
+}
+
+struct cmd_context *init_lvm(void)
+{
+ struct cmd_context *cmd;
+
+ if (!(cmd = create_toolcontext(0, NULL)))
+ return_NULL;
+
+ _cmdline.arg_props = &_arg_props[0];
+
+ if (stored_errno()) {
+ destroy_toolcontext(cmd);
+ return_NULL;
+ }
+
+ return cmd;
+}
+
+static void _fin_commands(void)
+{
+ int i;
+
+ for (i = 0; i < _cmdline.num_commands; i++)
+ dm_free(_cmdline.commands[i].valid_args);
+
+ dm_free(_cmdline.commands);
+
+ _cmdline.commands = NULL;
+ _cmdline.num_commands = 0;
+ _cmdline.commands_size = 0;
+}
+
+void lvm_fin(struct cmd_context *cmd)
+{
+ _fin_commands();
+ destroy_toolcontext(cmd);
+}
+
+static int _run_script(struct cmd_context *cmd, int argc, char **argv)
+{
+ FILE *script;
+
+ char buffer[CMD_LEN];
+ int ret = 0;
+ int magic_number = 0;
+ char *script_file = argv[0];
+
+ if ((script = fopen(script_file, "r")) == NULL)
+ return ENO_SUCH_CMD;
+
+ while (fgets(buffer, sizeof(buffer), script) != NULL) {
+ if (!magic_number) {
+ if (buffer[0] == '#' && buffer[1] == '!')
+ magic_number = 1;
+ else {
+ ret = ENO_SUCH_CMD;
+ break;
+ }
+ }
+ if ((strlen(buffer) == sizeof(buffer) - 1)
+ && (buffer[sizeof(buffer) - 1] - 2 != '\n')) {
+ buffer[50] = '\0';
+ log_error("Line too long (max 255) beginning: %s",
+ buffer);
+ ret = EINVALID_CMD_LINE;
+ break;
+ }
+ if (lvm_split(buffer, &argc, argv, MAX_ARGS) == MAX_ARGS) {
+ buffer[50] = '\0';
+ log_error("Too many arguments: %s", buffer);
+ ret = EINVALID_CMD_LINE;
+ break;
+ }
+ if (!argc)
+ continue;
+ if (!strcmp(argv[0], "quit") || !strcmp(argv[0], "exit"))
+ break;
+ ret = lvm_run_command(cmd, argc, argv);
+ if (ret != ECMD_PROCESSED) {
+ if (!error_message_produced()) {
+ log_debug(INTERNAL_ERROR "Failed command did not use log_error");
+ log_error("Command failed with status code %d.", ret);
+ }
+ break;
+ }
+ }
+
+ if (fclose(script))
+ log_sys_error("fclose", script_file);
+
+ return ret;
+}
+
+/*
+ * Determine whether we should fall back and exec the equivalent LVM1 tool
+ */
+static int _lvm1_fallback(struct cmd_context *cmd)
+{
+ char vsn[80];
+ int dm_present;
+
+ if (!find_config_tree_int(cmd, "global/fallback_to_lvm1",
+ DEFAULT_FALLBACK_TO_LVM1) ||
+ strncmp(cmd->kernel_vsn, "2.4.", 4))
+ return 0;
+
+ log_suppress(1);
+ dm_present = driver_version(vsn, sizeof(vsn));
+ log_suppress(0);
+
+ if (dm_present || !lvm1_present(cmd))
+ return 0;
+
+ return 1;
+}
+
+static void _exec_lvm1_command(char **argv)
+{
+ char path[PATH_MAX];
+
+ if (dm_snprintf(path, sizeof(path), "%s.lvm1", argv[0]) < 0) {
+ log_error("Failed to create LVM1 tool pathname");
+ return;
+ }
+
+ execvp(path, argv);
+ log_sys_error("execvp", path);
+}
+
+static void _nonroot_warning(void)
+{
+ if (getuid() || geteuid())
+ log_warn("WARNING: Running as a non-root user. Functionality may be unavailable.");
+}
+
+int lvm2_main(int argc, char **argv)
+{
+ const char *base;
+ int ret, alias = 0;
+ struct cmd_context *cmd;
+
+ base = last_path_component(argv[0]);
+ if (strcmp(base, "lvm") && strcmp(base, "lvm.static") &&
+ strcmp(base, "initrd-lvm"))
+ alias = 1;
+
+ _close_stray_fds(base);
+
+ if (is_static() && strcmp(base, "lvm.static") &&
+ path_exists(LVM_SHARED_PATH) &&
+ !getenv("LVM_DID_EXEC")) {
+ setenv("LVM_DID_EXEC", base, 1);
+ execvp(LVM_SHARED_PATH, argv);
+ unsetenv("LVM_DID_EXEC");
+ }
+
+ /* "version" command is simple enough so it doesn't need any complex init */
+ if (!alias && argc > 1 && !strcmp(argv[1], "version"))
+ return lvm_return_code(version(NULL, argc, argv));
+
+ if (!(cmd = init_lvm()))
+ return -1;
+
+ cmd->argv = argv;
+ lvm_register_commands();
+
+ if (_lvm1_fallback(cmd)) {
+ /* Attempt to run equivalent LVM1 tool instead */
+ if (!alias) {
+ argv++;
+ argc--;
+ }
+ if (!argc) {
+ log_error("Falling back to LVM1 tools, but no "
+ "command specified.");
+ ret = ECMD_FAILED;
+ goto out;
+ }
+ _exec_lvm1_command(argv);
+ ret = ECMD_FAILED;
+ goto out;
+ }
+#ifdef READLINE_SUPPORT
+ if (!alias && argc == 1) {
+ _nonroot_warning();
+ ret = lvm_shell(cmd, &_cmdline);
+ goto out;
+ }
+#endif
+
+ if (!alias) {
+ if (argc < 2) {
+ log_fatal("Please supply an LVM command.");
+ _display_help();
+ ret = EINVALID_CMD_LINE;
+ goto out;
+ }
+
+ argc--;
+ argv++;
+ }
+
+ _nonroot_warning();
+ ret = lvm_run_command(cmd, argc, argv);
+ if ((ret == ENO_SUCH_CMD) && (!alias))
+ ret = _run_script(cmd, argc, argv);
+ if (ret == ENO_SUCH_CMD)
+ log_error("No such command. Try 'help'.");
+
+ if ((ret != ECMD_PROCESSED) && !error_message_produced()) {
+ log_debug(INTERNAL_ERROR "Failed command did not use log_error");
+ log_error("Command failed with status code %d.", ret);
+ }
+
+ out:
+ lvm_fin(cmd);
+ return lvm_return_code(ret);
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Changelog
+ *
+ * 05/02/2002 - First drop [HM]
+ */
+
+#include "tools.h"
+
+int disks_found;
+int parts_found;
+int pv_disks_found;
+int pv_parts_found;
+int max_len;
+
+static int _get_max_dev_name_len(struct dev_filter *filter)
+{
+ int len = 0;
+ int maxlen = 0;
+ struct dev_iter *iter;
+ struct device *dev;
+
+ if (!(iter = dev_iter_create(filter, 1))) {
+ log_error("dev_iter_create failed");
+ return 0;
+ }
+
+ /* Do scan */
+ for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) {
+ len = strlen(dev_name(dev));
+ if (len > maxlen)
+ maxlen = len;
+ }
+ dev_iter_destroy(iter);
+
+ return maxlen;
+}
+
+static void _count(struct device *dev, int *disks, int *parts)
+{
+ int c = dev_name(dev)[strlen(dev_name(dev)) - 1];
+
+ if (!isdigit(c))
+ (*disks)++;
+ else
+ (*parts)++;
+}
+
+static void _print(struct cmd_context *cmd, const struct device *dev,
+ uint64_t size, const char *what)
+{
+ log_print("%-*s [%15s] %s", max_len, dev_name(dev),
+ display_size(cmd, size), what ? : "");
+}
+
+static int _check_device(struct cmd_context *cmd, struct device *dev)
+{
+ char buffer;
+ uint64_t size;
+
+ if (!dev_open(dev)) {
+ return 0;
+ }
+ if (!dev_read(dev, UINT64_C(0), (size_t) 1, &buffer)) {
+ dev_close(dev);
+ return 0;
+ }
+ if (!dev_get_size(dev, &size)) {
+ log_error("Couldn't get size of \"%s\"", dev_name(dev));
+ }
+ _print(cmd, dev, size, NULL);
+ _count(dev, &disks_found, &parts_found);
+ if (!dev_close(dev)) {
+ log_error("dev_close on \"%s\" failed", dev_name(dev));
+ return 0;
+ }
+ return 1;
+}
+
+int lvmdiskscan(struct cmd_context *cmd, int argc __attribute__((unused)),
+ char **argv __attribute__((unused)))
+{
+ uint64_t size;
+ struct dev_iter *iter;
+ struct device *dev;
+ struct label *label;
+
+ /* initialise these here to avoid problems with the lvm shell */
+ disks_found = 0;
+ parts_found = 0;
+ pv_disks_found = 0;
+ pv_parts_found = 0;
+
+ if (arg_count(cmd, lvmpartition_ARG))
+ log_warn("WARNING: only considering LVM devices");
+
+ max_len = _get_max_dev_name_len(cmd->filter);
+
+ if (!(iter = dev_iter_create(cmd->filter, 0))) {
+ log_error("dev_iter_create failed");
+ return ECMD_FAILED;
+ }
+
+ /* Do scan */
+ for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) {
+ /* Try if it is a PV first */
+ if ((label_read(dev, &label, UINT64_C(0)))) {
+ if (!dev_get_size(dev, &size)) {
+ log_error("Couldn't get size of \"%s\"",
+ dev_name(dev));
+ continue;
+ }
+ _print(cmd, dev, size, "LVM physical volume");
+ _count(dev, &pv_disks_found, &pv_parts_found);
+ continue;
+ }
+ /* If user just wants PVs we are done */
+ if (arg_count(cmd, lvmpartition_ARG))
+ continue;
+
+ /* What other device is it? */
+ if (!_check_device(cmd, dev))
+ continue;
+ }
+ dev_iter_destroy(iter);
+
+ /* Display totals */
+ if (!arg_count(cmd, lvmpartition_ARG)) {
+ log_print("%d disk%s",
+ disks_found, disks_found == 1 ? "" : "s");
+ log_print("%d partition%s",
+ parts_found, parts_found == 1 ? "" : "s");
+ }
+ log_print("%d LVM physical volume whole disk%s",
+ pv_disks_found, pv_disks_found == 1 ? "" : "s");
+ log_print("%d LVM physical volume%s",
+ pv_parts_found, pv_parts_found == 1 ? "" : "s");
+
+ return ECMD_PROCESSED;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+int lvreduce(struct cmd_context *cmd, int argc, char **argv)
+{
+ return lvresize(cmd, argc, argv);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv,
+ void *handle __attribute__((unused)))
+{
+ struct logical_volume *origin;
+
+ /*
+ * If this is a sparse device, remove its origin too.
+ */
+ if (lv_is_cow(lv) && lv_is_virtual_origin(origin = origin_from_cow(lv)))
+ lv = origin;
+
+ if (!lv_remove_with_dependencies(cmd, lv, arg_count(cmd, force_ARG), 0)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ return ECMD_PROCESSED;
+}
+
+int lvremove(struct cmd_context *cmd, int argc, char **argv)
+{
+ if (!argc) {
+ log_error("Please enter one or more logical volume paths");
+ return EINVALID_CMD_LINE;
+ }
+
+ cmd->handles_missing_pvs = 1;
+
+ return process_each_lv(cmd, argc, argv, READ_FOR_UPDATE, NULL,
+ &lvremove_single);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "lvm-types.h"
+
+
+/*
+ * lvrename command implementation.
+ * Check arguments and call lv_rename() to execute the request.
+ */
+int lvrename(struct cmd_context *cmd, int argc, char **argv)
+{
+ size_t maxlen;
+ char *lv_name_old, *lv_name_new;
+ const char *vg_name, *vg_name_new, *vg_name_old;
+ char *st;
+ int r = ECMD_FAILED;
+
+ struct volume_group *vg = NULL;
+ struct lv_list *lvl;
+
+ if (argc == 3) {
+ vg_name = skip_dev_dir(cmd, argv[0], NULL);
+ lv_name_old = argv[1];
+ lv_name_new = argv[2];
+ if (strchr(lv_name_old, '/') &&
+ (vg_name_old = extract_vgname(cmd, lv_name_old)) &&
+ strcmp(vg_name_old, vg_name)) {
+ log_error("Please use a single volume group name "
+ "(\"%s\" or \"%s\")", vg_name, vg_name_old);
+ return EINVALID_CMD_LINE;
+ }
+ } else if (argc == 2) {
+ lv_name_old = argv[0];
+ lv_name_new = argv[1];
+ vg_name = extract_vgname(cmd, lv_name_old);
+ } else {
+ log_error("Old and new logical volume names required");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (!validate_name(vg_name)) {
+ log_error("Please provide a valid volume group name");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (strchr(lv_name_new, '/') &&
+ (vg_name_new = extract_vgname(cmd, lv_name_new)) &&
+ strcmp(vg_name, vg_name_new)) {
+ log_error("Logical volume names must "
+ "have the same volume group (\"%s\" or \"%s\")",
+ vg_name, vg_name_new);
+ return EINVALID_CMD_LINE;
+ }
+
+ if ((st = strrchr(lv_name_old, '/')))
+ lv_name_old = st + 1;
+
+ if ((st = strrchr(lv_name_new, '/')))
+ lv_name_new = st + 1;
+
+ /* Check sanity of new name */
+ maxlen = NAME_LEN - strlen(vg_name) - strlen(cmd->dev_dir) - 3;
+ if (strlen(lv_name_new) > maxlen) {
+ log_error("New logical volume path exceeds maximum length "
+ "of %" PRIsize_t "!", maxlen);
+ return ECMD_FAILED;
+ }
+
+ if (!*lv_name_new) {
+ log_error("New logical volume name may not be blank");
+ return ECMD_FAILED;
+ }
+
+ if (!apply_lvname_restrictions(lv_name_new)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (!validate_name(lv_name_new)) {
+ log_error("New logical volume name \"%s\" is invalid",
+ lv_name_new);
+ return EINVALID_CMD_LINE;
+ }
+
+ if (!strcmp(lv_name_old, lv_name_new)) {
+ log_error("Old and new logical volume names must differ");
+ return EINVALID_CMD_LINE;
+ }
+
+ log_verbose("Checking for existing volume group \"%s\"", vg_name);
+ vg = vg_read_for_update(cmd, vg_name, NULL, 0);
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (!(lvl = find_lv_in_vg(vg, lv_name_old))) {
+ log_error("Existing logical volume \"%s\" not found in "
+ "volume group \"%s\"", lv_name_old, vg_name);
+ goto error;
+ }
+
+ if (!lv_rename(cmd, lvl->lv, lv_name_new))
+ goto error;
+
+ log_print("Renamed \"%s\" to \"%s\" in volume group \"%s\"",
+ lv_name_old, lv_name_new, vg_name);
+
+ r = ECMD_PROCESSED;
+error:
+ unlock_and_free_vg(cmd, vg, vg_name);
+ return r;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+#define SIZE_BUF 128
+
+struct lvresize_params {
+ const char *vg_name;
+ const char *lv_name;
+
+ uint32_t stripes;
+ uint32_t stripe_size;
+ uint32_t mirrors;
+
+ const struct segment_type *segtype;
+
+ /* size */
+ uint32_t extents;
+ uint64_t size;
+ sign_t sign;
+ percent_type_t percent;
+
+ enum {
+ LV_ANY = 0,
+ LV_REDUCE = 1,
+ LV_EXTEND = 2
+ } resize;
+
+ int resizefs;
+ int nofsck;
+
+ int argc;
+ char **argv;
+};
+
+static int _validate_stripesize(struct cmd_context *cmd,
+ const struct volume_group *vg,
+ struct lvresize_params *lp)
+{
+ if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) {
+ log_error("Stripesize may not be negative.");
+ return 0;
+ }
+
+ if (arg_uint_value(cmd, stripesize_ARG, 0) > STRIPE_SIZE_LIMIT * 2) {
+ log_error("Stripe size cannot be larger than %s",
+ display_size(cmd, (uint64_t) STRIPE_SIZE_LIMIT));
+ return 0;
+ }
+
+ if (!(vg->fid->fmt->features & FMT_SEGMENTS))
+ log_warn("Varied stripesize not supported. Ignoring.");
+ else if (arg_uint_value(cmd, stripesize_ARG, 0) > vg->extent_size * 2) {
+ log_error("Reducing stripe size %s to maximum, "
+ "physical extent size %s",
+ display_size(cmd,
+ (uint64_t) arg_uint_value(cmd, stripesize_ARG, 0)),
+ display_size(cmd, (uint64_t) vg->extent_size));
+ lp->stripe_size = vg->extent_size;
+ } else
+ lp->stripe_size = arg_uint_value(cmd, stripesize_ARG, 0);
+
+ if (lp->stripe_size & (lp->stripe_size - 1)) {
+ log_error("Stripe size must be power of 2");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _request_confirmation(struct cmd_context *cmd,
+ const struct volume_group *vg,
+ const struct logical_volume *lv,
+ const struct lvresize_params *lp)
+{
+ struct lvinfo info;
+
+ memset(&info, 0, sizeof(info));
+
+ if (!lv_info(cmd, lv, 0, &info, 1, 0) && driver_version(NULL, 0)) {
+ log_error("lv_info failed: aborting");
+ return 0;
+ }
+
+ if (lp->resizefs) {
+ if (!info.exists) {
+ log_error("Logical volume %s must be activated "
+ "before resizing filesystem", lp->lv_name);
+ return 0;
+ }
+ return 1;
+ }
+
+ if (!info.exists)
+ return 1;
+
+ log_warn("WARNING: Reducing active%s logical volume to %s",
+ info.open_count ? " and open" : "",
+ display_size(cmd, (uint64_t) lp->extents * vg->extent_size));
+
+ log_warn("THIS MAY DESTROY YOUR DATA (filesystem etc.)");
+
+ if (!arg_count(cmd, force_ARG)) {
+ if (yes_no_prompt("Do you really want to reduce %s? [y/n]: ",
+ lp->lv_name) == 'n') {
+ log_error("Logical volume %s NOT reduced", lp->lv_name);
+ return 0;
+ }
+ if (sigint_caught())
+ return 0;
+ }
+
+ return 1;
+}
+
+enum fsadm_cmd_e { FSADM_CMD_CHECK, FSADM_CMD_RESIZE };
+#define FSADM_CMD "fsadm"
+#define FSADM_CMD_MAX_ARGS 6
+#define FSADM_CHECK_FAILS_FOR_MOUNTED 3 /* shell exist status code */
+
+/*
+ * FSADM_CMD --dry-run --verbose --force check lv_path
+ * FSADM_CMD --dry-run --verbose --force resize lv_path size
+ */
+static int _fsadm_cmd(struct cmd_context *cmd,
+ const struct volume_group *vg,
+ const struct lvresize_params *lp,
+ enum fsadm_cmd_e fcmd,
+ int *status)
+{
+ char lv_path[PATH_MAX];
+ char size_buf[SIZE_BUF];
+ const char *argv[FSADM_CMD_MAX_ARGS + 2];
+ unsigned i = 0;
+
+ argv[i++] = FSADM_CMD;
+
+ if (test_mode())
+ argv[i++] = "--dry-run";
+
+ if (verbose_level() >= _LOG_NOTICE)
+ argv[i++] = "--verbose";
+
+ if (arg_count(cmd, force_ARG))
+ argv[i++] = "--force";
+
+ argv[i++] = (fcmd == FSADM_CMD_RESIZE) ? "resize" : "check";
+
+ if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", cmd->dev_dir, lp->vg_name,
+ lp->lv_name) < 0) {
+ log_error("Couldn't create LV path for %s", lp->lv_name);
+ return 0;
+ }
+
+ argv[i++] = lv_path;
+
+ if (fcmd == FSADM_CMD_RESIZE) {
+ if (dm_snprintf(size_buf, SIZE_BUF, "%" PRIu64 "K",
+ (uint64_t) lp->extents * vg->extent_size / 2) < 0) {
+ log_error("Couldn't generate new LV size string");
+ return 0;
+ }
+
+ argv[i++] = size_buf;
+ }
+
+ argv[i] = NULL;
+
+ return exec_cmd(cmd, argv, status);
+}
+
+static int _lvresize_params(struct cmd_context *cmd, int argc, char **argv,
+ struct lvresize_params *lp)
+{
+ const char *cmd_name;
+ char *st;
+ unsigned dev_dir_found = 0;
+ int use_policy = arg_count(cmd, use_policies_ARG);
+
+ lp->sign = SIGN_NONE;
+ lp->resize = LV_ANY;
+
+ cmd_name = command_name(cmd);
+ if (!strcmp(cmd_name, "lvreduce"))
+ lp->resize = LV_REDUCE;
+ if (!strcmp(cmd_name, "lvextend"))
+ lp->resize = LV_EXTEND;
+
+ if (use_policy) {
+ /* do nothing; _lvresize will handle --use-policies itself */
+ lp->extents = 0;
+ lp->sign = SIGN_PLUS;
+ lp->percent = PERCENT_LV;
+ } else {
+ /*
+ * Allow omission of extents and size if the user has given us
+ * one or more PVs. Most likely, the intent was "resize this
+ * LV the best you can with these PVs"
+ */
+ if ((arg_count(cmd, extents_ARG) + arg_count(cmd, size_ARG) == 0) &&
+ (argc >= 2)) {
+ lp->extents = 100;
+ lp->percent = PERCENT_PVS;
+ lp->sign = SIGN_PLUS;
+ } else if ((arg_count(cmd, extents_ARG) +
+ arg_count(cmd, size_ARG) != 1)) {
+ log_error("Please specify either size or extents but not "
+ "both.");
+ return 0;
+ }
+
+ if (arg_count(cmd, extents_ARG)) {
+ lp->extents = arg_uint_value(cmd, extents_ARG, 0);
+ lp->sign = arg_sign_value(cmd, extents_ARG, SIGN_NONE);
+ lp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE);
+ }
+
+ /* Size returned in kilobyte units; held in sectors */
+ if (arg_count(cmd, size_ARG)) {
+ lp->size = arg_uint64_value(cmd, size_ARG, 0);
+ lp->sign = arg_sign_value(cmd, size_ARG, SIGN_NONE);
+ lp->percent = PERCENT_NONE;
+ }
+ }
+
+ if (lp->resize == LV_EXTEND && lp->sign == SIGN_MINUS) {
+ log_error("Negative argument not permitted - use lvreduce");
+ return 0;
+ }
+
+ if (lp->resize == LV_REDUCE && lp->sign == SIGN_PLUS) {
+ log_error("Positive sign not permitted - use lvextend");
+ return 0;
+ }
+
+ lp->resizefs = arg_is_set(cmd, resizefs_ARG);
+ lp->nofsck = arg_is_set(cmd, nofsck_ARG);
+
+ if (!argc) {
+ log_error("Please provide the logical volume name");
+ return 0;
+ }
+
+ lp->lv_name = argv[0];
+ argv++;
+ argc--;
+
+ if (!(lp->lv_name = skip_dev_dir(cmd, lp->lv_name, &dev_dir_found)) ||
+ !(lp->vg_name = extract_vgname(cmd, lp->lv_name))) {
+ log_error("Please provide a volume group name");
+ return 0;
+ }
+
+ if (!validate_name(lp->vg_name)) {
+ log_error("Volume group name %s has invalid characters",
+ lp->vg_name);
+ return 0;
+ }
+
+ if ((st = strrchr(lp->lv_name, '/')))
+ lp->lv_name = st + 1;
+
+ lp->argc = argc;
+ lp->argv = argv;
+
+ return 1;
+}
+
+static int _adjust_policy_params(struct cmd_context *cmd,
+ struct logical_volume *lv, struct lvresize_params *lp)
+{
+ percent_t percent;
+ int policy_threshold, policy_amount;
+
+ policy_threshold =
+ find_config_tree_int(cmd, "activation/snapshot_autoextend_threshold",
+ DEFAULT_SNAPSHOT_AUTOEXTEND_THRESHOLD) * PERCENT_1;
+ policy_amount =
+ find_config_tree_int(cmd, "activation/snapshot_autoextend_percent",
+ DEFAULT_SNAPSHOT_AUTOEXTEND_PERCENT);
+
+ if (policy_threshold >= PERCENT_100)
+ return 1; /* nothing to do */
+
+ if (!lv_snapshot_percent(lv, &percent))
+ return_0;
+
+ if (!(PERCENT_0 < percent && percent < PERCENT_100) || percent <= policy_threshold)
+ return 1; /* nothing to do */
+
+ lp->extents = policy_amount;
+ return 1;
+}
+
+static int _lvresize(struct cmd_context *cmd, struct volume_group *vg,
+ struct lvresize_params *lp)
+{
+ struct logical_volume *lv;
+ struct lvinfo info;
+ uint32_t stripesize_extents = 0;
+ uint32_t seg_stripes = 0, seg_stripesize = 0, seg_size = 0;
+ uint32_t seg_mirrors = 0;
+ uint32_t extents_used = 0;
+ uint32_t size_rest;
+ uint32_t pv_extent_count = 0;
+ alloc_policy_t alloc;
+ struct logical_volume *lock_lv;
+ struct lv_list *lvl;
+ struct lv_segment *seg, *uninitialized_var(mirr_seg);
+ uint32_t seg_extents;
+ uint32_t sz, str;
+ int status;
+ struct dm_list *pvh = NULL;
+ int use_policy = arg_count(cmd, use_policies_ARG);
+
+ /* does LV exist? */
+ if (!(lvl = find_lv_in_vg(vg, lp->lv_name))) {
+ log_error("Logical volume %s not found in volume group %s",
+ lp->lv_name, lp->vg_name);
+ return ECMD_FAILED;
+ }
+
+ if (arg_count(cmd, stripes_ARG)) {
+ if (vg->fid->fmt->features & FMT_SEGMENTS)
+ lp->stripes = arg_uint_value(cmd, stripes_ARG, 1);
+ else
+ log_warn("Varied striping not supported. Ignoring.");
+ }
+
+ if (arg_count(cmd, mirrors_ARG)) {
+ if (vg->fid->fmt->features & FMT_SEGMENTS)
+ lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 1) + 1;
+ else
+ log_warn("Mirrors not supported. Ignoring.");
+ if (arg_sign_value(cmd, mirrors_ARG, 0) == SIGN_MINUS) {
+ log_error("Mirrors argument may not be negative");
+ return EINVALID_CMD_LINE;
+ }
+ }
+
+ if (arg_count(cmd, stripesize_ARG) &&
+ !_validate_stripesize(cmd, vg, lp))
+ return EINVALID_CMD_LINE;
+
+ lv = lvl->lv;
+
+ if (use_policy) {
+ if (!lv_is_cow(lv)) {
+ log_error("Can't use policy-based resize for non-snapshot volumes.");
+ return ECMD_FAILED;
+ }
+ _adjust_policy_params(cmd, lv, lp);
+ }
+
+ if (!lv_is_visible(lv)) {
+ log_error("Can't resize internal logical volume %s", lv->name);
+ return ECMD_FAILED;
+ }
+
+ if (lv->status & LOCKED) {
+ log_error("Can't resize locked LV %s", lv->name);
+ return ECMD_FAILED;
+ }
+
+ if (lv->status & CONVERTING) {
+ log_error("Can't resize %s while lvconvert in progress", lv->name);
+ return ECMD_FAILED;
+ }
+
+ alloc = arg_uint_value(cmd, alloc_ARG, lv->alloc);
+
+ if (lp->size) {
+ if (lp->size % vg->extent_size) {
+ if (lp->sign == SIGN_MINUS)
+ lp->size -= lp->size % vg->extent_size;
+ else
+ lp->size += vg->extent_size -
+ (lp->size % vg->extent_size);
+
+ log_print("Rounding up size to full physical extent %s",
+ display_size(cmd, (uint64_t) lp->size));
+ }
+
+ lp->extents = lp->size / vg->extent_size;
+ }
+
+ if (!(pvh = lp->argc ? create_pv_list(cmd->mem, vg, lp->argc,
+ lp->argv, 1) : &vg->pvs)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ switch(lp->percent) {
+ case PERCENT_VG:
+ lp->extents = lp->extents * vg->extent_count / 100;
+ break;
+ case PERCENT_FREE:
+ lp->extents = lp->extents * vg->free_count / 100;
+ break;
+ case PERCENT_LV:
+ lp->extents = lp->extents * lv->le_count / 100;
+ break;
+ case PERCENT_PVS:
+ if (lp->argc) {
+ pv_extent_count = pv_list_extents_free(pvh);
+ lp->extents = lp->extents * pv_extent_count / 100;
+ } else
+ lp->extents = lp->extents * vg->extent_count / 100;
+ break;
+ case PERCENT_ORIGIN:
+ if (!lv_is_cow(lv)) {
+ log_error("Specified LV does not have an origin LV.");
+ return EINVALID_CMD_LINE;
+ }
+ lp->extents = lp->extents * origin_from_cow(lv)->le_count / 100;
+ break;
+ case PERCENT_NONE:
+ break;
+ }
+
+ if (lp->sign == SIGN_PLUS)
+ lp->extents += lv->le_count;
+
+ if (lp->sign == SIGN_MINUS) {
+ if (lp->extents >= lv->le_count) {
+ log_error("Unable to reduce %s below 1 extent",
+ lp->lv_name);
+ return EINVALID_CMD_LINE;
+ }
+
+ lp->extents = lv->le_count - lp->extents;
+ }
+
+ if (!lp->extents) {
+ log_error("New size of 0 not permitted");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (lp->extents == lv->le_count) {
+ if (use_policy)
+ return ECMD_PROCESSED; /* Nothing to do. */
+ if (!lp->resizefs) {
+ log_error("New size (%d extents) matches existing size "
+ "(%d extents)", lp->extents, lv->le_count);
+ return EINVALID_CMD_LINE;
+ }
+ lp->resize = LV_EXTEND; /* lets pretend zero size extension */
+ }
+
+ seg_size = lp->extents - lv->le_count;
+
+ /* Use segment type of last segment */
+ dm_list_iterate_items(seg, &lv->segments) {
+ lp->segtype = seg->segtype;
+ }
+
+ /* FIXME Support LVs with mixed segment types */
+ if (lp->segtype != get_segtype_from_string(cmd, arg_str_value(cmd, type_ARG,
+ lp->segtype->name))) {
+ log_error("VolumeType does not match (%s)", lp->segtype->name);
+ return EINVALID_CMD_LINE;
+ }
+
+ /* If extending, find mirrors of last segment */
+ if ((lp->extents > lv->le_count)) {
+ dm_list_iterate_back_items(mirr_seg, &lv->segments) {
+ if (seg_is_mirrored(mirr_seg))
+ seg_mirrors = lv_mirror_count(mirr_seg->lv);
+ else
+ seg_mirrors = 0;
+ break;
+ }
+ if (!arg_count(cmd, mirrors_ARG) && seg_mirrors) {
+ log_print("Extending %" PRIu32 " mirror images.",
+ seg_mirrors);
+ lp->mirrors = seg_mirrors;
+ }
+ if ((arg_count(cmd, mirrors_ARG) || seg_mirrors) &&
+ (lp->mirrors != seg_mirrors)) {
+ log_error("Cannot vary number of mirrors in LV yet.");
+ return EINVALID_CMD_LINE;
+ }
+ }
+
+ /* If extending, find stripes, stripesize & size of last segment */
+ if ((lp->extents > lv->le_count) &&
+ !(lp->stripes == 1 || (lp->stripes > 1 && lp->stripe_size))) {
+ /* FIXME Don't assume mirror seg will always be AREA_LV */
+ dm_list_iterate_items(seg, seg_mirrors ? &seg_lv(mirr_seg, 0)->segments : &lv->segments) {
+ if (!seg_is_striped(seg))
+ continue;
+
+ sz = seg->stripe_size;
+ str = seg->area_count;
+
+ if ((seg_stripesize && seg_stripesize != sz &&
+ sz && !lp->stripe_size) ||
+ (seg_stripes && seg_stripes != str && !lp->stripes)) {
+ log_error("Please specify number of "
+ "stripes (-i) and stripesize (-I)");
+ return EINVALID_CMD_LINE;
+ }
+
+ seg_stripesize = sz;
+ seg_stripes = str;
+ }
+
+ if (!lp->stripes)
+ lp->stripes = seg_stripes;
+
+ if (!lp->stripe_size && lp->stripes > 1) {
+ if (seg_stripesize) {
+ log_print("Using stripesize of last segment %s",
+ display_size(cmd, (uint64_t) seg_stripesize));
+ lp->stripe_size = seg_stripesize;
+ } else {
+ lp->stripe_size =
+ find_config_tree_int(cmd,
+ "metadata/stripesize",
+ DEFAULT_STRIPESIZE) * 2;
+ log_print("Using default stripesize %s",
+ display_size(cmd, (uint64_t) lp->stripe_size));
+ }
+ }
+ }
+
+ /* If reducing, find stripes, stripesize & size of last segment */
+ if (lp->extents < lv->le_count) {
+ extents_used = 0;
+
+ if (lp->stripes || lp->stripe_size || lp->mirrors)
+ log_error("Ignoring stripes, stripesize and mirrors "
+ "arguments when reducing");
+
+ dm_list_iterate_items(seg, &lv->segments) {
+ seg_extents = seg->len;
+
+ if (seg_is_striped(seg)) {
+ seg_stripesize = seg->stripe_size;
+ seg_stripes = seg->area_count;
+ }
+
+ if (seg_is_mirrored(seg))
+ seg_mirrors = lv_mirror_count(seg->lv);
+ else
+ seg_mirrors = 0;
+
+ if (lp->extents <= extents_used + seg_extents)
+ break;
+
+ extents_used += seg_extents;
+ }
+
+ seg_size = lp->extents - extents_used;
+ lp->stripe_size = seg_stripesize;
+ lp->stripes = seg_stripes;
+ lp->mirrors = seg_mirrors;
+ }
+
+ if (lp->stripes > 1 && !lp->stripe_size) {
+ log_error("Stripesize for striped segment should not be 0!");
+ return EINVALID_CMD_LINE;
+ }
+
+ if ((lp->stripes > 1)) {
+ if (!(stripesize_extents = lp->stripe_size / vg->extent_size))
+ stripesize_extents = 1;
+
+ if ((size_rest = seg_size % (lp->stripes * stripesize_extents))) {
+ log_print("Rounding size (%d extents) down to stripe "
+ "boundary size for segment (%d extents)",
+ lp->extents, lp->extents - size_rest);
+ lp->extents = lp->extents - size_rest;
+ }
+
+ if (lp->stripe_size < STRIPE_SIZE_MIN) {
+ log_error("Invalid stripe size %s",
+ display_size(cmd, (uint64_t) lp->stripe_size));
+ return EINVALID_CMD_LINE;
+ }
+ }
+
+ if (lp->extents < lv->le_count) {
+ if (lp->resize == LV_EXTEND) {
+ log_error("New size given (%d extents) not larger "
+ "than existing size (%d extents)",
+ lp->extents, lv->le_count);
+ return EINVALID_CMD_LINE;
+ }
+ lp->resize = LV_REDUCE;
+ } else if (lp->extents > lv->le_count) {
+ if (lp->resize == LV_REDUCE) {
+ log_error("New size given (%d extents) not less than "
+ "existing size (%d extents)", lp->extents,
+ lv->le_count);
+ return EINVALID_CMD_LINE;
+ }
+ lp->resize = LV_EXTEND;
+ }
+
+ if (lv_is_origin(lv)) {
+ if (lp->resize == LV_REDUCE) {
+ log_error("Snapshot origin volumes cannot be reduced "
+ "in size yet.");
+ return ECMD_FAILED;
+ }
+
+ memset(&info, 0, sizeof(info));
+
+ if (lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) {
+ log_error("Snapshot origin volumes can be resized "
+ "only while inactive: try lvchange -an");
+ return ECMD_FAILED;
+ }
+ }
+
+ if ((lp->resize == LV_REDUCE) && lp->argc)
+ log_warn("Ignoring PVs on command line when reducing");
+
+ /* Request confirmation before operations that are often mistakes. */
+ if ((lp->resizefs || (lp->resize == LV_REDUCE)) &&
+ !_request_confirmation(cmd, vg, lv, lp)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (lp->resizefs) {
+ if (!lp->nofsck &&
+ !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_CHECK, &status)) {
+ if (status != FSADM_CHECK_FAILS_FOR_MOUNTED) {
+ stack;
+ return ECMD_FAILED;
+ }
+ /* some filesystems supports online resize */
+ }
+
+ if ((lp->resize == LV_REDUCE) &&
+ !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE, NULL)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ }
+
+ if (!archive(vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ log_print("%sing logical volume %s to %s",
+ (lp->resize == LV_REDUCE) ? "Reduc" : "Extend",
+ lp->lv_name,
+ display_size(cmd, (uint64_t) lp->extents * vg->extent_size));
+
+ if (lp->resize == LV_REDUCE) {
+ if (!lv_reduce(lv, lv->le_count - lp->extents)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ } else if ((lp->extents > lv->le_count) && /* Ensure we extend */
+ !lv_extend(lv, lp->segtype, lp->stripes,
+ lp->stripe_size, lp->mirrors,
+ lp->extents - lv->le_count,
+ NULL, 0u, 0u, pvh, alloc)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ /* store vg on disk(s) */
+ if (!vg_write(vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ /* If snapshot, must suspend all associated devices */
+ if (lv_is_cow(lv))
+ lock_lv = origin_from_cow(lv);
+ else
+ lock_lv = lv;
+
+ if (!suspend_lv(cmd, lock_lv)) {
+ log_error("Failed to suspend %s", lp->lv_name);
+ vg_revert(vg);
+ backup(vg);
+ return ECMD_FAILED;
+ }
+
+ if (!vg_commit(vg)) {
+ stack;
+ if (!resume_lv(cmd, lock_lv))
+ stack;
+ backup(vg);
+ return ECMD_FAILED;
+ }
+
+ if (!resume_lv(cmd, lock_lv)) {
+ log_error("Problem reactivating %s", lp->lv_name);
+ backup(vg);
+ return ECMD_FAILED;
+ }
+
+ backup(vg);
+
+ log_print("Logical volume %s successfully resized", lp->lv_name);
+
+ if (lp->resizefs && (lp->resize == LV_EXTEND) &&
+ !_fsadm_cmd(cmd, vg, lp, FSADM_CMD_RESIZE, NULL)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ return ECMD_PROCESSED;
+}
+
+int lvresize(struct cmd_context *cmd, int argc, char **argv)
+{
+ struct lvresize_params lp;
+ struct volume_group *vg;
+ int r;
+
+ memset(&lp, 0, sizeof(lp));
+
+ if (!_lvresize_params(cmd, argc, argv, &lp))
+ return EINVALID_CMD_LINE;
+
+ log_verbose("Finding volume group %s", lp.vg_name);
+ vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0);
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (!(r = _lvresize(cmd, vg, &lp)))
+ stack;
+
+ unlock_and_free_vg(cmd, vg, lp.vg_name);
+
+ return r;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static int lvscan_single(struct cmd_context *cmd, struct logical_volume *lv,
+ void *handle __attribute__((unused)))
+{
+ struct lvinfo info;
+ int lv_total = 0;
+ uint64_t lv_capacity_total = 0;
+ int inkernel, snap_active = 1;
+ struct lv_segment *snap_seg = NULL;
+ percent_t snap_percent; /* fused, fsize; */
+
+ const char *active_str, *snapshot_str;
+
+ if (!arg_count(cmd, all_ARG) && !lv_is_visible(lv))
+ return ECMD_PROCESSED;
+
+ inkernel = lv_info(cmd, lv, 0, &info, 1, 0) && info.exists;
+ if (lv_is_origin(lv)) {
+ dm_list_iterate_items_gen(snap_seg, &lv->snapshot_segs,
+ origin_list) {
+ if (inkernel &&
+ (snap_active = lv_snapshot_percent(snap_seg->cow,
+ &snap_percent)))
+ if (snap_percent == PERCENT_INVALID)
+ snap_active = 0;
+ }
+ snap_seg = NULL;
+ } else if (lv_is_cow(lv)) {
+ if (inkernel &&
+ (snap_active = lv_snapshot_percent(lv, &snap_percent)))
+ if (snap_percent == PERCENT_INVALID)
+ snap_active = 0;
+ }
+
+/* FIXME Add -D arg to skip this! */
+ if (inkernel && snap_active)
+ active_str = "ACTIVE ";
+ else
+ active_str = "inactive ";
+
+ if (lv_is_origin(lv))
+ snapshot_str = "Original";
+ else if (lv_is_cow(lv))
+ snapshot_str = "Snapshot";
+ else
+ snapshot_str = " ";
+
+ log_print("%s%s '%s%s/%s' [%s] %s", active_str, snapshot_str,
+ cmd->dev_dir, lv->vg->name, lv->name,
+ display_size(cmd, lv->size),
+ get_alloc_string(lv->alloc));
+
+ lv_total++;
+
+ lv_capacity_total += lv->size;
+
+ return ECMD_PROCESSED;
+}
+
+int lvscan(struct cmd_context *cmd, int argc, char **argv)
+{
+ if (argc) {
+ log_error("No additional command line arguments allowed");
+ return EINVALID_CMD_LINE;
+ }
+
+ return process_each_lv(cmd, argc, argv, 0, NULL,
+ &lvscan_single);
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "polldaemon.h"
+#include "lvm2cmdline.h"
+#include <signal.h>
+#include <sys/wait.h>
+
+static void _sigchld_handler(int sig __attribute__((unused)))
+{
+ while (wait4(-1, NULL, WNOHANG | WUNTRACED, NULL) > 0) ;
+}
+
+/*
+ * returns:
+ * -1 if the fork failed
+ * 0 if the parent
+ * 1 if the child
+ */
+static int _become_daemon(struct cmd_context *cmd)
+{
+ pid_t pid;
+ struct sigaction act = {
+ {_sigchld_handler},
+ .sa_flags = SA_NOCLDSTOP,
+ };
+
+ log_verbose("Forking background process");
+
+ sigaction(SIGCHLD, &act, NULL);
+
+ if ((pid = fork()) == -1) {
+ log_error("fork failed: %s", strerror(errno));
+ return -1;
+ }
+
+ /* Parent */
+ if (pid > 0)
+ return 0;
+
+ /* Child */
+ if (setsid() == -1)
+ log_error("Background process failed to setsid: %s",
+ strerror(errno));
+ init_verbose(VERBOSE_BASE_LEVEL);
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ strncpy(*cmd->argv, "(lvm2)", strlen(*cmd->argv));
+
+ reset_locking();
+ lvmcache_init();
+ dev_close_all();
+
+ return 1;
+}
+
+progress_t poll_mirror_progress(struct cmd_context *cmd,
+ struct logical_volume *lv, const char *name,
+ struct daemon_parms *parms)
+{
+ percent_t segment_percent = PERCENT_0, overall_percent = PERCENT_0;
+ uint32_t event_nr = 0;
+
+ if (!lv_is_mirrored(lv) ||
+ !lv_mirror_percent(cmd, lv, !parms->interval, &segment_percent,
+ &event_nr) ||
+ (segment_percent == PERCENT_INVALID)) {
+ log_error("ABORTING: Mirror percentage check failed.");
+ return PROGRESS_CHECK_FAILED;
+ }
+
+ overall_percent = copy_percent(lv);
+ if (parms->progress_display)
+ log_print("%s: %s: %.1f%%", name, parms->progress_title,
+ percent_to_float(overall_percent));
+ else
+ log_verbose("%s: %s: %.1f%%", name, parms->progress_title,
+ percent_to_float(overall_percent));
+
+ if (segment_percent != PERCENT_100)
+ return PROGRESS_UNFINISHED;
+
+ if (overall_percent == PERCENT_100)
+ return PROGRESS_FINISHED_ALL;
+
+ return PROGRESS_FINISHED_SEGMENT;
+}
+
+static int _check_lv_status(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct logical_volume *lv,
+ const char *name, struct daemon_parms *parms,
+ int *finished)
+{
+ struct dm_list *lvs_changed;
+ progress_t progress;
+
+ /* By default, caller should not retry */
+ *finished = 1;
+
+ if (parms->aborting) {
+ if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) {
+ log_error("Failed to generate list of copied LVs: "
+ "can't abort.");
+ return 0;
+ }
+ if (!parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed))
+ return_0;
+
+ return 1;
+ }
+
+ progress = parms->poll_fns->poll_progress(cmd, lv, name, parms);
+ if (progress == PROGRESS_CHECK_FAILED)
+ return_0;
+
+ if (progress == PROGRESS_UNFINISHED) {
+ /* The only case the caller *should* try again later */
+ *finished = 0;
+ return 1;
+ }
+
+ if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) {
+ log_error("ABORTING: Failed to generate list of copied LVs");
+ return 0;
+ }
+
+ /* Finished? Or progress to next segment? */
+ if (progress == PROGRESS_FINISHED_ALL) {
+ if (!parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed))
+ return 0;
+ } else {
+ if (parms->poll_fns->update_metadata &&
+ !parms->poll_fns->update_metadata(cmd, vg, lv, lvs_changed, 0)) {
+ log_error("ABORTING: Segment progression failed.");
+ parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed);
+ return 0;
+ }
+ *finished = 0; /* Another segment */
+ }
+
+ return 1;
+}
+
+static void _sleep_and_rescan_devices(struct daemon_parms *parms)
+{
+ /* FIXME Use alarm for regular intervals instead */
+ if (parms->interval && !parms->aborting) {
+ sleep(parms->interval);
+ /* Devices might have changed while we slept */
+ init_full_scan_done(0);
+ }
+}
+
+static int _wait_for_single_lv(struct cmd_context *cmd, const char *name, const char *uuid,
+ struct daemon_parms *parms)
+{
+ struct volume_group *vg;
+ struct logical_volume *lv;
+ int finished = 0;
+
+ /* Poll for completion */
+ while (!finished) {
+ if (parms->wait_before_testing)
+ _sleep_and_rescan_devices(parms);
+
+ /* Locks the (possibly renamed) VG again */
+ vg = parms->poll_fns->get_copy_vg(cmd, name, uuid);
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ log_error("ABORTING: Can't reread VG for %s", name);
+ /* What more could we do here? */
+ return 0;
+ }
+
+ if (!(lv = parms->poll_fns->get_copy_lv(cmd, vg, name, uuid,
+ parms->lv_type))) {
+ log_error("ABORTING: Can't find LV in %s for %s",
+ vg->name, name);
+ unlock_and_free_vg(cmd, vg, vg->name);
+ return 0;
+ }
+
+ if (!_check_lv_status(cmd, vg, lv, name, parms, &finished)) {
+ unlock_and_free_vg(cmd, vg, vg->name);
+ return 0;
+ }
+
+ unlock_and_free_vg(cmd, vg, vg->name);
+
+ /*
+ * FIXME Sleeping after testing, while preferred, also works around
+ * unreliable "finished" state checking in _percent_run. If the
+ * above _check_lv_status is deferred until after the first sleep it
+ * may be that a polldaemon will run without ever completing.
+ *
+ * This happens when one snapshot-merge polldaemon is racing with
+ * another (polling the same LV). The first to see the LV status
+ * reach the "finished" state will alter the LV that the other
+ * polldaemon(s) are polling. These other polldaemon(s) can then
+ * continue polling an LV that doesn't have a "status".
+ */
+ if (!parms->wait_before_testing)
+ _sleep_and_rescan_devices(parms);
+ }
+
+ return 1;
+}
+
+static int _poll_vg(struct cmd_context *cmd, const char *vgname,
+ struct volume_group *vg, void *handle)
+{
+ struct daemon_parms *parms = (struct daemon_parms *) handle;
+ struct lv_list *lvl;
+ struct logical_volume *lv;
+ const char *name;
+ int finished;
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ lv = lvl->lv;
+ if (!(lv->status & parms->lv_type))
+ continue;
+ name = parms->poll_fns->get_copy_name_from_lv(lv);
+ if (!name && !parms->aborting)
+ continue;
+
+ /* FIXME Need to do the activation from _set_up_pvmove here
+ * if it's not running and we're not aborting */
+ if (_check_lv_status(cmd, vg, lv, name, parms, &finished) &&
+ !finished)
+ parms->outstanding_count++;
+ }
+
+ return ECMD_PROCESSED;
+
+}
+
+static void _poll_for_all_vgs(struct cmd_context *cmd,
+ struct daemon_parms *parms)
+{
+ while (1) {
+ parms->outstanding_count = 0;
+ process_each_vg(cmd, 0, NULL, READ_FOR_UPDATE, parms, _poll_vg);
+ if (!parms->outstanding_count)
+ break;
+ sleep(parms->interval);
+ }
+}
+
+/*
+ * Only allow *one* return from poll_daemon() (the parent).
+ * If there is a child it must exit (ignoring the memory leak messages).
+ * - 'background' is advisory so a child polldaemon may not be used even
+ * if it was requested.
+ */
+int poll_daemon(struct cmd_context *cmd, const char *name, const char *uuid,
+ unsigned background,
+ uint32_t lv_type, struct poll_functions *poll_fns,
+ const char *progress_title)
+{
+ struct daemon_parms parms;
+ int daemon_mode = 0;
+ int ret = ECMD_PROCESSED;
+ sign_t interval_sign;
+
+ parms.aborting = arg_is_set(cmd, abort_ARG);
+ parms.background = background;
+ interval_sign = arg_sign_value(cmd, interval_ARG, 0);
+ if (interval_sign == SIGN_MINUS)
+ log_error("Argument to --interval cannot be negative");
+ parms.interval = arg_uint_value(cmd, interval_ARG,
+ find_config_tree_int(cmd, "activation/polling_interval",
+ DEFAULT_INTERVAL));
+ parms.wait_before_testing = (interval_sign == SIGN_PLUS);
+ parms.progress_display = 1;
+ parms.progress_title = progress_title;
+ parms.lv_type = lv_type;
+ parms.poll_fns = poll_fns;
+
+ if (parms.interval && !parms.aborting)
+ log_verbose("Checking progress %s waiting every %u seconds",
+ (parms.wait_before_testing ? "after" : "before"),
+ parms.interval);
+
+ if (!parms.interval) {
+ parms.progress_display = 0;
+
+ /* FIXME Disabled multiple-copy wait_event */
+ if (!name)
+ parms.interval = find_config_tree_int(cmd, "activation/polling_interval",
+ DEFAULT_INTERVAL);
+ }
+
+ if (parms.background) {
+ daemon_mode = _become_daemon(cmd);
+ if (daemon_mode == 0)
+ return ECMD_PROCESSED; /* Parent */
+ else if (daemon_mode == 1)
+ parms.progress_display = 0; /* Child */
+ /* FIXME Use wait_event (i.e. interval = 0) and */
+ /* fork one daemon per copy? */
+ }
+
+ /*
+ * Process one specific task or all incomplete tasks?
+ */
+ if (name) {
+ if (!_wait_for_single_lv(cmd, name, uuid, &parms)) {
+ stack;
+ ret = ECMD_FAILED;
+ }
+ } else
+ _poll_for_all_vgs(cmd, &parms);
+
+ if (parms.background && daemon_mode == 1) {
+ /*
+ * child was successfully forked:
+ * background polldaemon must not return to the caller
+ * because it will redundantly continue performing the
+ * caller's task (that the parent already performed)
+ */
+ /* FIXME Attempt proper cleanup */
+ _exit(lvm_return_code(ret));
+ }
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_TOOL_POLLDAEMON_H
+#define _LVM_TOOL_POLLDAEMON_H
+
+#include "metadata-exported.h"
+
+typedef enum {
+ PROGRESS_CHECK_FAILED = 0,
+ PROGRESS_UNFINISHED = 1,
+ PROGRESS_FINISHED_SEGMENT = 2,
+ PROGRESS_FINISHED_ALL = 3
+} progress_t;
+
+struct daemon_parms;
+
+struct poll_functions {
+ const char *(*get_copy_name_from_lv) (struct logical_volume *lv);
+ struct volume_group *(*get_copy_vg) (struct cmd_context *cmd,
+ const char *name,
+ const char *uuid);
+ struct logical_volume *(*get_copy_lv) (struct cmd_context *cmd,
+ struct volume_group *vg,
+ const char *name,
+ const char *uuid,
+ uint32_t lv_type);
+ progress_t (*poll_progress)(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ const char *name,
+ struct daemon_parms *parms);
+ int (*update_metadata) (struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct logical_volume *lv,
+ struct dm_list *lvs_changed, unsigned flags);
+ int (*finish_copy) (struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct logical_volume *lv,
+ struct dm_list *lvs_changed);
+};
+
+struct daemon_parms {
+ unsigned interval;
+ unsigned wait_before_testing;
+ unsigned aborting;
+ unsigned background;
+ unsigned outstanding_count;
+ unsigned progress_display;
+ const char *progress_title;
+ uint32_t lv_type;
+ struct poll_functions *poll_fns;
+};
+
+int poll_daemon(struct cmd_context *cmd, const char *name, const char *uuid,
+ unsigned background,
+ uint32_t lv_type, struct poll_functions *poll_fns,
+ const char *progress_title);
+
+progress_t poll_mirror_progress(struct cmd_context *cmd,
+ struct logical_volume *lv, const char *name,
+ struct daemon_parms *parms);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+/* FIXME Locking. PVs in VG. */
+
+static int _pvchange_single(struct cmd_context *cmd, struct volume_group *vg,
+ struct physical_volume *pv,
+ void *handle __attribute__((unused)))
+{
+ uint32_t orig_pe_alloc_count;
+ /* FIXME Next three only required for format1. */
+ uint32_t orig_pe_count, orig_pe_size;
+ uint64_t orig_pe_start;
+
+ const char *pv_name = pv_dev_name(pv);
+ const char *tag = NULL;
+ const char *orig_vg_name;
+ char uuid[64] __attribute__((aligned(8)));
+
+ int allocatable = 0;
+ int tagarg = 0;
+ int r = 0;
+ int mda_ignore = 0;
+
+ struct arg_value_group_list *current_group;
+
+ if (arg_count(cmd, addtag_ARG))
+ tagarg = addtag_ARG;
+ else if (arg_count(cmd, deltag_ARG))
+ tagarg = deltag_ARG;
+
+ if (arg_count(cmd, allocatable_ARG))
+ allocatable = !strcmp(arg_str_value(cmd, allocatable_ARG, "n"),
+ "y");
+ if (arg_count(cmd, metadataignore_ARG))
+ mda_ignore = !strcmp(arg_str_value(cmd, metadataignore_ARG, "n"),
+ "y");
+
+ /* If in a VG, must change using volume group. */
+ if (!is_orphan(pv)) {
+ if (tagarg && !(vg->fid->fmt->features & FMT_TAGS)) {
+ log_error("Volume group containing %s does not "
+ "support tags", pv_name);
+ goto out;
+ }
+ if (arg_count(cmd, uuid_ARG) && lvs_in_vg_activated(vg)) {
+ log_error("Volume group containing %s has active "
+ "logical volumes", pv_name);
+ goto out;
+ }
+ if (!archive(vg))
+ goto out;
+ } else {
+ if (tagarg) {
+ log_error("Can't change tag on Physical Volume %s not "
+ "in volume group", pv_name);
+ return 0;
+ }
+ }
+
+ if (arg_count(cmd, allocatable_ARG)) {
+ if (is_orphan(pv) &&
+ !(pv->fmt->features & FMT_ORPHAN_ALLOCATABLE)) {
+ log_error("Allocatability not supported by orphan "
+ "%s format PV %s", pv->fmt->name, pv_name);
+ goto out;
+ }
+
+ /* change allocatability for a PV */
+ if (allocatable && (pv_status(pv) & ALLOCATABLE_PV)) {
+ log_error("Physical volume \"%s\" is already "
+ "allocatable", pv_name);
+ r = 1;
+ goto out;
+ }
+
+ if (!allocatable && !(pv_status(pv) & ALLOCATABLE_PV)) {
+ log_error("Physical volume \"%s\" is already "
+ "unallocatable", pv_name);
+ r = 1;
+ goto out;
+ }
+
+ if (allocatable) {
+ log_verbose("Setting physical volume \"%s\" "
+ "allocatable", pv_name);
+ pv->status |= ALLOCATABLE_PV;
+ } else {
+ log_verbose("Setting physical volume \"%s\" NOT "
+ "allocatable", pv_name);
+ pv->status &= ~ALLOCATABLE_PV;
+ }
+ } else if (tagarg) {
+ /* tag or deltag */
+
+ dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
+ if (!grouped_arg_is_set(current_group->arg_values, tagarg))
+ continue;
+
+ if (!(tag = grouped_arg_str_value(current_group->arg_values, tagarg, NULL))) {
+ log_error("Failed to get tag");
+ goto out;
+ }
+
+ if ((tagarg == addtag_ARG)) {
+ if (!str_list_add(cmd->mem, &pv->tags, tag)) {
+ log_error("Failed to add tag %s to physical "
+ "volume %s", tag, pv_name);
+ goto out;
+ }
+ } else if (!str_list_del(&pv->tags, tag)) {
+ log_error("Failed to remove tag %s from "
+ "physical volume" "%s", tag, pv_name);
+ goto out;
+ }
+ }
+ } else if (arg_count(cmd, metadataignore_ARG)) {
+ if ((vg_mda_copies(vg) != VGMETADATACOPIES_UNMANAGED) &&
+ (arg_count(cmd, force_ARG) == PROMPT) &&
+ yes_no_prompt("Override preferred number of copies "
+ "of VG %s metadata? [y/n]: ",
+ pv_vg_name(pv)) == 'n') {
+ log_error("Physical volume %s not changed", pv_name);
+ goto out;
+ }
+ if (!pv_change_metadataignore(pv, mda_ignore))
+ goto out;
+ } else {
+ /* --uuid: Change PV ID randomly */
+ if (!id_create(&pv->id)) {
+ log_error("Failed to generate new random UUID for %s.",
+ pv_name);
+ goto out;
+ }
+ if (!id_write_format(&pv->id, uuid, sizeof(uuid)))
+ goto_out;
+ log_verbose("Changing uuid of %s to %s.", pv_name, uuid);
+ if (!is_orphan(pv)) {
+ orig_vg_name = pv_vg_name(pv);
+ orig_pe_alloc_count = pv_pe_alloc_count(pv);
+
+ /* FIXME format1 pv_write doesn't preserve these. */
+ orig_pe_size = pv_pe_size(pv);
+ orig_pe_start = pv_pe_start(pv);
+ orig_pe_count = pv_pe_count(pv);
+
+ pv->vg_name = pv->fmt->orphan_vg_name;
+ pv->pe_alloc_count = 0;
+ if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) {
+ log_error("pv_write with new uuid failed "
+ "for %s.", pv_name);
+ goto out;
+ }
+ pv->vg_name = orig_vg_name;
+ pv->pe_alloc_count = orig_pe_alloc_count;
+
+ pv->pe_size = orig_pe_size;
+ pv->pe_start = orig_pe_start;
+ pv->pe_count = orig_pe_count;
+ }
+ }
+
+ log_verbose("Updating physical volume \"%s\"", pv_name);
+ if (!is_orphan(pv)) {
+ if (!vg_write(vg) || !vg_commit(vg)) {
+ log_error("Failed to store physical volume \"%s\" in "
+ "volume group \"%s\"", pv_name, vg->name);
+ goto out;
+ }
+ backup(vg);
+ } else if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) {
+ log_error("Failed to store physical volume \"%s\"",
+ pv_name);
+ goto out;
+ }
+
+ log_print("Physical volume \"%s\" changed", pv_name);
+ r = 1;
+out:
+ return r;
+
+}
+
+int pvchange(struct cmd_context *cmd, int argc, char **argv)
+{
+ int opt = 0;
+ int done = 0;
+ int total = 0;
+
+ struct volume_group *vg;
+ const char *vg_name;
+ char *pv_name;
+
+ struct pv_list *pvl;
+ struct dm_list *vgnames;
+ struct str_list *sll;
+
+ if (arg_count(cmd, allocatable_ARG) + arg_is_set(cmd, addtag_ARG) +
+ arg_is_set(cmd, deltag_ARG) + arg_count(cmd, uuid_ARG) +
+ arg_count(cmd, metadataignore_ARG) != 1) {
+ log_error("Please give exactly one option of -x, -uuid, "
+ "--addtag or --deltag");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (!(arg_count(cmd, all_ARG)) && !argc) {
+ log_error("Please give a physical volume path");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, all_ARG) && argc) {
+ log_error("Option a and PhysicalVolumePath are exclusive");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (argc) {
+ log_verbose("Using physical volume(s) on command line");
+ for (; opt < argc; opt++) {
+ pv_name = argv[opt];
+ unescape_colons_and_at_signs(pv_name, NULL, NULL);
+ vg_name = find_vgname_from_pvname(cmd, pv_name);
+ if (!vg_name) {
+ log_error("Failed to read physical volume %s",
+ pv_name);
+ continue;
+ }
+ vg = vg_read_for_update(cmd, vg_name, NULL, 0);
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ stack;
+ continue;
+ }
+ pvl = find_pv_in_vg(vg, pv_name);
+ if (!pvl || !pvl->pv) {
+ log_error("Unable to find %s in %s",
+ pv_name, vg_name);
+ continue;
+ }
+
+ total++;
+ done += _pvchange_single(cmd, vg,
+ pvl->pv, NULL);
+ unlock_and_free_vg(cmd, vg, vg_name);
+ }
+ } else {
+ log_verbose("Scanning for physical volume names");
+ /* FIXME: share code with toollib */
+ /*
+ * Take the global lock here so the lvmcache remains
+ * consistent across orphan/non-orphan vg locks. If we don't
+ * take the lock here, pvs with 0 mdas in a non-orphan VG will
+ * be processed twice.
+ */
+ if (!lock_vol(cmd, VG_GLOBAL, LCK_VG_WRITE)) {
+ log_error("Unable to obtain global lock.");
+ return ECMD_FAILED;
+ }
+
+ if ((vgnames = get_vgnames(cmd, 1)) &&
+ !dm_list_empty(vgnames)) {
+ dm_list_iterate_items(sll, vgnames) {
+ vg = vg_read_for_update(cmd, sll->str, NULL, 0);
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ stack;
+ continue;
+ }
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ total++;
+ done += _pvchange_single(cmd, vg,
+ pvl->pv,
+ NULL);
+ }
+ unlock_and_free_vg(cmd, vg, sll->str);
+ }
+ }
+ }
+
+ unlock_vg(cmd, VG_GLOBAL);
+ log_print("%d physical volume%s changed / %d physical volume%s "
+ "not changed",
+ done, done == 1 ? "" : "s",
+ total - done, (total - done) == 1 ? "" : "s");
+
+ return (total == done) ? ECMD_PROCESSED : ECMD_FAILED;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+int pvck(struct cmd_context *cmd, int argc, char **argv)
+{
+ int i;
+
+ /* FIXME: validate cmdline options */
+ /* FIXME: what does the cmdline look like? */
+ /*
+ * Use what's on the cmdline directly, and avoid calling into
+ * some of the other infrastructure functions, so as to avoid
+ * hitting some of the lvmcache behavior, scanning other devices,
+ * etc.
+ */
+ for (i = 0; i < argc; i++) {
+ /* FIXME: warning and/or check if in use? */
+ log_verbose("Scanning %s", argv[i]);
+
+ unescape_colons_and_at_signs(argv[i], NULL, NULL);
+ pv_analyze(cmd, argv[i],
+ arg_uint64_value(cmd, labelsector_ARG,
+ UINT64_C(0)));
+ }
+
+ return ECMD_PROCESSED;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "metadata-exported.h"
+
+/*
+ * Intial sanity checking of recovery-related command-line arguments.
+ * These args are: --restorefile, --uuid, and --physicalvolumesize
+ *
+ * Output arguments:
+ * pp: structure allocated by caller, fields written / validated here
+ */
+static int pvcreate_restore_params_validate(struct cmd_context *cmd,
+ int argc, char **argv,
+ struct pvcreate_params *pp)
+{
+ const char *uuid = NULL;
+ struct volume_group *vg;
+ struct pv_list *existing_pvl;
+
+ if (arg_count(cmd, restorefile_ARG) && !arg_count(cmd, uuidstr_ARG)) {
+ log_error("--uuid is required with --restorefile");
+ return 0;
+ }
+
+ if (!arg_count(cmd, restorefile_ARG) && arg_count(cmd, uuidstr_ARG)) {
+ if (!arg_count(cmd, norestorefile_ARG) &&
+ find_config_tree_bool(cmd,
+ "devices/require_restorefile_with_uuid",
+ DEFAULT_REQUIRE_RESTOREFILE_WITH_UUID)) {
+ log_error("--restorefile is required with --uuid");
+ return 0;
+ }
+ }
+
+ if (arg_count(cmd, uuidstr_ARG) && argc != 1) {
+ log_error("Can only set uuid on one volume at once");
+ return 0;
+ }
+
+ if (arg_count(cmd, uuidstr_ARG)) {
+ uuid = arg_str_value(cmd, uuidstr_ARG, "");
+ if (!id_read_format(&pp->id, uuid))
+ return 0;
+ pp->idp = &pp->id;
+ }
+
+ if (arg_count(cmd, restorefile_ARG)) {
+ pp->restorefile = arg_str_value(cmd, restorefile_ARG, "");
+ /* The uuid won't already exist */
+ if (!(vg = backup_read_vg(cmd, NULL, pp->restorefile))) {
+ log_error("Unable to read volume group from %s",
+ pp->restorefile);
+ return 0;
+ }
+ if (!(existing_pvl = find_pv_in_vg_by_uuid(vg, pp->idp))) {
+ log_error("Can't find uuid %s in backup file %s",
+ uuid, pp->restorefile);
+ return 0;
+ }
+ pp->pe_start = pv_pe_start(existing_pvl->pv);
+ pp->extent_size = pv_pe_size(existing_pvl->pv);
+ pp->extent_count = pv_pe_count(existing_pvl->pv);
+ free_vg(vg);
+ }
+
+ if (arg_sign_value(cmd, physicalvolumesize_ARG, 0) == SIGN_MINUS) {
+ log_error("Physical volume size may not be negative");
+ return 0;
+ }
+ pp->size = arg_uint64_value(cmd, physicalvolumesize_ARG, UINT64_C(0));
+
+ if (arg_count(cmd, restorefile_ARG) || arg_count(cmd, uuidstr_ARG))
+ pp->zero = 0;
+ return 1;
+}
+
+int pvcreate(struct cmd_context *cmd, int argc, char **argv)
+{
+ int i;
+ int ret = ECMD_PROCESSED;
+ struct pvcreate_params pp;
+
+ pvcreate_params_set_defaults(&pp);
+
+ if (!pvcreate_restore_params_validate(cmd, argc, argv, &pp)) {
+ return EINVALID_CMD_LINE;
+ }
+ if (!pvcreate_params_validate(cmd, argc, argv, &pp)) {
+ return EINVALID_CMD_LINE;
+ }
+
+ for (i = 0; i < argc; i++) {
+ if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+ log_error("Can't get lock for orphan PVs");
+ return ECMD_FAILED;
+ }
+
+ unescape_colons_and_at_signs(argv[i], NULL, NULL);
+
+ if (!pvcreate_single(cmd, argv[i], &pp)) {
+ stack;
+ ret = ECMD_FAILED;
+ }
+
+ unlock_vg(cmd, VG_ORPHANS);
+ if (sigint_caught())
+ return ret;
+ }
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static int _pvdisplay_single(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct physical_volume *pv, void *handle)
+{
+ struct pv_list *pvl;
+ int ret = ECMD_PROCESSED;
+ uint64_t size;
+ struct volume_group *old_vg = vg;
+
+ const char *pv_name = pv_dev_name(pv);
+ const char *vg_name = NULL;
+
+ if (!is_orphan(pv) && !vg) {
+ vg_name = pv_vg_name(pv);
+ vg = vg_read(cmd, vg_name, (char *)&pv->vgid, 0);
+ if (vg_read_error(vg)) {
+ log_error("Skipping volume group %s", vg_name);
+ free_vg(vg);
+ /* FIXME If CLUSTERED should return ECMD_PROCESSED here */
+ return ECMD_FAILED;
+ }
+
+ /*
+ * Replace possibly incomplete PV structure with new one
+ * allocated in vg_read_internal() path.
+ */
+ if (!(pvl = find_pv_in_vg(vg, pv_name))) {
+ log_error("Unable to find \"%s\" in volume group \"%s\"",
+ pv_name, vg->name);
+ ret = ECMD_FAILED;
+ goto out;
+ }
+
+ pv = pvl->pv;
+ }
+
+ if (is_orphan(pv))
+ size = pv_size(pv);
+ else
+ size = (pv_pe_count(pv) - pv_pe_alloc_count(pv)) *
+ pv_pe_size(pv);
+
+ if (arg_count(cmd, short_ARG)) {
+ log_print("Device \"%s\" has a capacity of %s", pv_name,
+ display_size(cmd, size));
+ goto out;
+ }
+
+ if (pv_status(pv) & EXPORTED_VG)
+ log_print("Physical volume \"%s\" of volume group \"%s\" "
+ "is exported", pv_name, pv_vg_name(pv));
+
+ if (is_orphan(pv))
+ log_print("\"%s\" is a new physical volume of \"%s\"",
+ pv_name, display_size(cmd, size));
+
+ if (arg_count(cmd, colon_ARG)) {
+ pvdisplay_colons(pv);
+ goto out;
+ }
+
+ pvdisplay_full(cmd, pv, handle);
+
+ if (arg_count(cmd, maps_ARG))
+ pvdisplay_segments(pv);
+
+out:
+ if (vg_name)
+ unlock_vg(cmd, vg_name);
+ if (!old_vg)
+ free_vg(vg);
+
+ return ret;
+}
+
+int pvdisplay(struct cmd_context *cmd, int argc, char **argv)
+{
+ if (arg_count(cmd, columns_ARG)) {
+ if (arg_count(cmd, colon_ARG) || arg_count(cmd, maps_ARG) ||
+ arg_count(cmd, short_ARG)) {
+ log_error("Incompatible options selected");
+ return EINVALID_CMD_LINE;
+ }
+ return pvs(cmd, argc, argv);
+ } else if (arg_count(cmd, aligned_ARG) ||
+ arg_count(cmd, all_ARG) ||
+ arg_count(cmd, noheadings_ARG) ||
+ arg_count(cmd, options_ARG) ||
+ arg_count(cmd, separator_ARG) ||
+ arg_count(cmd, sort_ARG) || arg_count(cmd, unbuffered_ARG)) {
+ log_error("Incompatible options selected");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, colon_ARG) && arg_count(cmd, maps_ARG)) {
+ log_error("Option -v not allowed with option -c");
+ return EINVALID_CMD_LINE;
+ }
+
+ return process_each_pv(cmd, argc, argv, NULL, 0, 0, NULL,
+ _pvdisplay_single);
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "polldaemon.h"
+#include "display.h"
+
+#define PVMOVE_FIRST_TIME 0x00000001 /* Called for first time */
+
+static int _pvmove_target_present(struct cmd_context *cmd, int clustered)
+{
+ const struct segment_type *segtype;
+ unsigned attr = 0;
+ int found = 1;
+ static int _clustered_found = -1;
+
+ if (clustered && _clustered_found >= 0)
+ return _clustered_found;
+
+ if (!(segtype = get_segtype_from_string(cmd, "mirror")))
+ return_0;
+
+ if (activation() && segtype->ops->target_present &&
+ !segtype->ops->target_present(cmd, NULL, clustered ? &attr : NULL))
+ found = 0;
+
+ if (activation() && clustered) {
+ if (found && (attr & MIRROR_LOG_CLUSTERED))
+ _clustered_found = found = 1;
+ else
+ _clustered_found = found = 0;
+ }
+
+ return found;
+}
+
+static unsigned _pvmove_is_exclusive(struct cmd_context *cmd,
+ struct volume_group *vg)
+{
+ if (vg_is_clustered(vg))
+ if (!_pvmove_target_present(cmd, 1))
+ return 1;
+
+ return 0;
+}
+
+/* Allow /dev/vgname/lvname, vgname/lvname or lvname */
+static const char *_extract_lvname(struct cmd_context *cmd, const char *vgname,
+ const char *arg)
+{
+ const char *lvname;
+
+ /* Is an lvname supplied directly? */
+ if (!strchr(arg, '/'))
+ return arg;
+
+ lvname = skip_dev_dir(cmd, arg, NULL);
+ while (*lvname == '/')
+ lvname++;
+ if (!strchr(lvname, '/')) {
+ log_error("--name takes a logical volume name");
+ return NULL;
+ }
+ if (strncmp(vgname, lvname, strlen(vgname)) ||
+ (lvname += strlen(vgname), *lvname != '/')) {
+ log_error("Named LV and old PV must be in the same VG");
+ return NULL;
+ }
+ while (*lvname == '/')
+ lvname++;
+ if (!*lvname) {
+ log_error("Incomplete LV name supplied with --name");
+ return NULL;
+ }
+ return lvname;
+}
+
+static struct volume_group *_get_vg(struct cmd_context *cmd, const char *vgname)
+{
+ dev_close_all();
+
+ return vg_read_for_update(cmd, vgname, NULL, 0);
+}
+
+/* Create list of PVs for allocation of replacement extents */
+static struct dm_list *_get_allocatable_pvs(struct cmd_context *cmd, int argc,
+ char **argv, struct volume_group *vg,
+ struct physical_volume *pv,
+ alloc_policy_t alloc)
+{
+ struct dm_list *allocatable_pvs, *pvht, *pvh;
+ struct pv_list *pvl;
+
+ if (argc)
+ allocatable_pvs = create_pv_list(cmd->mem, vg, argc, argv, 1);
+ else
+ allocatable_pvs = clone_pv_list(cmd->mem, &vg->pvs);
+
+ if (!allocatable_pvs)
+ return_NULL;
+
+ dm_list_iterate_safe(pvh, pvht, allocatable_pvs) {
+ pvl = dm_list_item(pvh, struct pv_list);
+
+ /* Don't allocate onto the PV we're clearing! */
+ if ((alloc != ALLOC_ANYWHERE) && (pvl->pv->dev == pv_dev(pv))) {
+ dm_list_del(&pvl->list);
+ continue;
+ }
+
+ /* Remove PV if full */
+ if ((pvl->pv->pe_count == pvl->pv->pe_alloc_count))
+ dm_list_del(&pvl->list);
+ }
+
+ if (dm_list_empty(allocatable_pvs)) {
+ log_error("No extents available for allocation");
+ return NULL;
+ }
+
+ return allocatable_pvs;
+}
+
+/*
+ * Replace any LV segments on given PV with temporary mirror.
+ * Returns list of LVs changed.
+ */
+static int _insert_pvmove_mirrors(struct cmd_context *cmd,
+ struct logical_volume *lv_mirr,
+ struct dm_list *source_pvl,
+ struct logical_volume *lv,
+ struct dm_list *lvs_changed)
+
+{
+ struct pv_list *pvl;
+ uint32_t prev_le_count;
+
+ /* Only 1 PV may feature in source_pvl */
+ pvl = dm_list_item(source_pvl->n, struct pv_list);
+
+ prev_le_count = lv_mirr->le_count;
+ if (!insert_layer_for_segments_on_pv(cmd, lv, lv_mirr, PVMOVE,
+ pvl, lvs_changed))
+ return_0;
+
+ /* check if layer was inserted */
+ if (lv_mirr->le_count - prev_le_count) {
+ lv->status |= LOCKED;
+
+ log_verbose("Moving %u extents of logical volume %s/%s",
+ lv_mirr->le_count - prev_le_count,
+ lv->vg->name, lv->name);
+ }
+
+ return 1;
+}
+
+/* Create new LV with mirror segments for the required copies */
+static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct dm_list *source_pvl,
+ const char *lv_name,
+ struct dm_list *allocatable_pvs,
+ alloc_policy_t alloc,
+ struct dm_list **lvs_changed)
+{
+ struct logical_volume *lv_mirr, *lv;
+ struct lv_list *lvl;
+ uint32_t log_count = 0;
+ int lv_found = 0;
+ int lv_skipped = 0;
+
+ /* FIXME Cope with non-contiguous => splitting existing segments */
+ if (!(lv_mirr = lv_create_empty("pvmove%d", NULL,
+ LVM_READ | LVM_WRITE,
+ ALLOC_CONTIGUOUS, vg))) {
+ log_error("Creation of temporary pvmove LV failed");
+ return NULL;
+ }
+
+ lv_mirr->status |= (PVMOVE | LOCKED);
+
+ if (!(*lvs_changed = dm_pool_alloc(cmd->mem, sizeof(**lvs_changed)))) {
+ log_error("lvs_changed list struct allocation failed");
+ return NULL;
+ }
+
+ dm_list_init(*lvs_changed);
+
+ /* Find segments to be moved and set up mirrors */
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ lv = lvl->lv;
+ if ((lv == lv_mirr))
+ continue;
+ if (lv_name) {
+ if (strcmp(lv->name, lv_name))
+ continue;
+ lv_found = 1;
+ }
+ if (lv_is_origin(lv) || lv_is_cow(lv)) {
+ lv_skipped = 1;
+ log_print("Skipping snapshot-related LV %s", lv->name);
+ continue;
+ }
+ if (lv->status & MIRRORED) {
+ lv_skipped = 1;
+ log_print("Skipping mirror LV %s", lv->name);
+ continue;
+ }
+ if (lv->status & MIRROR_LOG) {
+ lv_skipped = 1;
+ log_print("Skipping mirror log LV %s", lv->name);
+ continue;
+ }
+ if (lv->status & MIRROR_IMAGE) {
+ lv_skipped = 1;
+ log_print("Skipping mirror image LV %s", lv->name);
+ continue;
+ }
+ if (lv->status & LOCKED) {
+ lv_skipped = 1;
+ log_print("Skipping locked LV %s", lv->name);
+ continue;
+ }
+ if (!_insert_pvmove_mirrors(cmd, lv_mirr, source_pvl, lv,
+ *lvs_changed))
+ return_NULL;
+ }
+
+ if (lv_name && !lv_found) {
+ log_error("Logical volume %s not found.", lv_name);
+ return NULL;
+ }
+
+ /* Is temporary mirror empty? */
+ if (!lv_mirr->le_count) {
+ if (lv_skipped)
+ log_error("All data on source PV skipped. "
+ "It contains locked, hidden or "
+ "non-top level LVs only.");
+ log_error("No data to move for %s", vg->name);
+ return NULL;
+ }
+
+ if (!lv_add_mirrors(cmd, lv_mirr, 1, 1, 0, 0, log_count,
+ allocatable_pvs, alloc, MIRROR_BY_SEG)) {
+ log_error("Failed to convert pvmove LV to mirrored");
+ return_NULL;
+ }
+
+ if (!split_parent_segments_for_layer(cmd, lv_mirr)) {
+ log_error("Failed to split segments being moved");
+ return_NULL;
+ }
+
+ return lv_mirr;
+}
+
+static int _activate_lv(struct cmd_context *cmd, struct logical_volume *lv_mirr,
+ unsigned exclusive)
+{
+ int r = 0;
+
+ if (exclusive)
+ r = activate_lv_excl(cmd, lv_mirr);
+ else
+ r = activate_lv(cmd, lv_mirr);
+
+ if (!r)
+ stack;
+
+ return r;
+}
+
+static int _detach_pvmove_mirror(struct cmd_context *cmd,
+ struct logical_volume *lv_mirr)
+{
+ struct dm_list lvs_completed;
+ struct lv_list *lvl;
+
+ /* Update metadata to remove mirror segments and break dependencies */
+ dm_list_init(&lvs_completed);
+ if (!lv_remove_mirrors(cmd, lv_mirr, 1, 0, NULL, NULL, PVMOVE) ||
+ !remove_layers_for_segments_all(cmd, lv_mirr, PVMOVE,
+ &lvs_completed)) {
+ return 0;
+ }
+
+ dm_list_iterate_items(lvl, &lvs_completed)
+ /* FIXME Assumes only one pvmove at a time! */
+ lvl->lv->status &= ~LOCKED;
+
+ return 1;
+}
+
+static int _update_metadata(struct cmd_context *cmd, struct volume_group *vg,
+ struct logical_volume *lv_mirr,
+ struct dm_list *lvs_changed, unsigned flags)
+{
+ unsigned exclusive = _pvmove_is_exclusive(cmd, vg);
+ unsigned first_time = (flags & PVMOVE_FIRST_TIME) ? 1 : 0;
+ int r = 0;
+
+ log_verbose("Updating volume group metadata");
+ if (!vg_write(vg)) {
+ log_error("ABORTING: Volume group metadata update failed.");
+ return 0;
+ }
+
+ /* Suspend lvs_changed */
+ if (!suspend_lvs(cmd, lvs_changed)) {
+ vg_revert(vg);
+ goto_out;
+ }
+
+ /* Suspend mirrors on subsequent calls */
+ if (!first_time) {
+ if (!suspend_lv(cmd, lv_mirr)) {
+ if (!resume_lvs(cmd, lvs_changed))
+ stack;
+ vg_revert(vg);
+ goto_out;
+ }
+ }
+
+ /* Commit on-disk metadata */
+ if (!vg_commit(vg)) {
+ log_error("ABORTING: Volume group metadata update failed.");
+ if (!first_time)
+ if (!resume_lv(cmd, lv_mirr))
+ stack;
+ if (!resume_lvs(cmd, lvs_changed))
+ stack;
+ vg_revert(vg);
+ goto out;
+ }
+
+ /* Activate the temporary mirror LV */
+ /* Only the first mirror segment gets activated as a mirror */
+ /* FIXME: Add option to use a log */
+ if (first_time) {
+ if (!_activate_lv(cmd, lv_mirr, exclusive)) {
+ if (test_mode()) {
+ r = 1;
+ goto out;
+ }
+
+ /*
+ * FIXME: review ordering of operations above,
+ * temporary mirror should be preloaded in suspend.
+ * Also banned operation here when suspended.
+ * Nothing changed yet, try to revert pvmove.
+ */
+ log_error("Temporary pvmove mirror activation failed.");
+
+ /* Ensure that temporary mrror is deactivate even on other nodes. */
+ (void)deactivate_lv(cmd, lv_mirr);
+
+ /* Revert metadata */
+ if (!_detach_pvmove_mirror(cmd, lv_mirr) ||
+ !lv_remove(lv_mirr) ||
+ !vg_write(vg) || !vg_commit(vg))
+ log_error("ABORTING: Restoring original configuration "
+ "before pvmove failed. Run pvmove --abort.");
+
+ /* Unsuspend LVs */
+ if(!resume_lvs(cmd, lvs_changed))
+ stack;
+
+ goto out;
+ }
+ } else if (!resume_lv(cmd, lv_mirr)) {
+ log_error("Unable to reactivate logical volume \"%s\"",
+ lv_mirr->name);
+ if (!resume_lvs(cmd, lvs_changed))
+ stack;
+ goto out;
+ }
+
+ /* Unsuspend LVs */
+ if (!resume_lvs(cmd, lvs_changed)) {
+ log_error("Unable to resume logical volumes");
+ goto out;
+ }
+
+ r = 1;
+out:
+ backup(vg);
+ return r;
+}
+
+static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name,
+ int argc, char **argv)
+{
+ const char *lv_name = NULL;
+ char *pv_name_arg;
+ struct volume_group *vg;
+ struct dm_list *source_pvl;
+ struct dm_list *allocatable_pvs;
+ alloc_policy_t alloc;
+ struct dm_list *lvs_changed;
+ struct physical_volume *pv;
+ struct logical_volume *lv_mirr;
+ unsigned first_time = 1;
+ unsigned exclusive;
+ int r = ECMD_FAILED;
+
+ pv_name_arg = argv[0];
+ argc--;
+ argv++;
+
+ /* Find PV (in VG) */
+ if (!(pv = find_pv_by_name(cmd, pv_name))) {
+ stack;
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, name_ARG)) {
+ if (!(lv_name = _extract_lvname(cmd, pv_vg_name(pv),
+ arg_value(cmd, name_ARG)))) {
+ stack;
+ return EINVALID_CMD_LINE;
+ }
+
+ if (!validate_name(lv_name)) {
+ log_error("Logical volume name %s is invalid", lv_name);
+ return EINVALID_CMD_LINE;
+ }
+ }
+
+ /* Read VG */
+ log_verbose("Finding volume group \"%s\"", pv_vg_name(pv));
+
+ vg = _get_vg(cmd, pv_vg_name(pv));
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ stack;
+ return ECMD_FAILED;
+ }
+
+ exclusive = _pvmove_is_exclusive(cmd, vg);
+
+ if ((lv_mirr = find_pvmove_lv(vg, pv_dev(pv), PVMOVE))) {
+ log_print("Detected pvmove in progress for %s", pv_name);
+ if (argc || lv_name)
+ log_error("Ignoring remaining command line arguments");
+
+ if (!(lvs_changed = lvs_using_lv(cmd, vg, lv_mirr))) {
+ log_error("ABORTING: Failed to generate list of moving LVs");
+ goto out;
+ }
+
+ /* Ensure mirror LV is active */
+ if (!_activate_lv(cmd, lv_mirr, exclusive)) {
+ log_error("ABORTING: Temporary mirror activation failed.");
+ goto out;
+ }
+
+ first_time = 0;
+ } else {
+ /* Determine PE ranges to be moved */
+ if (!(source_pvl = create_pv_list(cmd->mem, vg, 1,
+ &pv_name_arg, 0)))
+ goto_out;
+
+ alloc = arg_uint_value(cmd, alloc_ARG, ALLOC_INHERIT);
+ if (alloc == ALLOC_INHERIT)
+ alloc = vg->alloc;
+
+ /* Get PVs we can use for allocation */
+ if (!(allocatable_pvs = _get_allocatable_pvs(cmd, argc, argv,
+ vg, pv, alloc)))
+ goto_out;
+
+ if (!archive(vg))
+ goto_out;
+
+ if (!(lv_mirr = _set_up_pvmove_lv(cmd, vg, source_pvl, lv_name,
+ allocatable_pvs, alloc,
+ &lvs_changed)))
+ goto_out;
+ }
+
+ /* Lock lvs_changed and activate (with old metadata) */
+ if (!activate_lvs(cmd, lvs_changed, exclusive))
+ goto_out;
+
+ /* FIXME Presence of a mirror once set PVMOVE - now remove associated logic */
+ /* init_pvmove(1); */
+ /* vg->status |= PVMOVE; */
+
+ if (first_time) {
+ if (!_update_metadata
+ (cmd, vg, lv_mirr, lvs_changed, PVMOVE_FIRST_TIME))
+ goto_out;
+ }
+
+ /* LVs are all in status LOCKED */
+ r = ECMD_PROCESSED;
+out:
+ unlock_and_free_vg(cmd, vg, pv_vg_name(pv));
+ return r;
+}
+
+static int _finish_pvmove(struct cmd_context *cmd, struct volume_group *vg,
+ struct logical_volume *lv_mirr,
+ struct dm_list *lvs_changed)
+{
+ int r = 1;
+
+ if (!dm_list_empty(lvs_changed) &&
+ !_detach_pvmove_mirror(cmd, lv_mirr)) {
+ log_error("ABORTING: Removal of temporary mirror failed");
+ return 0;
+ }
+
+ /* Store metadata without dependencies on mirror segments */
+ if (!vg_write(vg)) {
+ log_error("ABORTING: Failed to write new data locations "
+ "to disk.");
+ return 0;
+ }
+
+ /* Suspend LVs changed */
+ if (!suspend_lvs(cmd, lvs_changed)) {
+ log_error("Locking LVs to remove temporary mirror failed");
+ r = 0;
+ }
+
+ /* Suspend mirror LV to flush pending I/O */
+ if (!suspend_lv(cmd, lv_mirr)) {
+ log_error("Suspension of temporary mirror LV failed");
+ r = 0;
+ }
+
+ /* Store metadata without dependencies on mirror segments */
+ if (!vg_commit(vg)) {
+ log_error("ABORTING: Failed to write new data locations "
+ "to disk.");
+ vg_revert(vg);
+ if (!resume_lv(cmd, lv_mirr))
+ stack;
+ if (!resume_lvs(cmd, lvs_changed))
+ stack;
+ return 0;
+ }
+
+ /* Release mirror LV. (No pending I/O because it's been suspended.) */
+ if (!resume_lv(cmd, lv_mirr)) {
+ log_error("Unable to reactivate logical volume \"%s\"",
+ lv_mirr->name);
+ r = 0;
+ }
+
+ /* Unsuspend LVs */
+ if (!resume_lvs(cmd, lvs_changed))
+ stack;
+
+ /* Deactivate mirror LV */
+ if (!deactivate_lv(cmd, lv_mirr)) {
+ log_error("ABORTING: Unable to deactivate temporary logical "
+ "volume \"%s\"", lv_mirr->name);
+ r = 0;
+ }
+
+ log_verbose("Removing temporary pvmove LV");
+ if (!lv_remove(lv_mirr)) {
+ log_error("ABORTING: Removal of temporary pvmove LV failed");
+ return 0;
+ }
+
+ /* Store it on disks */
+ log_verbose("Writing out final volume group after pvmove");
+ if (!vg_write(vg) || !vg_commit(vg)) {
+ log_error("ABORTING: Failed to write new data locations "
+ "to disk.");
+ return 0;
+ }
+
+ /* FIXME backup positioning */
+ backup(vg);
+
+ return r;
+}
+
+static struct volume_group *_get_move_vg(struct cmd_context *cmd,
+ const char *name,
+ const char *uuid __attribute__((unused)))
+{
+ struct physical_volume *pv;
+
+ /* Reread all metadata in case it got changed */
+ if (!(pv = find_pv_by_name(cmd, name))) {
+ log_error("ABORTING: Can't reread PV %s", name);
+ /* What more could we do here? */
+ return NULL;
+ }
+
+ return _get_vg(cmd, pv_vg_name(pv));
+}
+
+static struct poll_functions _pvmove_fns = {
+ .get_copy_name_from_lv = get_pvmove_pvname_from_lv_mirr,
+ .get_copy_vg = _get_move_vg,
+ .get_copy_lv = find_pvmove_lv_from_pvname,
+ .poll_progress = poll_mirror_progress,
+ .update_metadata = _update_metadata,
+ .finish_copy = _finish_pvmove,
+};
+
+int pvmove_poll(struct cmd_context *cmd, const char *pv_name,
+ unsigned background)
+{
+ if (test_mode())
+ return ECMD_PROCESSED;
+
+ return poll_daemon(cmd, pv_name, NULL, background, PVMOVE, &_pvmove_fns,
+ "Moved");
+}
+
+int pvmove(struct cmd_context *cmd, int argc, char **argv)
+{
+ char *pv_name = NULL;
+ char *colon;
+ int ret;
+
+ /* dm raid1 target must be present in every case */
+ if (!_pvmove_target_present(cmd, 0)) {
+ log_error("Required device-mapper target(s) not "
+ "detected in your kernel");
+ return ECMD_FAILED;
+ }
+
+ if (argc) {
+ if (!(pv_name = dm_pool_strdup(cmd->mem, argv[0]))) {
+ log_error("Failed to clone PV name");
+ return ECMD_FAILED;
+ }
+
+ unescape_colons_and_at_signs(pv_name, &colon, NULL);
+
+ /* Drop any PE lists from PV name */
+ if (colon)
+ *colon = '\0';
+
+ if (!arg_count(cmd, abort_ARG) &&
+ (ret = _set_up_pvmove(cmd, pv_name, argc, argv)) !=
+ ECMD_PROCESSED) {
+ stack;
+ return ret;
+ }
+ }
+
+ return pvmove_poll(cmd, pv_name, arg_is_set(cmd, background_ARG));
+}
--- /dev/null
+/*
+ * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+const char _really_wipe[] =
+ "Really WIPE LABELS from physical volume \"%s\" of volume group \"%s\" [y/n]? ";
+
+/*
+ * Decide whether it is "safe" to wipe the labels on this device.
+ * 0 indicates we may not.
+ */
+static int pvremove_check(struct cmd_context *cmd, const char *name)
+{
+ struct physical_volume *pv;
+ struct dm_list mdas;
+
+ dm_list_init(&mdas);
+
+ /* FIXME Check partition type is LVM unless --force is given */
+
+ /* Is there a pv here already? */
+ /* If not, this is an error unless you used -f. */
+ if (!(pv = pv_read(cmd, name, &mdas, NULL, 1, 0))) {
+ if (arg_count(cmd, force_ARG))
+ return 1;
+ log_error("Physical Volume %s not found", name);
+ return 0;
+ }
+
+ /*
+ * If a PV has no MDAs it may appear to be an
+ * orphan until the metadata is read off
+ * another PV in the same VG. Detecting this
+ * means checking every VG by scanning every
+ * PV on the system.
+ */
+ if (is_orphan(pv) && !dm_list_size(&mdas)) {
+ if (!scan_vgs_for_pvs(cmd, 0)) {
+ log_error("Rescan for PVs without metadata areas "
+ "failed.");
+ return 0;
+ }
+ if (!(pv = pv_read(cmd, name, NULL, NULL, 1, 0))) {
+ log_error("Failed to read physical volume %s", name);
+ return 0;
+ }
+ }
+
+ /* orphan ? */
+ if (is_orphan(pv))
+ return 1;
+
+ /* Allow partial & exported VGs to be destroyed. */
+ /* we must have -ff to overwrite a non orphan */
+ if (arg_count(cmd, force_ARG) < 2) {
+ log_error("Can't pvremove physical volume \"%s\" of "
+ "volume group \"%s\" without -ff", name, pv_vg_name(pv));
+ return 0;
+ }
+
+ /* prompt */
+ if (!arg_count(cmd, yes_ARG) &&
+ yes_no_prompt(_really_wipe, name, pv_vg_name(pv)) == 'n') {
+ log_error("%s: physical volume label not removed", name);
+ return 0;
+ }
+
+ if (arg_count(cmd, force_ARG)) {
+ log_warn("WARNING: Wiping physical volume label from "
+ "%s%s%s%s", name,
+ !is_orphan(pv) ? " of volume group \"" : "",
+ !is_orphan(pv) ? pv_vg_name(pv) : "",
+ !is_orphan(pv) ? "\"" : "");
+ }
+
+ return 1;
+}
+
+static int pvremove_single(struct cmd_context *cmd, const char *pv_name,
+ void *handle __attribute__((unused)))
+{
+ struct device *dev;
+ int ret = ECMD_FAILED;
+
+ if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+ log_error("Can't get lock for orphan PVs");
+ return ECMD_FAILED;
+ }
+
+ if (!pvremove_check(cmd, pv_name))
+ goto error;
+
+ if (!(dev = dev_cache_get(pv_name, cmd->filter))) {
+ log_error("%s: Couldn't find device. Check your filters?",
+ pv_name);
+ goto error;
+ }
+
+ if (!dev_test_excl(dev)) {
+ /* FIXME Detect whether device-mapper is still using the device */
+ log_error("Can't open %s exclusively - not removing. "
+ "Mounted filesystem?", dev_name(dev));
+ goto error;
+ }
+
+ /* Wipe existing label(s) */
+ if (!label_remove(dev)) {
+ log_error("Failed to wipe existing label(s) on %s", pv_name);
+ goto error;
+ }
+
+ log_print("Labels on physical volume \"%s\" successfully wiped",
+ pv_name);
+
+ ret = ECMD_PROCESSED;
+
+ error:
+ unlock_vg(cmd, VG_ORPHANS);
+
+ return ret;
+}
+
+int pvremove(struct cmd_context *cmd, int argc, char **argv)
+{
+ int i, r;
+ int ret = ECMD_PROCESSED;
+
+ if (!argc) {
+ log_error("Please enter a physical volume path");
+ return EINVALID_CMD_LINE;
+ }
+
+ for (i = 0; i < argc; i++) {
+ unescape_colons_and_at_signs(argv[i], NULL, NULL);
+ r = pvremove_single(cmd, argv[i], NULL);
+ if (r > ret)
+ ret = r;
+ }
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2005 Zak Kipling. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+struct pvresize_params {
+ uint64_t new_size;
+
+ unsigned done;
+ unsigned total;
+};
+
+static int _pv_resize_single(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct physical_volume *pv,
+ const uint64_t new_size)
+{
+ struct pv_list *pvl;
+ uint64_t size = 0;
+ uint32_t new_pe_count = 0;
+ int r = 0;
+ struct dm_list mdas;
+ const char *pv_name = pv_dev_name(pv);
+ const char *vg_name = pv_vg_name(pv);
+ struct lvmcache_info *info;
+ int mda_count = 0;
+ struct volume_group *old_vg = vg;
+
+ dm_list_init(&mdas);
+
+ if (is_orphan_vg(vg_name)) {
+ if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) {
+ log_error("Can't get lock for orphans");
+ return 0;
+ }
+
+ if (!(pv = pv_read(cmd, pv_name, &mdas, NULL, 1, 0))) {
+ unlock_vg(cmd, vg_name);
+ log_error("Unable to read PV \"%s\"", pv_name);
+ return 0;
+ }
+
+ mda_count = dm_list_size(&mdas);
+ } else {
+ vg = vg_read_for_update(cmd, vg_name, NULL, 0);
+
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ log_error("Unable to read volume group \"%s\".",
+ vg_name);
+ return 0;
+ }
+
+ if (!(pvl = find_pv_in_vg(vg, pv_name))) {
+ log_error("Unable to find \"%s\" in volume group \"%s\"",
+ pv_name, vg->name);
+ goto out;
+ }
+
+ pv = pvl->pv;
+
+ if (!(info = info_from_pvid(pv->dev->pvid, 0))) {
+ log_error("Can't get info for PV %s in volume group %s",
+ pv_name, vg->name);
+ goto out;
+ }
+
+ mda_count = dm_list_size(&info->mdas);
+
+ if (!archive(vg))
+ goto out;
+ }
+
+ /* FIXME Create function to test compatibility properly */
+ if (mda_count > 1) {
+ log_error("%s: too many metadata areas for pvresize", pv_name);
+ goto out;
+ }
+
+ if (!(pv->fmt->features & FMT_RESIZE_PV)) {
+ log_error("Physical volume %s format does not support resizing.",
+ pv_name);
+ goto out;
+ }
+
+ /* Get new size */
+ if (!dev_get_size(pv_dev(pv), &size)) {
+ log_error("%s: Couldn't get size.", pv_name);
+ goto out;
+ }
+
+ if (new_size) {
+ if (new_size > size)
+ log_warn("WARNING: %s: Overriding real size. "
+ "You could lose data.", pv_name);
+ log_verbose("%s: Pretending size is %" PRIu64 " not %" PRIu64
+ " sectors.", pv_name, new_size, pv_size(pv));
+ size = new_size;
+ }
+
+ if (size < PV_MIN_SIZE) {
+ log_error("%s: Size must exceed minimum of %ld sectors.",
+ pv_name, PV_MIN_SIZE);
+ goto out;
+ }
+
+ if (size < pv_pe_start(pv)) {
+ log_error("%s: Size must exceed physical extent start of "
+ "%" PRIu64 " sectors.", pv_name, pv_pe_start(pv));
+ goto out;
+ }
+
+ pv->size = size;
+
+ if (vg) {
+ pv->size -= pv_pe_start(pv);
+ new_pe_count = pv_size(pv) / vg->extent_size;
+
+ if (!new_pe_count) {
+ log_error("%s: Size must leave space for at "
+ "least one physical extent of "
+ "%" PRIu32 " sectors.", pv_name,
+ pv_pe_size(pv));
+ goto out;
+ }
+
+ if (!pv_resize(pv, vg, new_pe_count))
+ goto_out;
+ }
+
+ log_verbose("Resizing volume \"%s\" to %" PRIu64 " sectors.",
+ pv_name, pv_size(pv));
+
+ log_verbose("Updating physical volume \"%s\"", pv_name);
+ if (!is_orphan_vg(vg_name)) {
+ if (!vg_write(vg) || !vg_commit(vg)) {
+ log_error("Failed to store physical volume \"%s\" in "
+ "volume group \"%s\"", pv_name, vg_name);
+ goto out;
+ }
+ backup(vg);
+ } else if (!(pv_write(cmd, pv, NULL, INT64_C(-1)))) {
+ log_error("Failed to store physical volume \"%s\"",
+ pv_name);
+ goto out;
+ }
+
+ log_print("Physical volume \"%s\" changed", pv_name);
+ r = 1;
+
+out:
+ unlock_vg(cmd, vg_name);
+ if (!old_vg)
+ free_vg(vg);
+ return r;
+}
+
+static int _pvresize_single(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct physical_volume *pv,
+ void *handle)
+{
+ struct pvresize_params *params = (struct pvresize_params *) handle;
+
+ params->total++;
+
+ if (!_pv_resize_single(cmd, vg, pv, params->new_size)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ params->done++;
+
+ return ECMD_PROCESSED;
+}
+
+int pvresize(struct cmd_context *cmd, int argc, char **argv)
+{
+ struct pvresize_params params;
+ int ret;
+
+ if (!argc) {
+ log_error("Please supply physical volume(s)");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_sign_value(cmd, physicalvolumesize_ARG, 0) == SIGN_MINUS) {
+ log_error("Physical volume size may not be negative");
+ return 0;
+ }
+
+ params.new_size = arg_uint64_value(cmd, physicalvolumesize_ARG,
+ UINT64_C(0));
+
+ params.done = 0;
+ params.total = 0;
+
+ ret = process_each_pv(cmd, argc, argv, NULL, READ_FOR_UPDATE, 0, ¶ms,
+ _pvresize_single);
+
+ log_print("%d physical volume(s) resized / %d physical volume(s) "
+ "not resized", params.done, params.total - params.done);
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+int pv_max_name_len = 0;
+int vg_max_name_len = 0;
+
+static void _pvscan_display_single(struct cmd_context *cmd,
+ struct physical_volume *pv,
+ void *handle __attribute__((unused)))
+{
+ char uuid[64] __attribute__((aligned(8)));
+ unsigned vg_name_len = 0;
+
+ char pv_tmp_name[NAME_LEN] = { 0, };
+ char vg_tmp_name[NAME_LEN] = { 0, };
+ char vg_name_this[NAME_LEN] = { 0, };
+
+ /* short listing? */
+ if (arg_count(cmd, short_ARG) > 0) {
+ log_print("%s", pv_dev_name(pv));
+ return;
+ }
+
+ if (arg_count(cmd, verbose_ARG) > 1) {
+ /* FIXME As per pv_display! Drop through for now. */
+ /* pv_show(pv); */
+
+ /* FIXME - Moved to Volume Group structure */
+ /* log_print("System Id %s", pv->vg->system_id); */
+
+ /* log_print(" "); */
+ /* return; */
+ }
+
+ memset(pv_tmp_name, 0, sizeof(pv_tmp_name));
+
+ vg_name_len = strlen(pv_vg_name(pv)) + 1;
+
+ if (arg_count(cmd, uuid_ARG)) {
+ if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
+ stack;
+ return;
+ }
+
+ sprintf(pv_tmp_name, "%-*s with UUID %s",
+ pv_max_name_len - 2, pv_dev_name(pv), uuid);
+ } else {
+ sprintf(pv_tmp_name, "%s", pv_dev_name(pv));
+ }
+
+ if (is_orphan(pv)) {
+ log_print("PV %-*s %-*s %s [%s]",
+ pv_max_name_len, pv_tmp_name,
+ vg_max_name_len, " ",
+ pv->fmt ? pv->fmt->name : " ",
+ display_size(cmd, pv_size(pv)));
+ return;
+ }
+
+ if (pv_status(pv) & EXPORTED_VG) {
+ strncpy(vg_name_this, pv_vg_name(pv), vg_name_len);
+ log_print("PV %-*s is in exported VG %s "
+ "[%s / %s free]",
+ pv_max_name_len, pv_tmp_name,
+ vg_name_this,
+ display_size(cmd, (uint64_t) pv_pe_count(pv) *
+ pv_pe_size(pv)),
+ display_size(cmd, (uint64_t) (pv_pe_count(pv) -
+ pv_pe_alloc_count(pv))
+ * pv_pe_size(pv)));
+ return;
+ }
+
+ sprintf(vg_tmp_name, "%s", pv_vg_name(pv));
+ log_print("PV %-*s VG %-*s %s [%s / %s free]", pv_max_name_len,
+ pv_tmp_name, vg_max_name_len, vg_tmp_name,
+ pv->fmt ? pv->fmt->name : " ",
+ display_size(cmd, (uint64_t) pv_pe_count(pv) *
+ pv_pe_size(pv)),
+ display_size(cmd, (uint64_t) (pv_pe_count(pv) -
+ pv_pe_alloc_count(pv)) *
+ pv_pe_size(pv)));
+}
+
+int pvscan(struct cmd_context *cmd, int argc __attribute__((unused)),
+ char **argv __attribute__((unused)))
+{
+ int new_pvs_found = 0;
+ int pvs_found = 0;
+
+ struct dm_list *pvslist;
+ struct pv_list *pvl;
+ struct physical_volume *pv;
+
+ uint64_t size_total = 0;
+ uint64_t size_new = 0;
+
+ int len = 0;
+ pv_max_name_len = 0;
+ vg_max_name_len = 0;
+
+ if (arg_count(cmd, novolumegroup_ARG) && arg_count(cmd, exported_ARG)) {
+ log_error("Options -e and -n are incompatible");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, exported_ARG) || arg_count(cmd, novolumegroup_ARG))
+ log_warn("WARNING: only considering physical volumes %s",
+ arg_count(cmd, exported_ARG) ?
+ "of exported volume group(s)" : "in no volume group");
+
+ if (!lock_vol(cmd, VG_GLOBAL, LCK_VG_WRITE)) {
+ log_error("Unable to obtain global lock.");
+ return ECMD_FAILED;
+ }
+
+ persistent_filter_wipe(cmd->filter);
+ lvmcache_destroy(cmd, 1);
+
+ log_verbose("Walking through all physical volumes");
+ if (!(pvslist = get_pvs(cmd))) {
+ unlock_vg(cmd, VG_GLOBAL);
+ stack;
+ return ECMD_FAILED;
+ }
+
+ /* eliminate exported/new if required */
+ dm_list_iterate_items(pvl, pvslist) {
+ pv = pvl->pv;
+
+ if ((arg_count(cmd, exported_ARG)
+ && !(pv_status(pv) & EXPORTED_VG))
+ || (arg_count(cmd, novolumegroup_ARG) && (!is_orphan(pv)))) {
+ dm_list_del(&pvl->list);
+ continue;
+ }
+
+ /* Also check for MD use? */
+/*******
+ if (MAJOR(pv_create_kdev_t(pv[p]->pv_name)) != MD_MAJOR) {
+ log_warn
+ ("WARNING: physical volume \"%s\" belongs to a meta device",
+ pv[p]->pv_name);
+ }
+ if (MAJOR(pv[p]->pv_dev) != MD_MAJOR)
+ continue;
+********/
+ pvs_found++;
+
+ if (is_orphan(pv)) {
+ new_pvs_found++;
+ size_new += pv_size(pv);
+ size_total += pv_size(pv);
+ } else
+ size_total += (uint64_t) pv_pe_count(pv) * pv_pe_size(pv);
+ }
+
+ /* find maximum pv name length */
+ pv_max_name_len = vg_max_name_len = 0;
+ dm_list_iterate_items(pvl, pvslist) {
+ pv = pvl->pv;
+ len = strlen(pv_dev_name(pv));
+ if (pv_max_name_len < len)
+ pv_max_name_len = len;
+ len = strlen(pv_vg_name(pv));
+ if (vg_max_name_len < len)
+ vg_max_name_len = len;
+ }
+ pv_max_name_len += 2;
+ vg_max_name_len += 2;
+
+ dm_list_iterate_items(pvl, pvslist)
+ _pvscan_display_single(cmd, pvl->pv, NULL);
+
+ if (!pvs_found) {
+ log_print("No matching physical volumes found");
+ unlock_vg(cmd, VG_GLOBAL);
+ return ECMD_PROCESSED;
+ }
+
+ log_print("Total: %d [%s] / in use: %d [%s] / in no VG: %d [%s]",
+ pvs_found,
+ display_size(cmd, size_total),
+ pvs_found - new_pvs_found,
+ display_size(cmd, (size_total - size_new)),
+ new_pvs_found, display_size(cmd, size_new));
+
+ unlock_vg(cmd, VG_GLOBAL);
+
+ return ECMD_PROCESSED;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "report.h"
+
+static int _vgs_single(struct cmd_context *cmd __attribute__((unused)),
+ const char *vg_name, struct volume_group *vg,
+ void *handle)
+{
+ if (!report_object(handle, vg, NULL, NULL, NULL, NULL)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ check_current_backup(vg);
+
+ return ECMD_PROCESSED;
+}
+
+static int _lvs_single(struct cmd_context *cmd, struct logical_volume *lv,
+ void *handle)
+{
+ if (!report_object(handle, lv->vg, lv, NULL, NULL, NULL)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ return ECMD_PROCESSED;
+}
+
+static int _segs_single(struct cmd_context *cmd __attribute__((unused)),
+ struct lv_segment *seg, void *handle)
+{
+ if (!report_object(handle, seg->lv->vg, seg->lv, NULL, seg, NULL)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ return ECMD_PROCESSED;
+}
+
+static int _pvsegs_sub_single(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct pv_segment *pvseg, void *handle)
+{
+ int ret = ECMD_PROCESSED;
+ struct lv_segment *seg = pvseg->lvseg;
+
+ struct volume_group _free_vg = {
+ .cmd = cmd,
+ .name = (char *)"",
+ };
+
+ if (!(_free_vg.vgmem = dm_pool_create("_free_vg", 10240)))
+ return ECMD_FAILED;
+
+ struct logical_volume _free_logical_volume = {
+ .vg = vg ?: &_free_vg,
+ .name = (char *) "",
+ .snapshot = NULL,
+ .status = VISIBLE_LV,
+ .major = -1,
+ .minor = -1,
+ };
+
+ struct lv_segment _free_lv_segment = {
+ .lv = &_free_logical_volume,
+ .le = 0,
+ .status = 0,
+ .stripe_size = 0,
+ .area_count = 0,
+ .area_len = 0,
+ .origin = NULL,
+ .cow = NULL,
+ .chunk_size = 0,
+ .region_size = 0,
+ .extents_copied = 0,
+ .log_lv = NULL,
+ .areas = NULL,
+ };
+
+ _free_lv_segment.segtype = get_segtype_from_string(cmd, "free");
+ _free_lv_segment.len = pvseg->len;
+ dm_list_init(&_free_vg.pvs);
+ dm_list_init(&_free_vg.lvs);
+ dm_list_init(&_free_vg.tags);
+ dm_list_init(&_free_lv_segment.tags);
+ dm_list_init(&_free_lv_segment.origin_list);
+ dm_list_init(&_free_logical_volume.tags);
+ dm_list_init(&_free_logical_volume.segments);
+ dm_list_init(&_free_logical_volume.segs_using_this_lv);
+ dm_list_init(&_free_logical_volume.snapshot_segs);
+
+ if (!report_object(handle, vg, seg ? seg->lv : &_free_logical_volume, pvseg->pv,
+ seg ? : &_free_lv_segment, pvseg)) {
+ ret = ECMD_FAILED;
+ goto_out;
+ }
+ out:
+ free_vg(&_free_vg);
+ return ret;
+}
+
+static int _lvsegs_single(struct cmd_context *cmd, struct logical_volume *lv,
+ void *handle)
+{
+ if (!arg_count(cmd, all_ARG) && !lv_is_visible(lv))
+ return ECMD_PROCESSED;
+
+ return process_each_segment_in_lv(cmd, lv, handle, _segs_single);
+}
+
+static int _pvsegs_single(struct cmd_context *cmd, struct volume_group *vg,
+ struct physical_volume *pv, void *handle)
+{
+ return process_each_segment_in_pv(cmd, vg, pv, handle,
+ _pvsegs_sub_single);
+}
+
+static int _pvs_single(struct cmd_context *cmd, struct volume_group *vg,
+ struct physical_volume *pv, void *handle)
+{
+ struct pv_list *pvl;
+ int ret = ECMD_PROCESSED;
+ const char *vg_name = NULL;
+ struct volume_group *old_vg = vg;
+ char uuid[64] __attribute__((aligned(8)));
+
+ if (is_pv(pv) && !is_orphan(pv) && !vg) {
+ vg_name = pv_vg_name(pv);
+
+ vg = vg_read(cmd, vg_name, (char *)&pv->vgid, 0);
+ if (vg_read_error(vg)) {
+ log_error("Skipping volume group %s", vg_name);
+ free_vg(vg);
+ return ECMD_FAILED;
+ }
+
+ /*
+ * Replace possibly incomplete PV structure with new one
+ * allocated in vg_read.
+ */
+ if (!is_missing_pv(pv)) {
+ if (!(pvl = find_pv_in_vg(vg, pv_dev_name(pv)))) {
+ log_error("Unable to find \"%s\" in volume group \"%s\"",
+ pv_dev_name(pv), vg->name);
+ ret = ECMD_FAILED;
+ goto out;
+ }
+ } else if (!(pvl = find_pv_in_vg_by_uuid(vg, &pv->id))) {
+ if (!id_write_format(&pv->id, uuid, sizeof(uuid))) {
+ stack;
+ uuid[0] = '\0';
+ }
+
+ log_error("Unable to find missing PV %s in volume group %s",
+ uuid, vg->name);
+ ret = ECMD_FAILED;
+ goto out;
+ }
+
+ pv = pvl->pv;
+ }
+
+ if (!report_object(handle, vg, NULL, pv, NULL, NULL)) {
+ stack;
+ ret = ECMD_FAILED;
+ }
+
+out:
+ if (vg_name)
+ unlock_vg(cmd, vg_name);
+
+ if (!old_vg)
+ free_vg(vg);
+
+ return ret;
+}
+
+static int _label_single(struct cmd_context *cmd, struct volume_group *vg,
+ struct physical_volume *pv, void *handle)
+{
+ if (!report_object(handle, vg, NULL, pv, NULL, NULL)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ return ECMD_PROCESSED;
+}
+
+static int _pvs_in_vg(struct cmd_context *cmd, const char *vg_name,
+ struct volume_group *vg,
+ void *handle)
+{
+ if (vg_read_error(vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ return process_each_pv_in_vg(cmd, vg, NULL, handle, &_pvs_single);
+}
+
+static int _pvsegs_in_vg(struct cmd_context *cmd, const char *vg_name,
+ struct volume_group *vg,
+ void *handle)
+{
+ if (vg_read_error(vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ return process_each_pv_in_vg(cmd, vg, NULL, handle, &_pvsegs_single);
+}
+
+static int _report(struct cmd_context *cmd, int argc, char **argv,
+ report_type_t report_type)
+{
+ void *report_handle;
+ const char *opts;
+ char *str;
+ const char *keys = NULL, *options = NULL, *separator;
+ int r = ECMD_PROCESSED;
+ int aligned, buffered, headings, field_prefixes, quoted;
+ int columns_as_rows;
+ unsigned args_are_pvs;
+
+ aligned = find_config_tree_int(cmd, "report/aligned",
+ DEFAULT_REP_ALIGNED);
+ buffered = find_config_tree_int(cmd, "report/buffered",
+ DEFAULT_REP_BUFFERED);
+ headings = find_config_tree_int(cmd, "report/headings",
+ DEFAULT_REP_HEADINGS);
+ separator = find_config_tree_str(cmd, "report/separator",
+ DEFAULT_REP_SEPARATOR);
+ field_prefixes = find_config_tree_int(cmd, "report/prefixes",
+ DEFAULT_REP_PREFIXES);
+ quoted = find_config_tree_int(cmd, "report/quoted",
+ DEFAULT_REP_QUOTED);
+ columns_as_rows = find_config_tree_int(cmd, "report/columns_as_rows",
+ DEFAULT_REP_COLUMNS_AS_ROWS);
+
+ args_are_pvs = (report_type == PVS ||
+ report_type == LABEL ||
+ report_type == PVSEGS) ? 1 : 0;
+
+ switch (report_type) {
+ case LVS:
+ keys = find_config_tree_str(cmd, "report/lvs_sort",
+ DEFAULT_LVS_SORT);
+ if (!arg_count(cmd, verbose_ARG))
+ options = find_config_tree_str(cmd,
+ "report/lvs_cols",
+ DEFAULT_LVS_COLS);
+ else
+ options = find_config_tree_str(cmd,
+ "report/lvs_cols_verbose",
+ DEFAULT_LVS_COLS_VERB);
+ break;
+ case VGS:
+ keys = find_config_tree_str(cmd, "report/vgs_sort",
+ DEFAULT_VGS_SORT);
+ if (!arg_count(cmd, verbose_ARG))
+ options = find_config_tree_str(cmd,
+ "report/vgs_cols",
+ DEFAULT_VGS_COLS);
+ else
+ options = find_config_tree_str(cmd,
+ "report/vgs_cols_verbose",
+ DEFAULT_VGS_COLS_VERB);
+ break;
+ case LABEL:
+ case PVS:
+ keys = find_config_tree_str(cmd, "report/pvs_sort",
+ DEFAULT_PVS_SORT);
+ if (!arg_count(cmd, verbose_ARG))
+ options = find_config_tree_str(cmd,
+ "report/pvs_cols",
+ DEFAULT_PVS_COLS);
+ else
+ options = find_config_tree_str(cmd,
+ "report/pvs_cols_verbose",
+ DEFAULT_PVS_COLS_VERB);
+ break;
+ case SEGS:
+ keys = find_config_tree_str(cmd, "report/segs_sort",
+ DEFAULT_SEGS_SORT);
+ if (!arg_count(cmd, verbose_ARG))
+ options = find_config_tree_str(cmd,
+ "report/segs_cols",
+ DEFAULT_SEGS_COLS);
+ else
+ options = find_config_tree_str(cmd,
+ "report/segs_cols_verbose",
+ DEFAULT_SEGS_COLS_VERB);
+ break;
+ case PVSEGS:
+ keys = find_config_tree_str(cmd, "report/pvsegs_sort",
+ DEFAULT_PVSEGS_SORT);
+ if (!arg_count(cmd, verbose_ARG))
+ options = find_config_tree_str(cmd,
+ "report/pvsegs_cols",
+ DEFAULT_PVSEGS_COLS);
+ else
+ options = find_config_tree_str(cmd,
+ "report/pvsegs_cols_verbose",
+ DEFAULT_PVSEGS_COLS_VERB);
+ break;
+ }
+
+ /* If -o supplied use it, else use default for report_type */
+ if (arg_count(cmd, options_ARG)) {
+ opts = arg_str_value(cmd, options_ARG, "");
+ if (!opts || !*opts) {
+ log_error("Invalid options string: %s", opts);
+ return EINVALID_CMD_LINE;
+ }
+ if (*opts == '+') {
+ if (!(str = dm_pool_alloc(cmd->mem,
+ strlen(options) + strlen(opts) + 1))) {
+ log_error("options string allocation failed");
+ return ECMD_FAILED;
+ }
+ strcpy(str, options);
+ strcat(str, ",");
+ strcat(str, opts + 1);
+ options = str;
+ } else
+ options = opts;
+ }
+
+ /* -O overrides default sort settings */
+ keys = arg_str_value(cmd, sort_ARG, keys);
+
+ separator = arg_str_value(cmd, separator_ARG, separator);
+ if (arg_count(cmd, separator_ARG))
+ aligned = 0;
+ if (arg_count(cmd, aligned_ARG))
+ aligned = 1;
+ if (arg_count(cmd, unbuffered_ARG) && !arg_count(cmd, sort_ARG))
+ buffered = 0;
+ if (arg_count(cmd, noheadings_ARG))
+ headings = 0;
+ if (arg_count(cmd, nameprefixes_ARG)) {
+ aligned = 0;
+ field_prefixes = 1;
+ }
+ if (arg_count(cmd, unquoted_ARG))
+ quoted = 0;
+ if (arg_count(cmd, rows_ARG))
+ columns_as_rows = 1;
+
+ if (!(report_handle = report_init(cmd, options, keys, &report_type,
+ separator, aligned, buffered,
+ headings, field_prefixes, quoted,
+ columns_as_rows))) {
+ if (!strcasecmp(options, "help") || !strcmp(options, "?"))
+ return r;
+ stack;
+ return ECMD_FAILED;
+ }
+
+ /* Ensure options selected are compatible */
+ if (report_type & SEGS)
+ report_type |= LVS;
+ if (report_type & PVSEGS)
+ report_type |= PVS;
+ if ((report_type & LVS) && (report_type & (PVS | LABEL)) && !args_are_pvs) {
+ log_error("Can't report LV and PV fields at the same time");
+ dm_report_free(report_handle);
+ return ECMD_FAILED;
+ }
+
+ /* Change report type if fields specified makes this necessary */
+ if ((report_type & PVSEGS) ||
+ ((report_type & (PVS | LABEL)) && (report_type & LVS)))
+ report_type = PVSEGS;
+ else if ((report_type & LABEL) && (report_type & VGS))
+ report_type = PVS;
+ else if (report_type & PVS)
+ report_type = PVS;
+ else if (report_type & SEGS)
+ report_type = SEGS;
+ else if (report_type & LVS)
+ report_type = LVS;
+
+ switch (report_type) {
+ case LVS:
+ r = process_each_lv(cmd, argc, argv, 0, report_handle,
+ &_lvs_single);
+ break;
+ case VGS:
+ r = process_each_vg(cmd, argc, argv, 0,
+ report_handle, &_vgs_single);
+ break;
+ case LABEL:
+ r = process_each_pv(cmd, argc, argv, NULL, READ_WITHOUT_LOCK,
+ 1, report_handle, &_label_single);
+ break;
+ case PVS:
+ if (args_are_pvs)
+ r = process_each_pv(cmd, argc, argv, NULL, 0,
+ 0, report_handle, &_pvs_single);
+ else
+ r = process_each_vg(cmd, argc, argv, 0,
+ report_handle, &_pvs_in_vg);
+ break;
+ case SEGS:
+ r = process_each_lv(cmd, argc, argv, 0, report_handle,
+ &_lvsegs_single);
+ break;
+ case PVSEGS:
+ if (args_are_pvs)
+ r = process_each_pv(cmd, argc, argv, NULL, 0,
+ 0, report_handle, &_pvsegs_single);
+ else
+ r = process_each_vg(cmd, argc, argv, 0,
+ report_handle, &_pvsegs_in_vg);
+ break;
+ }
+
+ dm_report_output(report_handle);
+
+ dm_report_free(report_handle);
+ return r;
+}
+
+int lvs(struct cmd_context *cmd, int argc, char **argv)
+{
+ report_type_t type;
+
+ if (arg_count(cmd, segments_ARG))
+ type = SEGS;
+ else
+ type = LVS;
+
+ return _report(cmd, argc, argv, type);
+}
+
+int vgs(struct cmd_context *cmd, int argc, char **argv)
+{
+ return _report(cmd, argc, argv, VGS);
+}
+
+int pvs(struct cmd_context *cmd, int argc, char **argv)
+{
+ report_type_t type;
+
+ if (arg_count(cmd, segments_ARG))
+ type = PVSEGS;
+ else
+ type = LABEL;
+
+ return _report(cmd, argc, argv, type);
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+int segtypes(struct cmd_context *cmd, int argc __attribute__((unused)),
+ char **argv __attribute__((unused)))
+{
+ display_segtypes(cmd);
+
+ return ECMD_PROCESSED;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define unimplemented \
+ log_error("Command not implemented yet."); return ECMD_FAILED
+
+/*int e2fsadm(struct cmd_context *cmd, int argc, char **argv) unimplemented*/
+int lvmsadc(struct cmd_context *cmd __attribute__((unused)),
+ int argc __attribute__((unused)),
+ char **argv __attribute__((unused)))
+{
+ unimplemented;
+}
+
+int lvmsar(struct cmd_context *cmd __attribute__((unused)),
+ int argc __attribute__((unused)),
+ char **argv __attribute__((unused)))
+{
+ unimplemented;
+}
+
+int pvdata(struct cmd_context *cmd __attribute__((unused)),
+ int argc __attribute__((unused)),
+ char **argv __attribute__((unused)))
+{
+ log_error("There's no 'pvdata' command in LVM2.");
+ log_error("Use lvs, pvs, vgs instead; or use vgcfgbackup and read the text file backup.");
+ log_error("Metadata in LVM1 format can still be displayed using LVM1's pvdata command.");
+ return ECMD_FAILED;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "lv_alloc.h"
+#include "xlate.h"
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+const char *command_name(struct cmd_context *cmd)
+{
+ return cmd->command->name;
+}
+
+/*
+ * Strip dev_dir if present
+ */
+char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name,
+ unsigned *dev_dir_found)
+{
+ const char *dmdir = dm_dir();
+ size_t dmdir_len = strlen(dmdir), vglv_sz;
+ char *vgname, *lvname, *layer, *vglv;
+
+ /* FIXME Do this properly */
+ if (*vg_name == '/') {
+ while (*vg_name == '/')
+ vg_name++;
+ vg_name--;
+ }
+
+ /* Reformat string if /dev/mapper found */
+ if (!strncmp(vg_name, dmdir, dmdir_len) && vg_name[dmdir_len] == '/') {
+ if (dev_dir_found)
+ *dev_dir_found = 1;
+ vg_name += dmdir_len;
+ while (*vg_name == '/')
+ vg_name++;
+
+ if (!dm_split_lvm_name(cmd->mem, vg_name, &vgname, &lvname, &layer) ||
+ *layer) {
+ log_error("skip_dev_dir: Couldn't split up device name %s",
+ vg_name);
+ return (char *) vg_name;
+ }
+ vglv_sz = strlen(vgname) + strlen(lvname) + 2;
+ if (!(vglv = dm_pool_alloc(cmd->mem, vglv_sz)) ||
+ dm_snprintf(vglv, vglv_sz, "%s%s%s", vgname,
+ *lvname ? "/" : "",
+ lvname) < 0) {
+ log_error("vg/lv string alloc failed");
+ return (char *) vg_name;
+ }
+ return vglv;
+ }
+
+ if (!strncmp(vg_name, cmd->dev_dir, strlen(cmd->dev_dir))) {
+ if (dev_dir_found)
+ *dev_dir_found = 1;
+ vg_name += strlen(cmd->dev_dir);
+ while (*vg_name == '/')
+ vg_name++;
+ } else if (dev_dir_found)
+ *dev_dir_found = 0;
+
+ return (char *) vg_name;
+}
+
+/*
+ * Metadata iteration functions
+ */
+int process_each_lv_in_vg(struct cmd_context *cmd,
+ struct volume_group *vg,
+ const struct dm_list *arg_lvnames,
+ const struct dm_list *tags,
+ struct dm_list *failed_lvnames,
+ void *handle,
+ process_single_lv_fn_t process_single_lv)
+{
+ int ret_max = ECMD_PROCESSED;
+ int ret = 0;
+ unsigned process_all = 0;
+ unsigned process_lv = 0;
+ unsigned tags_supplied = 0;
+ unsigned lvargs_supplied = 0;
+ unsigned lvargs_matched = 0;
+ char *lv_name;
+ struct lv_list *lvl;
+
+ if (!vg_check_status(vg, EXPORTED_VG))
+ return ECMD_FAILED;
+
+ if (tags && !dm_list_empty(tags))
+ tags_supplied = 1;
+
+ if (arg_lvnames && !dm_list_empty(arg_lvnames))
+ lvargs_supplied = 1;
+
+ /* Process all LVs in this VG if no restrictions given */
+ if (!tags_supplied && !lvargs_supplied)
+ process_all = 1;
+
+ /* Or if VG tags match */
+ if (!process_lv && tags_supplied &&
+ str_list_match_list(tags, &vg->tags, NULL)) {
+ process_all = 1;
+ }
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ if (lvl->lv->status & SNAPSHOT)
+ continue;
+
+ if (lv_is_virtual_origin(lvl->lv) && !arg_count(cmd, all_ARG))
+ continue;
+
+ /*
+ * Only let hidden LVs through it --all was used or the LVs
+ * were specifically named on the command line.
+ */
+ if (!lvargs_supplied && !lv_is_visible(lvl->lv) && !arg_count(cmd, all_ARG))
+ continue;
+
+ /* Should we process this LV? */
+ if (process_all)
+ process_lv = 1;
+ else
+ process_lv = 0;
+
+ /* LV tag match? */
+ if (!process_lv && tags_supplied &&
+ str_list_match_list(tags, &lvl->lv->tags, NULL)) {
+ process_lv = 1;
+ }
+
+ /* LV name match? */
+ if (lvargs_supplied &&
+ str_list_match_item(arg_lvnames, lvl->lv->name)) {
+ process_lv = 1;
+ lvargs_matched++;
+ }
+
+ if (!process_lv)
+ continue;
+
+ lvl->lv->vg->cmd_missing_vgs = 0;
+ ret = process_single_lv(cmd, lvl->lv, handle);
+ if (ret != ECMD_PROCESSED && failed_lvnames) {
+ lv_name = dm_pool_strdup(cmd->mem, lvl->lv->name);
+ if (!lv_name ||
+ !str_list_add(cmd->mem, failed_lvnames, lv_name)) {
+ log_error("Allocation failed for str_list.");
+ return ECMD_FAILED;
+ }
+ if (lvl->lv->vg->cmd_missing_vgs)
+ ret = ECMD_PROCESSED;
+ }
+ if (ret > ret_max)
+ ret_max = ret;
+ if (sigint_caught())
+ return ret_max;
+ }
+
+ if (lvargs_supplied && lvargs_matched != dm_list_size(arg_lvnames)) {
+ log_error("One or more specified logical volume(s) not found.");
+ if (ret_max < ECMD_FAILED)
+ ret_max = ECMD_FAILED;
+ }
+
+ return ret_max;
+}
+
+int process_each_lv(struct cmd_context *cmd, int argc, char **argv,
+ uint32_t flags, void *handle,
+ process_single_lv_fn_t process_single_lv)
+{
+ int opt = 0;
+ int ret_max = ECMD_PROCESSED;
+ int ret = 0;
+
+ struct dm_list *tags_arg;
+ struct dm_list *vgnames; /* VGs to process */
+ struct str_list *sll, *strl;
+ struct cmd_vg *cvl_vg;
+ struct dm_list cmd_vgs;
+ struct dm_list failed_lvnames;
+ struct dm_list tags, lvnames;
+ struct dm_list arg_lvnames; /* Cmdline vgname or vgname/lvname */
+ struct dm_list arg_vgnames;
+ char *vglv;
+ size_t vglv_sz;
+
+ const char *vgname;
+
+ dm_list_init(&tags);
+ dm_list_init(&arg_lvnames);
+ dm_list_init(&failed_lvnames);
+
+ if (argc) {
+ log_verbose("Using logical volume(s) on command line");
+ dm_list_init(&arg_vgnames);
+
+ for (; opt < argc; opt++) {
+ const char *lv_name = argv[opt];
+ const char *tmp_lv_name;
+ char *vgname_def;
+ unsigned dev_dir_found = 0;
+
+ /* Do we have a tag or vgname or lvname? */
+ vgname = lv_name;
+
+ if (*vgname == '@') {
+ if (!validate_tag(vgname + 1)) {
+ log_error("Skipping invalid tag %s",
+ vgname);
+ continue;
+ }
+ if (!str_list_add(cmd->mem, &tags,
+ dm_pool_strdup(cmd->mem,
+ vgname + 1))) {
+ log_error("strlist allocation failed");
+ return ECMD_FAILED;
+ }
+ continue;
+ }
+
+ /* FIXME Jumbled parsing */
+ vgname = skip_dev_dir(cmd, vgname, &dev_dir_found);
+
+ if (*vgname == '/') {
+ log_error("\"%s\": Invalid path for Logical "
+ "Volume", argv[opt]);
+ if (ret_max < ECMD_FAILED)
+ ret_max = ECMD_FAILED;
+ continue;
+ }
+ lv_name = vgname;
+ if ((tmp_lv_name = strchr(vgname, '/'))) {
+ /* Must be an LV */
+ lv_name = tmp_lv_name;
+ while (*lv_name == '/')
+ lv_name++;
+ if (!(vgname = extract_vgname(cmd, vgname))) {
+ if (ret_max < ECMD_FAILED)
+ ret_max = ECMD_FAILED;
+ continue;
+ }
+ } else if (!dev_dir_found &&
+ (vgname_def = default_vgname(cmd))) {
+ vgname = vgname_def;
+ } else
+ lv_name = NULL;
+
+ if (!str_list_add(cmd->mem, &arg_vgnames,
+ dm_pool_strdup(cmd->mem, vgname))) {
+ log_error("strlist allocation failed");
+ return ECMD_FAILED;
+ }
+
+ if (!lv_name) {
+ if (!str_list_add(cmd->mem, &arg_lvnames,
+ dm_pool_strdup(cmd->mem,
+ vgname))) {
+ log_error("strlist allocation failed");
+ return ECMD_FAILED;
+ }
+ } else {
+ vglv_sz = strlen(vgname) + strlen(lv_name) + 2;
+ if (!(vglv = dm_pool_alloc(cmd->mem, vglv_sz)) ||
+ dm_snprintf(vglv, vglv_sz, "%s/%s", vgname,
+ lv_name) < 0) {
+ log_error("vg/lv string alloc failed");
+ return ECMD_FAILED;
+ }
+ if (!str_list_add(cmd->mem, &arg_lvnames, vglv)) {
+ log_error("strlist allocation failed");
+ return ECMD_FAILED;
+ }
+ }
+ }
+ vgnames = &arg_vgnames;
+ }
+
+ if (!argc || !dm_list_empty(&tags)) {
+ log_verbose("Finding all logical volumes");
+ if (!(vgnames = get_vgnames(cmd, 0)) || dm_list_empty(vgnames)) {
+ log_error("No volume groups found");
+ return ret_max;
+ }
+ }
+
+ dm_list_iterate_items(strl, vgnames) {
+ vgname = strl->str;
+ dm_list_init(&cmd_vgs);
+ if (!(cvl_vg = cmd_vg_add(cmd->mem, &cmd_vgs,
+ vgname, NULL, flags))) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (!cmd_vg_read(cmd, &cmd_vgs)) {
+ free_cmd_vgs(&cmd_vgs);
+ if (ret_max < ECMD_FAILED) {
+ log_error("Skipping volume group %s", vgname);
+ ret_max = ECMD_FAILED;
+ } else
+ stack;
+ continue;
+ }
+
+ tags_arg = &tags;
+ dm_list_init(&lvnames); /* LVs to be processed in this VG */
+ dm_list_iterate_items(sll, &arg_lvnames) {
+ const char *vg_name = sll->str;
+ const char *lv_name = strchr(vg_name, '/');
+
+ if ((!lv_name && !strcmp(vg_name, vgname))) {
+ /* Process all LVs in this VG */
+ tags_arg = NULL;
+ dm_list_init(&lvnames);
+ break;
+ } else if (!strncmp(vg_name, vgname, strlen(vgname)) && lv_name &&
+ strlen(vgname) == (size_t) (lv_name - vg_name)) {
+ if (!str_list_add(cmd->mem, &lvnames,
+ dm_pool_strdup(cmd->mem,
+ lv_name + 1))) {
+ log_error("strlist allocation failed");
+ free_cmd_vgs(&cmd_vgs);
+ return ECMD_FAILED;
+ }
+ }
+ }
+
+ while (!sigint_caught()) {
+ ret = process_each_lv_in_vg(cmd, cvl_vg->vg, &lvnames,
+ tags_arg, &failed_lvnames,
+ handle, process_single_lv);
+ if (ret != ECMD_PROCESSED ||
+ dm_list_empty(&failed_lvnames))
+ break;
+
+ /* Try again with failed LVs in this VG */
+ dm_list_init(&lvnames);
+ dm_list_splice(&lvnames, &failed_lvnames);
+
+ free_cmd_vgs(&cmd_vgs);
+ if (!cmd_vg_read(cmd, &cmd_vgs)) {
+ ret = ECMD_FAILED; /* break */
+ break;
+ }
+ }
+ if (ret > ret_max)
+ ret_max = ret;
+
+ free_cmd_vgs(&cmd_vgs);
+ /* FIXME: logic for breaking command is not consistent */
+ if (sigint_caught())
+ return ECMD_FAILED;
+ }
+
+ return ret_max;
+}
+
+int process_each_segment_in_pv(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct physical_volume *pv,
+ void *handle,
+ process_single_pvseg_fn_t process_single_pvseg)
+{
+ struct pv_segment *pvseg;
+ struct pv_list *pvl;
+ const char *vg_name = NULL;
+ int ret_max = ECMD_PROCESSED;
+ int ret;
+ struct volume_group *old_vg = vg;
+ struct pv_segment _free_pv_segment = { .pv = pv };
+
+ if (is_pv(pv) && !vg && !is_orphan(pv)) {
+ vg_name = pv_vg_name(pv);
+
+ vg = vg_read(cmd, vg_name, NULL, 0);
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ log_error("Skipping volume group %s", vg_name);
+ return ECMD_FAILED;
+ }
+
+ /*
+ * Replace possibly incomplete PV structure with new one
+ * allocated in vg_read_internal() path.
+ */
+ if (!(pvl = find_pv_in_vg(vg, pv_dev_name(pv)))) {
+ log_error("Unable to find %s in volume group %s",
+ pv_dev_name(pv), vg_name);
+ unlock_and_free_vg(cmd, vg, vg_name);
+ return ECMD_FAILED;
+ }
+
+ pv = pvl->pv;
+ }
+
+ if (dm_list_empty(&pv->segments)) {
+ ret = process_single_pvseg(cmd, NULL, &_free_pv_segment, handle);
+ if (ret > ret_max)
+ ret_max = ret;
+ } else
+ dm_list_iterate_items(pvseg, &pv->segments) {
+ ret = process_single_pvseg(cmd, vg, pvseg, handle);
+ if (ret > ret_max)
+ ret_max = ret;
+ if (sigint_caught())
+ break;
+ }
+
+ if (vg_name)
+ unlock_vg(cmd, vg_name);
+ if (!old_vg)
+ free_vg(vg);
+
+ return ret_max;
+}
+
+int process_each_segment_in_lv(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ void *handle,
+ process_single_seg_fn_t process_single_seg)
+{
+ struct lv_segment *seg;
+ int ret_max = ECMD_PROCESSED;
+ int ret;
+
+ dm_list_iterate_items(seg, &lv->segments) {
+ ret = process_single_seg(cmd, seg, handle);
+ if (ret > ret_max)
+ ret_max = ret;
+ /* FIXME: logic for breaking command is not consistent */
+ if (sigint_caught())
+ return ECMD_FAILED;
+ }
+
+ return ret_max;
+}
+
+static int _process_one_vg(struct cmd_context *cmd, const char *vg_name,
+ const char *vgid,
+ struct dm_list *tags, struct dm_list *arg_vgnames,
+ uint32_t flags, void *handle, int ret_max,
+ process_single_vg_fn_t process_single_vg)
+{
+ struct dm_list cmd_vgs;
+ struct cmd_vg *cvl_vg;
+ int ret = 0;
+
+ log_verbose("Finding volume group \"%s\"", vg_name);
+
+ dm_list_init(&cmd_vgs);
+ if (!(cvl_vg = cmd_vg_add(cmd->mem, &cmd_vgs, vg_name, vgid, flags)))
+ return_0;
+
+ for (;;) {
+ /* FIXME: consistent handling of command break */
+ if (sigint_caught()) {
+ ret = ECMD_FAILED;
+ break;
+ }
+ if (!cmd_vg_read(cmd, &cmd_vgs))
+ /* Allow FAILED_INCONSISTENT through only for vgcfgrestore */
+ if (vg_read_error(cvl_vg->vg) &&
+ (!((flags & READ_ALLOW_INCONSISTENT) &&
+ (vg_read_error(cvl_vg->vg) == FAILED_INCONSISTENT)))) {
+ ret = ECMD_FAILED;
+ break;
+ }
+
+ if (!dm_list_empty(tags) &&
+ /* Only process if a tag matches or it's on arg_vgnames */
+ !str_list_match_item(arg_vgnames, vg_name) &&
+ !str_list_match_list(tags, &cvl_vg->vg->tags, NULL))
+ break;
+
+ ret = process_single_vg(cmd, vg_name, cvl_vg->vg, handle);
+
+ if (vg_read_error(cvl_vg->vg)) /* FAILED_INCONSISTENT */
+ break;
+
+ if (!cvl_vg->vg->cmd_missing_vgs)
+ break;
+
+ free_cmd_vgs(&cmd_vgs);
+ }
+
+ free_cmd_vgs(&cmd_vgs);
+
+ return (ret > ret_max) ? ret : ret_max;
+}
+
+int process_each_vg(struct cmd_context *cmd, int argc, char **argv,
+ uint32_t flags, void *handle,
+ process_single_vg_fn_t process_single_vg)
+{
+ int opt = 0;
+ int ret_max = ECMD_PROCESSED;
+
+ struct str_list *sl;
+ struct dm_list *vgnames, *vgids;
+ struct dm_list arg_vgnames, tags;
+
+ const char *vg_name, *vgid;
+
+ dm_list_init(&tags);
+ dm_list_init(&arg_vgnames);
+
+ if (argc) {
+ log_verbose("Using volume group(s) on command line");
+
+ for (; opt < argc; opt++) {
+ vg_name = argv[opt];
+ if (*vg_name == '@') {
+ if (!validate_tag(vg_name + 1)) {
+ log_error("Skipping invalid tag %s",
+ vg_name);
+ if (ret_max < EINVALID_CMD_LINE)
+ ret_max = EINVALID_CMD_LINE;
+ continue;
+ }
+ if (!str_list_add(cmd->mem, &tags,
+ dm_pool_strdup(cmd->mem,
+ vg_name + 1))) {
+ log_error("strlist allocation failed");
+ return ECMD_FAILED;
+ }
+ continue;
+ }
+
+ vg_name = skip_dev_dir(cmd, vg_name, NULL);
+ if (strchr(vg_name, '/')) {
+ log_error("Invalid volume group name: %s",
+ vg_name);
+ if (ret_max < EINVALID_CMD_LINE)
+ ret_max = EINVALID_CMD_LINE;
+ continue;
+ }
+ if (!str_list_add(cmd->mem, &arg_vgnames,
+ dm_pool_strdup(cmd->mem, vg_name))) {
+ log_error("strlist allocation failed");
+ return ECMD_FAILED;
+ }
+ }
+
+ vgnames = &arg_vgnames;
+ }
+
+ if (!argc || !dm_list_empty(&tags)) {
+ log_verbose("Finding all volume groups");
+ if (!(vgids = get_vgids(cmd, 0)) || dm_list_empty(vgids)) {
+ log_error("No volume groups found");
+ return ret_max;
+ }
+ dm_list_iterate_items(sl, vgids) {
+ vgid = sl->str;
+ if (!(vgid) || !(vg_name = vgname_from_vgid(cmd->mem, vgid)))
+ continue;
+ ret_max = _process_one_vg(cmd, vg_name, vgid, &tags,
+ &arg_vgnames,
+ flags, handle,
+ ret_max, process_single_vg);
+ if (sigint_caught())
+ return ret_max;
+ }
+ } else {
+ dm_list_iterate_items(sl, vgnames) {
+ vg_name = sl->str;
+ if (is_orphan_vg(vg_name))
+ continue; /* FIXME Unnecessary? */
+ ret_max = _process_one_vg(cmd, vg_name, NULL, &tags,
+ &arg_vgnames,
+ flags, handle,
+ ret_max, process_single_vg);
+ if (sigint_caught())
+ return ret_max;
+ }
+ }
+
+ return ret_max;
+}
+
+int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
+ const struct dm_list *tags, void *handle,
+ process_single_pv_fn_t process_single_pv)
+{
+ int ret_max = ECMD_PROCESSED;
+ int ret = 0;
+ struct pv_list *pvl;
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (tags && !dm_list_empty(tags) &&
+ !str_list_match_list(tags, &pvl->pv->tags, NULL)) {
+ continue;
+ }
+ if ((ret = process_single_pv(cmd, vg, pvl->pv, handle)) > ret_max)
+ ret_max = ret;
+ if (sigint_caught())
+ return ret_max;
+ }
+
+ return ret_max;
+}
+
+static int _process_all_devs(struct cmd_context *cmd, void *handle,
+ process_single_pv_fn_t process_single_pv)
+{
+ struct physical_volume *pv;
+ struct physical_volume pv_dummy;
+ struct dev_iter *iter;
+ struct device *dev;
+
+ int ret_max = ECMD_PROCESSED;
+ int ret = 0;
+
+ if (!scan_vgs_for_pvs(cmd, 1)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (!(iter = dev_iter_create(cmd->filter, 1))) {
+ log_error("dev_iter creation failed");
+ return ECMD_FAILED;
+ }
+
+ while ((dev = dev_iter_get(iter))) {
+ if (!(pv = pv_read(cmd, dev_name(dev), NULL, NULL, 0, 0))) {
+ memset(&pv_dummy, 0, sizeof(pv_dummy));
+ dm_list_init(&pv_dummy.tags);
+ dm_list_init(&pv_dummy.segments);
+ pv_dummy.dev = dev;
+ pv_dummy.fmt = NULL;
+ pv = &pv_dummy;
+ }
+ ret = process_single_pv(cmd, NULL, pv, handle);
+ if (ret > ret_max)
+ ret_max = ret;
+ if (sigint_caught())
+ break;
+ }
+
+ dev_iter_destroy(iter);
+
+ return ret_max;
+}
+
+/*
+ * If the lock_type is LCK_VG_READ (used only in reporting commands),
+ * we lock VG_GLOBAL to enable use of metadata cache.
+ * This can pause alongide pvscan or vgscan process for a while.
+ */
+int process_each_pv(struct cmd_context *cmd, int argc, char **argv,
+ struct volume_group *vg, uint32_t flags,
+ int scan_label_only, void *handle,
+ process_single_pv_fn_t process_single_pv)
+{
+ int opt = 0;
+ int ret_max = ECMD_PROCESSED;
+ int ret = 0;
+ int lock_global = !(flags & READ_WITHOUT_LOCK) && !(flags & READ_FOR_UPDATE);
+
+ struct pv_list *pvl;
+ struct physical_volume *pv;
+ struct dm_list *pvslist, *vgnames;
+ struct dm_list tags;
+ struct str_list *sll;
+ char *at_sign, *tagname;
+ int scanned = 0;
+ struct dm_list mdas;
+
+ dm_list_init(&tags);
+
+ if (lock_global && !lock_vol(cmd, VG_GLOBAL, LCK_VG_READ)) {
+ log_error("Unable to obtain global lock.");
+ return ECMD_FAILED;
+ }
+
+ if (argc) {
+ log_verbose("Using physical volume(s) on command line");
+ for (; opt < argc; opt++) {
+ unescape_colons_and_at_signs(argv[opt], NULL, &at_sign);
+ if (at_sign && (at_sign == argv[opt])) {
+ tagname = at_sign + 1;
+
+ if (!validate_tag(tagname)) {
+ log_error("Skipping invalid tag %s",
+ tagname);
+ if (ret_max < EINVALID_CMD_LINE)
+ ret_max = EINVALID_CMD_LINE;
+ continue;
+ }
+ if (!str_list_add(cmd->mem, &tags,
+ dm_pool_strdup(cmd->mem,
+ tagname))) {
+ log_error("strlist allocation failed");
+ goto bad;
+ }
+ continue;
+ }
+ if (vg) {
+ if (!(pvl = find_pv_in_vg(vg, argv[opt]))) {
+ log_error("Physical Volume \"%s\" not "
+ "found in Volume Group "
+ "\"%s\"", argv[opt],
+ vg->name);
+ ret_max = ECMD_FAILED;
+ continue;
+ }
+ pv = pvl->pv;
+ } else {
+
+ dm_list_init(&mdas);
+ if (!(pv = pv_read(cmd, argv[opt], &mdas,
+ NULL, 1, scan_label_only))) {
+ log_error("Failed to read physical "
+ "volume \"%s\"", argv[opt]);
+ ret_max = ECMD_FAILED;
+ continue;
+ }
+
+ /*
+ * If a PV has no MDAs it may appear to be an
+ * orphan until the metadata is read off
+ * another PV in the same VG. Detecting this
+ * means checking every VG by scanning every
+ * PV on the system.
+ */
+ if (!scanned && is_orphan(pv) &&
+ !dm_list_size(&mdas)) {
+ if (!scan_label_only &&
+ !scan_vgs_for_pvs(cmd, 1)) {
+ stack;
+ ret_max = ECMD_FAILED;
+ continue;
+ }
+ scanned = 1;
+ if (!(pv = pv_read(cmd, argv[opt],
+ NULL, NULL, 1,
+ scan_label_only))) {
+ log_error("Failed to read "
+ "physical volume "
+ "\"%s\"", argv[opt]);
+ ret_max = ECMD_FAILED;
+ continue;
+ }
+ }
+ }
+
+ ret = process_single_pv(cmd, vg, pv, handle);
+ if (ret > ret_max)
+ ret_max = ret;
+ if (sigint_caught())
+ goto out;
+ }
+ if (!dm_list_empty(&tags) && (vgnames = get_vgnames(cmd, 1)) &&
+ !dm_list_empty(vgnames)) {
+ dm_list_iterate_items(sll, vgnames) {
+ vg = vg_read(cmd, sll->str, NULL, flags);
+ if (vg_read_error(vg)) {
+ ret_max = ECMD_FAILED;
+ free_vg(vg);
+ stack;
+ continue;
+ }
+
+ ret = process_each_pv_in_vg(cmd, vg, &tags,
+ handle,
+ process_single_pv);
+
+ unlock_and_free_vg(cmd, vg, sll->str);
+
+ if (ret > ret_max)
+ ret_max = ret;
+ if (sigint_caught())
+ goto out;
+ }
+ }
+ } else {
+ if (vg) {
+ log_verbose("Using all physical volume(s) in "
+ "volume group");
+ ret = process_each_pv_in_vg(cmd, vg, NULL, handle,
+ process_single_pv);
+ if (ret > ret_max)
+ ret_max = ret;
+ if (sigint_caught())
+ goto out;
+ } else if (arg_count(cmd, all_ARG)) {
+ ret = _process_all_devs(cmd, handle, process_single_pv);
+ if (ret > ret_max)
+ ret_max = ret;
+ if (sigint_caught())
+ goto out;
+ } else {
+ log_verbose("Scanning for physical volume names");
+
+ if (!(pvslist = get_pvs(cmd)))
+ goto bad;
+
+ dm_list_iterate_items(pvl, pvslist) {
+ ret = process_single_pv(cmd, NULL, pvl->pv,
+ handle);
+ if (ret > ret_max)
+ ret_max = ret;
+ if (sigint_caught())
+ goto out;
+ }
+ }
+ }
+out:
+ if (lock_global)
+ unlock_vg(cmd, VG_GLOBAL);
+ return ret_max;
+bad:
+ if (lock_global)
+ unlock_vg(cmd, VG_GLOBAL);
+
+ return ECMD_FAILED;
+}
+
+/*
+ * Determine volume group name from a logical volume name
+ */
+const char *extract_vgname(struct cmd_context *cmd, const char *lv_name)
+{
+ const char *vg_name = lv_name;
+ char *st;
+ char *dev_dir = cmd->dev_dir;
+
+ /* Path supplied? */
+ if (vg_name && strchr(vg_name, '/')) {
+ /* Strip dev_dir (optional) */
+ if (*vg_name == '/') {
+ while (*vg_name == '/')
+ vg_name++;
+ vg_name--;
+ }
+ if (!strncmp(vg_name, dev_dir, strlen(dev_dir))) {
+ vg_name += strlen(dev_dir);
+ while (*vg_name == '/')
+ vg_name++;
+ }
+ if (*vg_name == '/') {
+ log_error("\"%s\": Invalid path for Logical "
+ "Volume", lv_name);
+ return 0;
+ }
+
+ /* Require exactly one set of consecutive slashes */
+ if ((st = strchr(vg_name, '/')))
+ while (*st == '/')
+ st++;
+
+ if (!st || strchr(st, '/')) {
+ log_error("\"%s\": Invalid path for Logical Volume",
+ lv_name);
+ return 0;
+ }
+
+ vg_name = dm_pool_strdup(cmd->mem, vg_name);
+ if (!vg_name) {
+ log_error("Allocation of vg_name failed");
+ return 0;
+ }
+
+ *strchr(vg_name, '/') = '\0';
+ return vg_name;
+ }
+
+ if (!(vg_name = default_vgname(cmd))) {
+ if (lv_name)
+ log_error("Path required for Logical Volume \"%s\"",
+ lv_name);
+ return 0;
+ }
+
+ return vg_name;
+}
+
+/*
+ * Extract default volume group name from environment
+ */
+char *default_vgname(struct cmd_context *cmd)
+{
+ char *vg_path;
+
+ /* Take default VG from environment? */
+ vg_path = getenv("LVM_VG_NAME");
+ if (!vg_path)
+ return 0;
+
+ vg_path = skip_dev_dir(cmd, vg_path, NULL);
+
+ if (strchr(vg_path, '/')) {
+ log_error("Environment Volume Group in LVM_VG_NAME invalid: "
+ "\"%s\"", vg_path);
+ return 0;
+ }
+
+ return dm_pool_strdup(cmd->mem, vg_path);
+}
+
+/*
+ * Process physical extent range specifiers
+ */
+static int _add_pe_range(struct dm_pool *mem, const char *pvname,
+ struct dm_list *pe_ranges, uint32_t start, uint32_t count)
+{
+ struct pe_range *per;
+
+ log_debug("Adding PE range: start PE %" PRIu32 " length %" PRIu32
+ " on %s", start, count, pvname);
+
+ /* Ensure no overlap with existing areas */
+ dm_list_iterate_items(per, pe_ranges) {
+ if (((start < per->start) && (start + count - 1 >= per->start))
+ || ((start >= per->start) &&
+ (per->start + per->count - 1) >= start)) {
+ log_error("Overlapping PE ranges specified (%" PRIu32
+ "-%" PRIu32 ", %" PRIu32 "-%" PRIu32 ")"
+ " on %s",
+ start, start + count - 1, per->start,
+ per->start + per->count - 1, pvname);
+ return 0;
+ }
+ }
+
+ if (!(per = dm_pool_alloc(mem, sizeof(*per)))) {
+ log_error("Allocation of list failed");
+ return 0;
+ }
+
+ per->start = start;
+ per->count = count;
+ dm_list_add(pe_ranges, &per->list);
+
+ return 1;
+}
+
+static int xstrtouint32(const char *s, char **p, int base, uint32_t *result)
+{
+ unsigned long ul;
+
+ errno = 0;
+ ul = strtoul(s, p, base);
+ if (errno || *p == s || (uint32_t) ul != ul)
+ return -1;
+ *result = ul;
+ return 0;
+}
+
+static int _parse_pes(struct dm_pool *mem, char *c, struct dm_list *pe_ranges,
+ const char *pvname, uint32_t size)
+{
+ char *endptr;
+ uint32_t start, end;
+
+ /* Default to whole PV */
+ if (!c) {
+ if (!_add_pe_range(mem, pvname, pe_ranges, UINT32_C(0), size))
+ return_0;
+ return 1;
+ }
+
+ while (*c) {
+ if (*c != ':')
+ goto error;
+
+ c++;
+
+ /* Disallow :: and :\0 */
+ if (*c == ':' || !*c)
+ goto error;
+
+ /* Default to whole range */
+ start = UINT32_C(0);
+ end = size - 1;
+
+ /* Start extent given? */
+ if (isdigit(*c)) {
+ if (xstrtouint32(c, &endptr, 10, &start))
+ goto error;
+ c = endptr;
+ /* Just one number given? */
+ if (!*c || *c == ':')
+ end = start;
+ }
+ /* Range? */
+ if (*c == '-') {
+ c++;
+ if (isdigit(*c)) {
+ if (xstrtouint32(c, &endptr, 10, &end))
+ goto error;
+ c = endptr;
+ }
+ }
+ if (*c && *c != ':')
+ goto error;
+
+ if ((start > end) || (end > size - 1)) {
+ log_error("PE range error: start extent %" PRIu32 " to "
+ "end extent %" PRIu32, start, end);
+ return 0;
+ }
+
+ if (!_add_pe_range(mem, pvname, pe_ranges, start, end - start + 1))
+ return_0;
+
+ }
+
+ return 1;
+
+ error:
+ log_error("Physical extent parsing error at %s", c);
+ return 0;
+}
+
+static int _create_pv_entry(struct dm_pool *mem, struct pv_list *pvl,
+ char *colon, int allocatable_only, struct dm_list *r)
+{
+ const char *pvname;
+ struct pv_list *new_pvl = NULL, *pvl2;
+ struct dm_list *pe_ranges;
+
+ pvname = pv_dev_name(pvl->pv);
+ if (allocatable_only && !(pvl->pv->status & ALLOCATABLE_PV)) {
+ log_error("Physical volume %s not allocatable", pvname);
+ return 1;
+ }
+
+ if (allocatable_only && is_missing_pv(pvl->pv)) {
+ log_error("Physical volume %s is missing", pvname);
+ return 1;
+ }
+
+ if (allocatable_only &&
+ (pvl->pv->pe_count == pvl->pv->pe_alloc_count)) {
+ log_error("No free extents on physical volume \"%s\"", pvname);
+ return 1;
+ }
+
+ dm_list_iterate_items(pvl2, r)
+ if (pvl->pv->dev == pvl2->pv->dev) {
+ new_pvl = pvl2;
+ break;
+ }
+
+ if (!new_pvl) {
+ if (!(new_pvl = dm_pool_alloc(mem, sizeof(*new_pvl)))) {
+ log_error("Unable to allocate physical volume list.");
+ return 0;
+ }
+
+ memcpy(new_pvl, pvl, sizeof(*new_pvl));
+
+ if (!(pe_ranges = dm_pool_alloc(mem, sizeof(*pe_ranges)))) {
+ log_error("Allocation of pe_ranges list failed");
+ return 0;
+ }
+ dm_list_init(pe_ranges);
+ new_pvl->pe_ranges = pe_ranges;
+ dm_list_add(r, &new_pvl->list);
+ }
+
+ /* Determine selected physical extents */
+ if (!_parse_pes(mem, colon, new_pvl->pe_ranges, pv_dev_name(pvl->pv),
+ pvl->pv->pe_count))
+ return_0;
+
+ return 1;
+}
+
+struct dm_list *create_pv_list(struct dm_pool *mem, struct volume_group *vg, int argc,
+ char **argv, int allocatable_only)
+{
+ struct dm_list *r;
+ struct pv_list *pvl;
+ struct dm_list tags, arg_pvnames;
+ char *pvname = NULL;
+ char *colon, *at_sign, *tagname;
+ int i;
+
+ /* Build up list of PVs */
+ if (!(r = dm_pool_alloc(mem, sizeof(*r)))) {
+ log_error("Allocation of list failed");
+ return NULL;
+ }
+ dm_list_init(r);
+
+ dm_list_init(&tags);
+ dm_list_init(&arg_pvnames);
+
+ for (i = 0; i < argc; i++) {
+ unescape_colons_and_at_signs(argv[i], &colon, &at_sign);
+
+ if (at_sign && (at_sign == argv[i])) {
+ tagname = at_sign + 1;
+ if (!validate_tag(tagname)) {
+ log_error("Skipping invalid tag %s", tagname);
+ continue;
+ }
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (str_list_match_item(&pvl->pv->tags,
+ tagname)) {
+ if (!_create_pv_entry(mem, pvl, NULL,
+ allocatable_only,
+ r))
+ return_NULL;
+ }
+ }
+ continue;
+ }
+
+ pvname = argv[i];
+
+ if (colon && !(pvname = dm_pool_strndup(mem, pvname,
+ (unsigned) (colon - pvname)))) {
+ log_error("Failed to clone PV name");
+ return NULL;
+ }
+
+ if (!(pvl = find_pv_in_vg(vg, pvname))) {
+ log_error("Physical Volume \"%s\" not found in "
+ "Volume Group \"%s\"", pvname, vg->name);
+ return NULL;
+ }
+ if (!_create_pv_entry(mem, pvl, colon, allocatable_only, r))
+ return_NULL;
+ }
+
+ if (dm_list_empty(r))
+ log_error("No specified PVs have space available");
+
+ return dm_list_empty(r) ? NULL : r;
+}
+
+struct dm_list *clone_pv_list(struct dm_pool *mem, struct dm_list *pvsl)
+{
+ struct dm_list *r;
+ struct pv_list *pvl, *new_pvl;
+
+ /* Build up list of PVs */
+ if (!(r = dm_pool_alloc(mem, sizeof(*r)))) {
+ log_error("Allocation of list failed");
+ return NULL;
+ }
+ dm_list_init(r);
+
+ dm_list_iterate_items(pvl, pvsl) {
+ if (!(new_pvl = dm_pool_zalloc(mem, sizeof(*new_pvl)))) {
+ log_error("Unable to allocate physical volume list.");
+ return NULL;
+ }
+
+ memcpy(new_pvl, pvl, sizeof(*new_pvl));
+ dm_list_add(r, &new_pvl->list);
+ }
+
+ return r;
+}
+
+void vgcreate_params_set_defaults(struct vgcreate_params *vp_def,
+ struct volume_group *vg)
+{
+ if (vg) {
+ vp_def->vg_name = NULL;
+ vp_def->extent_size = vg->extent_size;
+ vp_def->max_pv = vg->max_pv;
+ vp_def->max_lv = vg->max_lv;
+ vp_def->alloc = vg->alloc;
+ vp_def->clustered = vg_is_clustered(vg);
+ vp_def->vgmetadatacopies = vg->mda_copies;
+ } else {
+ vp_def->vg_name = NULL;
+ vp_def->extent_size = DEFAULT_EXTENT_SIZE * 2;
+ vp_def->max_pv = DEFAULT_MAX_PV;
+ vp_def->max_lv = DEFAULT_MAX_LV;
+ vp_def->alloc = DEFAULT_ALLOC_POLICY;
+ vp_def->clustered = DEFAULT_CLUSTERED;
+ vp_def->vgmetadatacopies = DEFAULT_VGMETADATACOPIES;
+ }
+}
+
+/*
+ * Set members of struct vgcreate_params from cmdline arguments.
+ * Do preliminary validation with arg_*() interface.
+ * Further, more generic validation is done in validate_vgcreate_params().
+ * This function is to remain in tools directory.
+ */
+int vgcreate_params_set_from_args(struct cmd_context *cmd,
+ struct vgcreate_params *vp_new,
+ struct vgcreate_params *vp_def)
+{
+ vp_new->vg_name = skip_dev_dir(cmd, vp_def->vg_name, NULL);
+ vp_new->max_lv = arg_uint_value(cmd, maxlogicalvolumes_ARG,
+ vp_def->max_lv);
+ vp_new->max_pv = arg_uint_value(cmd, maxphysicalvolumes_ARG,
+ vp_def->max_pv);
+ vp_new->alloc = arg_uint_value(cmd, alloc_ARG, vp_def->alloc);
+
+ /* Units of 512-byte sectors */
+ vp_new->extent_size =
+ arg_uint_value(cmd, physicalextentsize_ARG, vp_def->extent_size);
+
+ if (arg_count(cmd, clustered_ARG))
+ vp_new->clustered =
+ !strcmp(arg_str_value(cmd, clustered_ARG,
+ vp_def->clustered ? "y":"n"), "y");
+ else
+ /* Default depends on current locking type */
+ vp_new->clustered = locking_is_clustered();
+
+ if (arg_sign_value(cmd, physicalextentsize_ARG, 0) == SIGN_MINUS) {
+ log_error("Physical extent size may not be negative");
+ return 1;
+ }
+
+ if (arg_sign_value(cmd, maxlogicalvolumes_ARG, 0) == SIGN_MINUS) {
+ log_error("Max Logical Volumes may not be negative");
+ return 1;
+ }
+
+ if (arg_sign_value(cmd, maxphysicalvolumes_ARG, 0) == SIGN_MINUS) {
+ log_error("Max Physical Volumes may not be negative");
+ return 1;
+ }
+
+ if (arg_count(cmd, metadatacopies_ARG)) {
+ vp_new->vgmetadatacopies = arg_int_value(cmd, metadatacopies_ARG,
+ DEFAULT_VGMETADATACOPIES);
+ } else if (arg_count(cmd, vgmetadatacopies_ARG)) {
+ vp_new->vgmetadatacopies = arg_int_value(cmd, vgmetadatacopies_ARG,
+ DEFAULT_VGMETADATACOPIES);
+ } else {
+ vp_new->vgmetadatacopies = find_config_tree_int(cmd,
+ "metadata/vgmetadatacopies",
+ DEFAULT_VGMETADATACOPIES);
+ }
+
+ return 0;
+}
+
+int lv_refresh(struct cmd_context *cmd, struct logical_volume *lv)
+{
+ int r = 0;
+
+ if (!cmd->partial_activation && (lv->status & PARTIAL_LV)) {
+ log_error("Refusing refresh of partial LV %s. Use --partial to override.",
+ lv->name);
+ goto out;
+ }
+
+ r = suspend_lv(cmd, lv);
+ if (!r)
+ goto_out;
+
+ r = resume_lv(cmd, lv);
+ if (!r)
+ goto_out;
+
+ /*
+ * check if snapshot merge should be polled
+ * - unfortunately: even though the dev_manager will clear
+ * the lv's merge attributes if a merge is not possible;
+ * it is clearing a different instance of the lv (as
+ * retrieved with lv_from_lvid)
+ * - fortunately: polldaemon will immediately shutdown if the
+ * origin doesn't have a status with a snapshot percentage
+ */
+ if (background_polling() && lv_is_origin(lv) && lv_is_merging_origin(lv))
+ lv_spawn_background_polling(cmd, lv);
+
+out:
+ return r;
+}
+
+int vg_refresh_visible(struct cmd_context *cmd, struct volume_group *vg)
+{
+ struct lv_list *lvl;
+ int r = 1;
+
+ dm_list_iterate_items(lvl, &vg->lvs)
+ if (lv_is_visible(lvl->lv))
+ if (!lv_refresh(cmd, lvl->lv))
+ r = 0;
+
+ return r;
+}
+
+void lv_spawn_background_polling(struct cmd_context *cmd,
+ struct logical_volume *lv)
+{
+ const char *pvname;
+
+ if ((lv->status & PVMOVE) &&
+ (pvname = get_pvmove_pvname_from_lv_mirr(lv))) {
+ log_verbose("Spawning background pvmove process for %s",
+ pvname);
+ pvmove_poll(cmd, pvname, 1);
+ } else if ((lv->status & LOCKED) &&
+ (pvname = get_pvmove_pvname_from_lv(lv))) {
+ log_verbose("Spawning background pvmove process for %s",
+ pvname);
+ pvmove_poll(cmd, pvname, 1);
+ }
+
+ if (lv->status & (CONVERTING|MERGING)) {
+ log_verbose("Spawning background lvconvert process for %s",
+ lv->name);
+ lvconvert_poll(cmd, lv, 1);
+ }
+}
+
+/*
+ * Intial sanity checking of non-recovery related command-line arguments.
+ *
+ * Output arguments:
+ * pp: structure allocated by caller, fields written / validated here
+ */
+int pvcreate_params_validate(struct cmd_context *cmd,
+ int argc, char **argv,
+ struct pvcreate_params *pp)
+{
+ if (!argc) {
+ log_error("Please enter a physical volume path");
+ return 0;
+ }
+
+ pp->yes = arg_count(cmd, yes_ARG);
+ pp->force = arg_count(cmd, force_ARG);
+
+ if (arg_int_value(cmd, labelsector_ARG, 0) >= LABEL_SCAN_SECTORS) {
+ log_error("labelsector must be less than %lu",
+ LABEL_SCAN_SECTORS);
+ return 0;
+ } else {
+ pp->labelsector = arg_int64_value(cmd, labelsector_ARG,
+ DEFAULT_LABELSECTOR);
+ }
+
+ if (!(cmd->fmt->features & FMT_MDAS) &&
+ (arg_count(cmd, pvmetadatacopies_ARG) ||
+ arg_count(cmd, metadatasize_ARG) ||
+ arg_count(cmd, dataalignment_ARG) ||
+ arg_count(cmd, dataalignmentoffset_ARG))) {
+ log_error("Metadata and data alignment parameters only "
+ "apply to text format.");
+ return 0;
+ }
+
+ if (arg_count(cmd, pvmetadatacopies_ARG) &&
+ arg_int_value(cmd, pvmetadatacopies_ARG, -1) > 2) {
+ log_error("Metadatacopies may only be 0, 1 or 2");
+ return 0;
+ }
+
+ if (arg_count(cmd, metadataignore_ARG)) {
+ pp->metadataignore = !strcmp(arg_str_value(cmd,
+ metadataignore_ARG,
+ DEFAULT_PVMETADATAIGNORE_STR),
+ "y");
+ } else {
+ pp->metadataignore = !strcmp(find_config_tree_str(cmd,
+ "metadata/pvmetadataignore",
+ DEFAULT_PVMETADATAIGNORE_STR),
+ "y");
+ }
+ if (arg_count(cmd, pvmetadatacopies_ARG) &&
+ !arg_int_value(cmd, pvmetadatacopies_ARG, -1) &&
+ pp->metadataignore) {
+ log_error("metadataignore only applies to metadatacopies > 0");
+ return 0;
+ }
+
+ if (arg_count(cmd, zero_ARG))
+ pp->zero = strcmp(arg_str_value(cmd, zero_ARG, "y"), "n");
+
+ if (arg_sign_value(cmd, dataalignment_ARG, 0) == SIGN_MINUS) {
+ log_error("Physical volume data alignment may not be negative");
+ return 0;
+ }
+ pp->data_alignment = arg_uint64_value(cmd, dataalignment_ARG, UINT64_C(0));
+
+ if (pp->data_alignment > ULONG_MAX) {
+ log_error("Physical volume data alignment is too big.");
+ return 0;
+ }
+
+ if (pp->data_alignment && pp->pe_start) {
+ if (pp->pe_start % pp->data_alignment)
+ log_warn("WARNING: Ignoring data alignment %" PRIu64
+ " incompatible with --restorefile value (%"
+ PRIu64").", pp->data_alignment, pp->pe_start);
+ pp->data_alignment = 0;
+ }
+
+ if (arg_sign_value(cmd, dataalignmentoffset_ARG, 0) == SIGN_MINUS) {
+ log_error("Physical volume data alignment offset may not be negative");
+ return 0;
+ }
+ pp->data_alignment_offset = arg_uint64_value(cmd, dataalignmentoffset_ARG, UINT64_C(0));
+
+ if (pp->data_alignment_offset > ULONG_MAX) {
+ log_error("Physical volume data alignment offset is too big.");
+ return 0;
+ }
+
+ if (pp->data_alignment_offset && pp->pe_start) {
+ log_warn("WARNING: Ignoring data alignment offset %" PRIu64
+ " incompatible with --restorefile value (%"
+ PRIu64").", pp->data_alignment_offset, pp->pe_start);
+ pp->data_alignment_offset = 0;
+ }
+
+ if (arg_sign_value(cmd, metadatasize_ARG, 0) == SIGN_MINUS) {
+ log_error("Metadata size may not be negative");
+ return 0;
+ }
+
+ pp->pvmetadatasize = arg_uint64_value(cmd, metadatasize_ARG, UINT64_C(0));
+ if (!pp->pvmetadatasize)
+ pp->pvmetadatasize = find_config_tree_int(cmd,
+ "metadata/pvmetadatasize",
+ DEFAULT_PVMETADATASIZE);
+
+ pp->pvmetadatacopies = arg_int_value(cmd, pvmetadatacopies_ARG, -1);
+ if (pp->pvmetadatacopies < 0)
+ pp->pvmetadatacopies = find_config_tree_int(cmd,
+ "metadata/pvmetadatacopies",
+ DEFAULT_PVMETADATACOPIES);
+
+ return 1;
+}
+
+int get_activation_monitoring_mode(struct cmd_context *cmd,
+ struct volume_group *vg,
+ int *monitoring_mode)
+{
+ *monitoring_mode = DEFAULT_DMEVENTD_MONITOR;
+
+ if (arg_count(cmd, monitor_ARG) &&
+ (arg_count(cmd, ignoremonitoring_ARG) ||
+ arg_count(cmd, sysinit_ARG))) {
+ log_error("--ignoremonitoring or --sysinit option not allowed with --monitor option");
+ return 0;
+ }
+
+ if (arg_count(cmd, monitor_ARG))
+ *monitoring_mode = arg_int_value(cmd, monitor_ARG,
+ DEFAULT_DMEVENTD_MONITOR);
+ else if (is_static() || arg_count(cmd, ignoremonitoring_ARG) ||
+ arg_count(cmd, sysinit_ARG) ||
+ !find_config_tree_bool(cmd, "activation/monitoring",
+ DEFAULT_DMEVENTD_MONITOR))
+ *monitoring_mode = DMEVENTD_MONITOR_IGNORE;
+
+ if (vg && vg_is_clustered(vg) &&
+ *monitoring_mode == DMEVENTD_MONITOR_IGNORE) {
+ log_error("%s is incompatible with clustered Volume Group "
+ "\"%s\": Skipping.",
+ (arg_count(cmd, ignoremonitoring_ARG) ?
+ "--ignoremonitoring" : "activation/monitoring=0"),
+ vg->name);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Generic stripe parameter checks.
+ */
+static int _validate_stripe_params(struct cmd_context *cmd, uint32_t *stripes,
+ uint32_t *stripe_size)
+{
+ if (*stripes == 1 && *stripe_size) {
+ log_print("Ignoring stripesize argument with single stripe");
+ *stripe_size = 0;
+ }
+
+ if (*stripes > 1 && !*stripe_size) {
+ *stripe_size = find_config_tree_int(cmd, "metadata/stripesize", DEFAULT_STRIPESIZE) * 2;
+ log_print("Using default stripesize %s",
+ display_size(cmd, (uint64_t) *stripe_size));
+ }
+
+ if (*stripes < 1 || *stripes > MAX_STRIPES) {
+ log_error("Number of stripes (%d) must be between %d and %d",
+ *stripes, 1, MAX_STRIPES);
+ return 0;
+ }
+
+ if (*stripes > 1 && (*stripe_size < STRIPE_SIZE_MIN ||
+ *stripe_size & (*stripe_size - 1))) {
+ log_error("Invalid stripe size %s",
+ display_size(cmd, (uint64_t) *stripe_size));
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * The stripe size is limited by the size of a uint32_t, but since the
+ * value given by the user is doubled, and the final result must be a
+ * power of 2, we must divide UINT_MAX by four and add 1 (to round it
+ * up to the power of 2)
+ */
+int get_stripe_params(struct cmd_context *cmd, uint32_t *stripes, uint32_t *stripe_size)
+{
+ /* stripes_long_ARG takes precedence (for lvconvert) */
+ *stripes = arg_uint_value(cmd, arg_count(cmd, stripes_long_ARG) ? stripes_long_ARG : stripes_ARG, 1);
+
+ *stripe_size = arg_uint_value(cmd, stripesize_ARG, 0);
+ if (*stripe_size) {
+ if (arg_sign_value(cmd, stripesize_ARG, 0) == SIGN_MINUS) {
+ log_error("Negative stripesize is invalid");
+ return 0;
+ }
+
+ if(*stripe_size > STRIPE_SIZE_LIMIT * 2) {
+ log_error("Stripe size cannot be larger than %s",
+ display_size(cmd, (uint64_t) STRIPE_SIZE_LIMIT));
+ return 0;
+ }
+ }
+
+ return _validate_stripe_params(cmd, stripes, stripe_size);
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_TOOLLIB_H
+#define _LVM_TOOLLIB_H
+
+#include "metadata-exported.h"
+
+int autobackup_set(void);
+int autobackup_init(const char *backup_dir, int keep_days, int keep_number,
+ int autobackup);
+int autobackup(struct volume_group *vg);
+
+struct volume_group *recover_vg(struct cmd_context *cmd, const char *vgname,
+ uint32_t lock_type);
+
+typedef int (*process_single_vg_fn_t) (struct cmd_context * cmd,
+ const char *vg_name,
+ struct volume_group * vg,
+ void *handle);
+typedef int (*process_single_pv_fn_t) (struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct physical_volume *pv,
+ void *handle);
+typedef int (*process_single_lv_fn_t) (struct cmd_context *cmd,
+ struct logical_volume *lv,
+ void *handle);
+typedef int (*process_single_seg_fn_t) (struct cmd_context * cmd,
+ struct lv_segment * seg,
+ void *handle);
+typedef int (*process_single_pvseg_fn_t) (struct cmd_context * cmd,
+ struct volume_group * vg,
+ struct pv_segment * pvseg,
+ void *handle);
+
+int process_each_vg(struct cmd_context *cmd, int argc, char **argv,
+ uint32_t flags, void *handle,
+ process_single_vg_fn_t process_single_vg);
+
+int process_each_pv(struct cmd_context *cmd, int argc, char **argv,
+ struct volume_group *vg, uint32_t lock_type,
+ int scan_label_only, void *handle,
+ process_single_pv_fn_t process_single_pv);
+
+int process_each_segment_in_pv(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct physical_volume *pv,
+ void *handle,
+ process_single_pvseg_fn_t process_single_pvseg);
+
+int process_each_lv(struct cmd_context *cmd, int argc, char **argv,
+ uint32_t flags, void *handle,
+ process_single_lv_fn_t process_single_lv);
+
+
+int process_each_segment_in_lv(struct cmd_context *cmd,
+ struct logical_volume *lv, void *handle,
+ process_single_seg_fn_t process_single_seg);
+
+int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
+ const struct dm_list *tags, void *handle,
+ process_single_pv_fn_t process_single_pv);
+
+
+int process_each_lv_in_vg(struct cmd_context *cmd,
+ struct volume_group *vg,
+ const struct dm_list *arg_lvnames,
+ const struct dm_list *tags,
+ struct dm_list *failed_lvnames,
+ void *handle,
+ process_single_lv_fn_t process_single_lv);
+
+char *default_vgname(struct cmd_context *cmd);
+const char *extract_vgname(struct cmd_context *cmd, const char *lv_name);
+char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name,
+ unsigned *dev_dir_found);
+
+/*
+ * Builds a list of pv's from the names in argv. Used in
+ * lvcreate/extend.
+ */
+struct dm_list *create_pv_list(struct dm_pool *mem, struct volume_group *vg, int argc,
+ char **argv, int allocatable_only);
+
+struct dm_list *clone_pv_list(struct dm_pool *mem, struct dm_list *pvs);
+
+void vgcreate_params_set_defaults(struct vgcreate_params *vp_def,
+ struct volume_group *vg);
+int vgcreate_params_set_from_args(struct cmd_context *cmd,
+ struct vgcreate_params *vp_new,
+ struct vgcreate_params *vp_def);
+int lv_refresh(struct cmd_context *cmd, struct logical_volume *lv);
+int vg_refresh_visible(struct cmd_context *cmd, struct volume_group *vg);
+void lv_spawn_background_polling(struct cmd_context *cmd,
+ struct logical_volume *lv);
+int pvcreate_params_validate(struct cmd_context *cmd,
+ int argc, char **argv,
+ struct pvcreate_params *pp);
+
+int get_activation_monitoring_mode(struct cmd_context *cmd,
+ struct volume_group *vg,
+ int *monitoring_mode);
+int get_stripe_params(struct cmd_context *cmd, uint32_t *stripes,
+ uint32_t *stripe_size);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_TOOLS_H
+#define _LVM_TOOLS_H
+
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "configure.h"
+#include <assert.h>
+#include "libdevmapper.h"
+
+#include "lvm-types.h"
+#include "lvm-logging.h"
+#include "activate.h"
+#include "archiver.h"
+#include "lvmcache.h"
+#include "config.h"
+#include "defaults.h"
+#include "dev-cache.h"
+#include "device.h"
+#include "display.h"
+#include "errors.h"
+#include "filter.h"
+#include "filter-composite.h"
+#include "filter-persistent.h"
+#include "filter-regex.h"
+#include "metadata-exported.h"
+#include "locking.h"
+#include "lvm-exec.h"
+#include "lvm-file.h"
+#include "lvm-string.h"
+#include "segtype.h"
+#include "str_list.h"
+#include "toolcontext.h"
+#include "toollib.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <sys/types.h>
+
+#define CMD_LEN 256
+#define MAX_ARGS 64
+
+/* command functions */
+typedef int (*command_fn) (struct cmd_context * cmd, int argc, char **argv);
+
+#define xx(a, b...) int a(struct cmd_context *cmd, int argc, char **argv);
+#include "commands.h"
+#undef xx
+
+/* define the enums for the command line switches */
+enum {
+#define arg(a, b, c, d, e) a ,
+#include "args.h"
+#undef arg
+};
+
+typedef enum {
+ SIGN_NONE = 0,
+ SIGN_PLUS = 1,
+ SIGN_MINUS = 2
+} sign_t;
+
+typedef enum {
+ PERCENT_NONE = 0,
+ PERCENT_VG,
+ PERCENT_FREE,
+ PERCENT_LV,
+ PERCENT_PVS,
+ PERCENT_ORIGIN
+} percent_type_t;
+
+enum {
+ CHANGE_AY = 0,
+ CHANGE_AN = 1,
+ CHANGE_AE = 2,
+ CHANGE_ALY = 3,
+ CHANGE_ALN = 4
+};
+
+#define ARG_COUNTABLE 0x00000001 /* E.g. -vvvv */
+#define ARG_GROUPABLE 0x00000002 /* E.g. --addtag */
+
+struct arg_values {
+ unsigned count;
+ char *value;
+ int32_t i_value;
+ uint32_t ui_value;
+ int64_t i64_value;
+ uint64_t ui64_value;
+ sign_t sign;
+ percent_type_t percent;
+/* void *ptr; // Currently not used. */
+};
+
+/* a global table of possible arguments */
+struct arg_props {
+ const char short_arg;
+ char _padding[7];
+ const char *long_arg;
+
+ int (*fn) (struct cmd_context *cmd, struct arg_values *av);
+ uint32_t flags;
+};
+
+struct arg_value_group_list {
+ struct dm_list list;
+ struct arg_values arg_values[0];
+};
+
+#define CACHE_VGMETADATA 0x00000001
+#define PERMITTED_READ_ONLY 0x00000002
+
+/* a register of the lvm commands */
+struct command {
+ const char *name;
+ const char *desc;
+ const char *usage;
+ command_fn fn;
+
+ unsigned flags;
+
+ int num_args;
+ int *valid_args;
+};
+
+void usage(const char *name);
+
+/* the argument verify/normalise functions */
+int yes_no_arg(struct cmd_context *cmd, struct arg_values *av);
+int yes_no_excl_arg(struct cmd_context *cmd, struct arg_values *av);
+int size_kb_arg(struct cmd_context *cmd, struct arg_values *av);
+int size_mb_arg(struct cmd_context *cmd, struct arg_values *av);
+int int_arg(struct cmd_context *cmd, struct arg_values *av);
+int int_arg_with_sign(struct cmd_context *cmd, struct arg_values *av);
+int int_arg_with_sign_and_percent(struct cmd_context *cmd, struct arg_values *av);
+int major_arg(struct cmd_context *cmd, struct arg_values *av);
+int minor_arg(struct cmd_context *cmd, struct arg_values *av);
+int string_arg(struct cmd_context *cmd, struct arg_values *av);
+int tag_arg(struct cmd_context *cmd, struct arg_values *av);
+int permission_arg(struct cmd_context *cmd, struct arg_values *av);
+int metadatatype_arg(struct cmd_context *cmd, struct arg_values *av);
+int units_arg(struct cmd_context *cmd, struct arg_values *av);
+int segtype_arg(struct cmd_context *cmd, struct arg_values *av);
+int alloc_arg(struct cmd_context *cmd, struct arg_values *av);
+int readahead_arg(struct cmd_context *cmd, struct arg_values *av);
+int metadatacopies_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_values *av);
+
+/* we use the enums to access the switches */
+unsigned arg_count(const struct cmd_context *cmd, int a);
+unsigned arg_is_set(const struct cmd_context *cmd, int a);
+const char *arg_value(struct cmd_context *cmd, int a);
+const char *arg_str_value(struct cmd_context *cmd, int a, const char *def);
+int32_t arg_int_value(struct cmd_context *cmd, int a, const int32_t def);
+uint32_t arg_uint_value(struct cmd_context *cmd, int a, const uint32_t def);
+int64_t arg_int64_value(struct cmd_context *cmd, int a, const int64_t def);
+uint64_t arg_uint64_value(struct cmd_context *cmd, int a, const uint64_t def);
+const void *arg_ptr_value(struct cmd_context *cmd, int a, const void *def);
+sign_t arg_sign_value(struct cmd_context *cmd, int a, const sign_t def);
+percent_type_t arg_percent_value(struct cmd_context *cmd, int a, const percent_type_t def);
+int arg_count_increment(struct cmd_context *cmd, int a);
+
+unsigned grouped_arg_count(const struct arg_values *av, int a);
+unsigned grouped_arg_is_set(const struct arg_values *av, int a);
+const char *grouped_arg_str_value(const struct arg_values *av, int a, const char *def);
+
+const char *command_name(struct cmd_context *cmd);
+
+int pvmove_poll(struct cmd_context *cmd, const char *pv, unsigned background);
+int lvconvert_poll(struct cmd_context *cmd, struct logical_volume *lv, unsigned background);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static char *_expand_filename(const char *template, const char *vg_name,
+ char **last_filename)
+{
+ char *filename;
+
+ if (security_level())
+ return dm_strdup(template);
+
+ if (!(filename = dm_malloc(PATH_MAX))) {
+ log_error("Failed to allocate filename.");
+ return NULL;
+ }
+
+ if (snprintf(filename, PATH_MAX, template, vg_name) < 0) {
+ log_error("Error processing filename template %s",
+ template);
+ dm_free(filename);
+ return NULL;
+ }
+ if (*last_filename && !strncmp(*last_filename, filename, PATH_MAX)) {
+ log_error("VGs must be backed up into different files. "
+ "Use %%s in filename for VG name.");
+ dm_free(filename);
+ return NULL;
+ }
+
+ dm_free(*last_filename);
+ *last_filename = filename;
+
+ return filename;
+}
+
+static int vg_backup_single(struct cmd_context *cmd, const char *vg_name,
+ struct volume_group *vg,
+ void *handle)
+{
+ char **last_filename = (char **)handle;
+ char *filename;
+
+ if (arg_count(cmd, file_ARG)) {
+ if (!(filename = _expand_filename(arg_value(cmd, file_ARG),
+ vg->name, last_filename))) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (!backup_to_file(filename, vg->cmd->cmd_line, vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ } else {
+ if (vg_read_error(vg) == FAILED_INCONSISTENT) {
+ log_error("No backup taken: specify filename with -f "
+ "to backup an inconsistent VG");
+ stack;
+ return ECMD_FAILED;
+ }
+
+ /* just use the normal backup code */
+ backup_enable(cmd, 1); /* force a backup */
+ if (!backup(vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ }
+
+ log_print("Volume group \"%s\" successfully backed up.", vg_name);
+ return ECMD_PROCESSED;
+}
+
+int vgcfgbackup(struct cmd_context *cmd, int argc, char **argv)
+{
+ int ret;
+ char *last_filename = NULL;
+
+ init_pvmove(1);
+
+ ret = process_each_vg(cmd, argc, argv, READ_ALLOW_INCONSISTENT,
+ &last_filename, &vg_backup_single);
+
+ dm_free(last_filename);
+
+ init_pvmove(0);
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+int vgcfgrestore(struct cmd_context *cmd, int argc, char **argv)
+{
+ char *vg_name = NULL;
+
+ if (argc == 1) {
+ vg_name = skip_dev_dir(cmd, argv[0], NULL);
+ if (!validate_name(vg_name)) {
+ log_error("Volume group name \"%s\" is invalid", vg_name);
+ return ECMD_FAILED;
+ }
+ } else if (!(arg_count(cmd, list_ARG) && arg_count(cmd, file_ARG))) {
+ log_error("Please specify a *single* volume group to restore.");
+ return ECMD_FAILED;
+ }
+
+ /*
+ * FIXME: overloading the -l arg for now to display a
+ * list of archive files for a particular vg
+ */
+ if (arg_count(cmd, list_ARG)) {
+ if (!(arg_count(cmd,file_ARG) ?
+ archive_display_file(cmd,
+ arg_str_value(cmd, file_ARG, "")) :
+ archive_display(cmd, vg_name))) {
+ stack;
+ return ECMD_FAILED;
+ }
+ return ECMD_PROCESSED;
+ }
+
+ if (!lock_vol(cmd, vg_name, LCK_VG_WRITE)) {
+ log_error("Unable to lock volume group %s", vg_name);
+ return ECMD_FAILED;
+ }
+
+ if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+ log_error("Unable to lock orphans");
+ unlock_vg(cmd, vg_name);
+ return ECMD_FAILED;
+ }
+
+ cmd->handles_unknown_segments = 1;
+
+ if (!(arg_count(cmd, file_ARG) ?
+ backup_restore_from_file(cmd, vg_name,
+ arg_str_value(cmd, file_ARG, "")) :
+ backup_restore(cmd, vg_name))) {
+ unlock_vg(cmd, VG_ORPHANS);
+ unlock_vg(cmd, vg_name);
+ log_error("Restore failed.");
+ return ECMD_FAILED;
+ }
+
+ log_print("Restored volume group %s", vg_name);
+
+ unlock_vg(cmd, VG_ORPHANS);
+ unlock_vg(cmd, vg_name);
+ return ECMD_PROCESSED;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+/*
+ * Increments *count by the number of _new_ monitored devices.
+ */
+static int _monitor_lvs_in_vg(struct cmd_context *cmd,
+ struct volume_group *vg, int reg, int *count)
+{
+ struct lv_list *lvl;
+ struct logical_volume *lv;
+ struct lvinfo info;
+ int lv_active;
+ int r = 1;
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ lv = lvl->lv;
+
+ if (!lv_info(cmd, lv, 0, &info, 0, 0))
+ lv_active = 0;
+ else
+ lv_active = info.exists;
+
+ /*
+ * FIXME: Need to consider all cases... PVMOVE, etc
+ */
+ if ((lv->status & PVMOVE) || !lv_active)
+ continue;
+
+ if (!monitor_dev_for_events(cmd, lv, 0, reg)) {
+ r = 0;
+ continue;
+ } else
+ (*count)++;
+ }
+
+ return r;
+}
+
+static int _poll_lvs_in_vg(struct cmd_context *cmd,
+ struct volume_group *vg)
+{
+ struct lv_list *lvl;
+ struct logical_volume *lv;
+ struct lvinfo info;
+ int lv_active;
+ int count = 0;
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ lv = lvl->lv;
+
+ if (!lv_info(cmd, lv, 0, &info, 0, 0))
+ lv_active = 0;
+ else
+ lv_active = info.exists;
+
+ if (lv_active &&
+ (lv->status & (PVMOVE|CONVERTING|MERGING))) {
+ lv_spawn_background_polling(cmd, lv);
+ count++;
+ }
+ }
+
+ /*
+ * returns the number of polled devices
+ * - there is no way to know if lv is already being polled
+ */
+
+ return count;
+}
+
+static int _activate_lvs_in_vg(struct cmd_context *cmd,
+ struct volume_group *vg, int activate)
+{
+ struct lv_list *lvl;
+ struct logical_volume *lv;
+ int count = 0, expected_count = 0;
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ lv = lvl->lv;
+
+ if (!lv_is_visible(lv))
+ continue;
+
+ /* Only request activation of snapshot origin devices */
+ if ((lv->status & SNAPSHOT) || lv_is_cow(lv))
+ continue;
+
+ /* Only request activation of mirror LV */
+ if ((lv->status & MIRROR_IMAGE) || (lv->status & MIRROR_LOG))
+ continue;
+
+ /* Only request activation of the first replicator-dev LV */
+ /* Avoids retry with all heads in case of failure */
+ if (lv_is_replicator_dev(lv) && (lv != first_replicator_dev(lv)))
+ continue;
+
+ /* Can't deactivate a pvmove LV */
+ /* FIXME There needs to be a controlled way of doing this */
+ if (((activate == CHANGE_AN) || (activate == CHANGE_ALN)) &&
+ ((lv->status & PVMOVE) ))
+ continue;
+
+ expected_count++;
+
+ if (activate == CHANGE_AN) {
+ if (!deactivate_lv(cmd, lv)) {
+ stack;
+ continue;
+ }
+ } else if (activate == CHANGE_ALN) {
+ if (!deactivate_lv_local(cmd, lv)) {
+ stack;
+ continue;
+ }
+ } else if (lv_is_origin(lv) || (activate == CHANGE_AE)) {
+ if (!activate_lv_excl(cmd, lv)) {
+ stack;
+ continue;
+ }
+ } else if (activate == CHANGE_ALY) {
+ if (!activate_lv_local(cmd, lv)) {
+ stack;
+ continue;
+ }
+ } else if (!activate_lv(cmd, lv)) {
+ stack;
+ continue;
+ }
+
+ if (background_polling() &&
+ activate != CHANGE_AN && activate != CHANGE_ALN &&
+ (lv->status & (PVMOVE|CONVERTING|MERGING)))
+ lv_spawn_background_polling(cmd, lv);
+
+ count++;
+ }
+
+ if (expected_count)
+ log_verbose("%s %d logical volumes in volume group %s",
+ (activate == CHANGE_AN || activate == CHANGE_ALN)?
+ "Deactivated" : "Activated", count, vg->name);
+
+ return (expected_count != count) ? 0 : 1;
+}
+
+static int _vgchange_monitoring(struct cmd_context *cmd, struct volume_group *vg)
+{
+ int r = 1;
+ int monitored = 0;
+
+ if (lvs_in_vg_activated(vg) &&
+ dmeventd_monitor_mode() != DMEVENTD_MONITOR_IGNORE) {
+ if (!_monitor_lvs_in_vg(cmd, vg, dmeventd_monitor_mode(), &monitored))
+ r = 0;
+ log_print("%d logical volume(s) in volume group "
+ "\"%s\" %smonitored",
+ monitored, vg->name, (dmeventd_monitor_mode()) ? "" : "un");
+ }
+
+ return r;
+}
+
+static int _vgchange_background_polling(struct cmd_context *cmd, struct volume_group *vg)
+{
+ int polled;
+
+ if (lvs_in_vg_activated(vg) && background_polling()) {
+ polled = _poll_lvs_in_vg(cmd, vg);
+ if (polled)
+ log_print("Background polling started for %d logical volume(s) "
+ "in volume group \"%s\"",
+ polled, vg->name);
+ }
+
+ return 1;
+}
+
+static int _vgchange_available(struct cmd_context *cmd, struct volume_group *vg)
+{
+ int lv_open, active, monitored = 0;
+ int available, r = 1;
+ int activate = 1;
+
+ /*
+ * Safe, since we never write out new metadata here. Required for
+ * partial activation to work.
+ */
+ cmd->handles_missing_pvs = 1;
+
+ available = arg_uint_value(cmd, available_ARG, 0);
+
+ if ((available == CHANGE_AN) || (available == CHANGE_ALN))
+ activate = 0;
+
+ /* FIXME: Force argument to deactivate them? */
+ if (!activate && (lv_open = lvs_in_vg_opened(vg))) {
+ log_error("Can't deactivate volume group \"%s\" with %d open "
+ "logical volume(s)", vg->name, lv_open);
+ return 0;
+ }
+
+ /* FIXME Move into library where clvmd can use it */
+ if (activate)
+ check_current_backup(vg);
+
+ if (activate && (active = lvs_in_vg_activated(vg))) {
+ log_verbose("%d logical volume(s) in volume group \"%s\" "
+ "already active", active, vg->name);
+ if (dmeventd_monitor_mode() != DMEVENTD_MONITOR_IGNORE) {
+ if (!_monitor_lvs_in_vg(cmd, vg, dmeventd_monitor_mode(), &monitored))
+ r = 0;
+ log_verbose("%d existing logical volume(s) in volume "
+ "group \"%s\" %smonitored",
+ monitored, vg->name,
+ dmeventd_monitor_mode() ? "" : "un");
+ }
+ }
+
+ if (!_activate_lvs_in_vg(cmd, vg, available))
+ r = 0;
+
+ /* Print message only if there was not found a missing VG */
+ if (!vg->cmd_missing_vgs)
+ log_print("%d logical volume(s) in volume group \"%s\" now active",
+ lvs_in_vg_activated(vg), vg->name);
+ return r;
+}
+
+static int _vgchange_refresh(struct cmd_context *cmd, struct volume_group *vg)
+{
+ log_verbose("Refreshing volume group \"%s\"", vg->name);
+
+ if (!vg_refresh_visible(cmd, vg)) {
+ stack;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _vgchange_alloc(struct cmd_context *cmd, struct volume_group *vg)
+{
+ alloc_policy_t alloc;
+
+ alloc = arg_uint_value(cmd, alloc_ARG, ALLOC_NORMAL);
+
+ /* FIXME: make consistent with vg_set_alloc_policy() */
+ if (alloc == vg->alloc) {
+ log_error("Volume group allocation policy is already %s",
+ get_alloc_string(vg->alloc));
+ return 0;
+ }
+
+ if (!vg_set_alloc_policy(vg, alloc))
+ return_0;
+
+ return 1;
+}
+
+static int _vgchange_resizeable(struct cmd_context *cmd,
+ struct volume_group *vg)
+{
+ int resizeable = !strcmp(arg_str_value(cmd, resizeable_ARG, "n"), "y");
+
+ if (resizeable && vg_is_resizeable(vg)) {
+ log_error("Volume group \"%s\" is already resizeable",
+ vg->name);
+ return 0;
+ }
+
+ if (!resizeable && !vg_is_resizeable(vg)) {
+ log_error("Volume group \"%s\" is already not resizeable",
+ vg->name);
+ return 0;
+ }
+
+ if (resizeable)
+ vg->status |= RESIZEABLE_VG;
+ else
+ vg->status &= ~RESIZEABLE_VG;
+
+ return 1;
+}
+
+static int _vgchange_clustered(struct cmd_context *cmd,
+ struct volume_group *vg)
+{
+ int clustered = !strcmp(arg_str_value(cmd, clustered_ARG, "n"), "y");
+
+ if (clustered && (vg_is_clustered(vg))) {
+ log_error("Volume group \"%s\" is already clustered",
+ vg->name);
+ return 0;
+ }
+
+ if (!clustered && !(vg_is_clustered(vg))) {
+ log_error("Volume group \"%s\" is already not clustered",
+ vg->name);
+ return 0;
+ }
+
+ if (!vg_set_clustered(vg, clustered))
+ return_0;
+
+ return 1;
+}
+
+static int _vgchange_logicalvolume(struct cmd_context *cmd,
+ struct volume_group *vg)
+{
+ uint32_t max_lv = arg_uint_value(cmd, logicalvolume_ARG, 0);
+
+ if (!vg_set_max_lv(vg, max_lv))
+ return_0;
+
+ return 1;
+}
+
+static int _vgchange_physicalvolumes(struct cmd_context *cmd,
+ struct volume_group *vg)
+{
+ uint32_t max_pv = arg_uint_value(cmd, maxphysicalvolumes_ARG, 0);
+
+ if (!vg_set_max_pv(vg, max_pv))
+ return_0;
+
+ return 1;
+}
+
+static int _vgchange_pesize(struct cmd_context *cmd, struct volume_group *vg)
+{
+ uint32_t extent_size;
+
+ extent_size = arg_uint_value(cmd, physicalextentsize_ARG, 0);
+ /* FIXME: remove check - redundant with vg_change_pesize */
+ if (extent_size == vg->extent_size) {
+ log_error("Physical extent size of VG %s is already %s",
+ vg->name, display_size(cmd, (uint64_t) extent_size));
+ return 1;
+ }
+
+ if (!vg_set_extent_size(vg, extent_size))
+ return_0;
+
+ return 1;
+}
+
+static int _vgchange_tag(struct cmd_context *cmd, struct volume_group *vg,
+ int arg)
+{
+ const char *tag;
+ struct arg_value_group_list *current_group;
+
+ dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
+ if (!grouped_arg_is_set(current_group->arg_values, arg))
+ continue;
+
+ if (!(tag = grouped_arg_str_value(current_group->arg_values, arg, NULL))) {
+ log_error("Failed to get tag");
+ return 0;
+ }
+
+ if (!vg_change_tag(vg, tag, arg == addtag_ARG))
+ return_0;
+
+ }
+
+ return 1;
+}
+
+static int _vgchange_addtag(struct cmd_context *cmd, struct volume_group *vg)
+{
+ return _vgchange_tag(cmd, vg, addtag_ARG);
+}
+
+static int _vgchange_deltag(struct cmd_context *cmd, struct volume_group *vg)
+{
+ return _vgchange_tag(cmd, vg, deltag_ARG);
+}
+
+static int _vgchange_uuid(struct cmd_context *cmd __attribute__((unused)),
+ struct volume_group *vg)
+{
+ struct lv_list *lvl;
+
+ if (lvs_in_vg_activated(vg)) {
+ log_error("Volume group has active logical volumes");
+ return 0;
+ }
+
+ if (!id_create(&vg->id)) {
+ log_error("Failed to generate new random UUID for VG %s.",
+ vg->name);
+ return 0;
+ }
+
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ memcpy(&lvl->lv->lvid, &vg->id, sizeof(vg->id));
+ }
+
+ return 1;
+}
+
+static int _vgchange_metadata_copies(struct cmd_context *cmd,
+ struct volume_group *vg)
+{
+ uint32_t mda_copies = arg_uint_value(cmd, vgmetadatacopies_ARG, DEFAULT_VGMETADATACOPIES);
+
+ if (mda_copies == vg_mda_copies(vg)) {
+ if (vg_mda_copies(vg) == VGMETADATACOPIES_UNMANAGED)
+ log_error("Number of metadata copies for VG %s is already unmanaged.",
+ vg->name);
+ else
+ log_error("Number of metadata copies for VG %s is already %" PRIu32,
+ vg->name, mda_copies);
+ return 1;
+ }
+
+ if (!vg_set_mda_copies(vg, mda_copies))
+ return_0;
+
+ return 1;
+}
+
+static int vgchange_single(struct cmd_context *cmd, const char *vg_name,
+ struct volume_group *vg,
+ void *handle __attribute__((unused)))
+{
+ int dmeventd_mode;
+ int archived = 0;
+ int i;
+
+ static struct {
+ int arg;
+ int (*fn)(struct cmd_context *cmd, struct volume_group *vg);
+ } _vgchange_args[] = {
+ { logicalvolume_ARG, &_vgchange_logicalvolume },
+ { maxphysicalvolumes_ARG, &_vgchange_physicalvolumes },
+ { resizeable_ARG, &_vgchange_resizeable },
+ { deltag_ARG, &_vgchange_deltag },
+ { addtag_ARG, &_vgchange_addtag },
+ { physicalextentsize_ARG, &_vgchange_pesize },
+ { uuid_ARG, &_vgchange_uuid },
+ { alloc_ARG, &_vgchange_alloc },
+ { clustered_ARG, &_vgchange_clustered },
+ { vgmetadatacopies_ARG, &_vgchange_metadata_copies },
+ { -1, NULL },
+ };
+
+ if (vg_is_exported(vg)) {
+ log_error("Volume group \"%s\" is exported", vg_name);
+ return ECMD_FAILED;
+ }
+
+ if (!get_activation_monitoring_mode(cmd, vg, &dmeventd_mode))
+ return ECMD_FAILED;
+
+ init_dmeventd_monitor(dmeventd_mode);
+
+ /*
+ * FIXME: DEFAULT_BACKGROUND_POLLING should be "unspecified".
+ * If --poll is explicitly provided use it; otherwise polling
+ * should only be started if the LV is not already active. So:
+ * 1) change the activation code to say if the LV was actually activated
+ * 2) make polling of an LV tightly coupled with LV activation
+ *
+ * Do not initiate any polling if --sysinit option is used.
+ */
+ init_background_polling(arg_count(cmd, sysinit_ARG) ? 0 :
+ arg_int_value(cmd, poll_ARG,
+ DEFAULT_BACKGROUND_POLLING));
+
+ for (i = 0; _vgchange_args[i].arg >= 0; i++) {
+ if (arg_count(cmd, _vgchange_args[i].arg)) {
+ if (!archived && !archive(vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ archived = 1;
+ if (!_vgchange_args[i].fn(cmd, vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ }
+ }
+
+ if (archived) {
+ if (!vg_write(vg) || !vg_commit(vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ backup(vg);
+
+ log_print("Volume group \"%s\" successfully changed", vg->name);
+ }
+
+ if (arg_count(cmd, available_ARG)) {
+ if (!_vgchange_available(cmd, vg))
+ return ECMD_FAILED;
+ }
+
+ if (arg_count(cmd, refresh_ARG)) {
+ /* refreshes the visible LVs (which starts polling) */
+ if (!_vgchange_refresh(cmd, vg))
+ return ECMD_FAILED;
+ }
+
+ if (!arg_count(cmd, available_ARG) &&
+ !arg_count(cmd, refresh_ARG) &&
+ arg_count(cmd, monitor_ARG)) {
+ /* -ay* will have already done monitoring changes */
+ if (!_vgchange_monitoring(cmd, vg))
+ return ECMD_FAILED;
+ }
+
+ if (!arg_count(cmd, refresh_ARG) &&
+ arg_count(cmd, poll_ARG))
+ if (!_vgchange_background_polling(cmd, vg))
+ return ECMD_FAILED;
+
+ return ECMD_PROCESSED;
+}
+
+int vgchange(struct cmd_context *cmd, int argc, char **argv)
+{
+ /* Update commands that can be combined */
+ int update =
+ arg_count(cmd, logicalvolume_ARG) ||
+ arg_count(cmd, maxphysicalvolumes_ARG) ||
+ arg_count(cmd, resizeable_ARG) ||
+ arg_count(cmd, deltag_ARG) ||
+ arg_count(cmd, addtag_ARG) ||
+ arg_count(cmd, uuid_ARG) ||
+ arg_count(cmd, physicalextentsize_ARG) ||
+ arg_count(cmd, clustered_ARG) ||
+ arg_count(cmd, alloc_ARG) ||
+ arg_count(cmd, vgmetadatacopies_ARG);
+
+ if (!update &&
+ !arg_count(cmd, available_ARG) &&
+ !arg_count(cmd, monitor_ARG) &&
+ !arg_count(cmd, poll_ARG) &&
+ !arg_count(cmd, refresh_ARG)) {
+ log_error("Need 1 or more of -a, -c, -l, -p, -s, -x, "
+ "--refresh, --uuid, --alloc, --addtag, --deltag, "
+ "--monitor, --poll, --vgmetadatacopies or "
+ "--metadatacopies");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, available_ARG) && arg_count(cmd, refresh_ARG)) {
+ log_error("Only one of -a and --refresh permitted.");
+ return EINVALID_CMD_LINE;
+ }
+
+ if ((arg_count(cmd, ignorelockingfailure_ARG) ||
+ arg_count(cmd, sysinit_ARG)) && update) {
+ log_error("Only -a permitted with --ignorelockingfailure and --sysinit");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, available_ARG) &&
+ (arg_count(cmd, monitor_ARG) || arg_count(cmd, poll_ARG))) {
+ int activate = arg_uint_value(cmd, available_ARG, 0);
+ if (activate == CHANGE_AN || activate == CHANGE_ALN) {
+ log_error("Only -ay* allowed with --monitor or --poll.");
+ return EINVALID_CMD_LINE;
+ }
+ }
+
+ if (arg_count(cmd, poll_ARG) && arg_count(cmd, sysinit_ARG)) {
+ log_error("Only one of --poll and --sysinit permitted.");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, available_ARG) == 1
+ && arg_count(cmd, autobackup_ARG)) {
+ log_error("-A option not necessary with -a option");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, maxphysicalvolumes_ARG) &&
+ arg_sign_value(cmd, maxphysicalvolumes_ARG, 0) == SIGN_MINUS) {
+ log_error("MaxPhysicalVolumes may not be negative");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, physicalextentsize_ARG) &&
+ arg_sign_value(cmd, physicalextentsize_ARG, 0) == SIGN_MINUS) {
+ log_error("Physical extent size may not be negative");
+ return EINVALID_CMD_LINE;
+ }
+
+ return process_each_vg(cmd, argc, argv, update ? READ_FOR_UPDATE : 0,
+ NULL, &vgchange_single);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "metadata.h"
+
+static int vgck_single(struct cmd_context *cmd __attribute__((unused)),
+ const char *vg_name,
+ struct volume_group *vg,
+ void *handle __attribute__((unused)))
+{
+ if (!vg_check_status(vg, EXPORTED_VG)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (!vg_validate(vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (vg_missing_pv_count(vg)) {
+ log_error("The volume group is missing %d physical volumes.",
+ vg_missing_pv_count(vg));
+ return ECMD_FAILED;
+ }
+
+ return ECMD_PROCESSED;
+}
+
+int vgck(struct cmd_context *cmd, int argc, char **argv)
+{
+ return process_each_vg(cmd, argc, argv, 0, NULL,
+ &vgck_single);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static int vgconvert_single(struct cmd_context *cmd, const char *vg_name,
+ struct volume_group *vg,
+ void *handle __attribute__((unused)))
+{
+ struct physical_volume *pv, *existing_pv;
+ struct logical_volume *lv;
+ struct lv_list *lvl;
+ uint64_t size = 0;
+ struct dm_list mdas;
+ int pvmetadatacopies = 0;
+ uint64_t pvmetadatasize = 0;
+ uint64_t pe_end = 0, pe_start = 0;
+ struct pv_list *pvl;
+ int change_made = 0;
+ struct lvinfo info;
+ int active = 0;
+
+ if (!vg_check_status(vg, LVM_WRITE | EXPORTED_VG)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (vg->fid->fmt == cmd->fmt) {
+ log_error("Volume group \"%s\" already uses format %s",
+ vg_name, cmd->fmt->name);
+ return ECMD_FAILED;
+ }
+
+ if (cmd->fmt->features & FMT_MDAS) {
+ if (arg_sign_value(cmd, metadatasize_ARG, 0) == SIGN_MINUS) {
+ log_error("Metadata size may not be negative");
+ return EINVALID_CMD_LINE;
+ }
+
+ pvmetadatasize = arg_uint64_value(cmd, metadatasize_ARG,
+ UINT64_C(0));
+ if (!pvmetadatasize)
+ pvmetadatasize =
+ find_config_tree_int(cmd,
+ "metadata/pvmetadatasize",
+ DEFAULT_PVMETADATASIZE);
+
+ pvmetadatacopies = arg_int_value(cmd, pvmetadatacopies_ARG, -1);
+ if (pvmetadatacopies < 0)
+ pvmetadatacopies =
+ find_config_tree_int(cmd,
+ "metadata/pvmetadatacopies",
+ DEFAULT_PVMETADATACOPIES);
+ }
+
+ if (!archive(vg)) {
+ log_error("Archive of \"%s\" metadata failed.", vg_name);
+ return ECMD_FAILED;
+ }
+
+ /* Set PV/LV limit if converting from unlimited metadata format */
+ if (vg->fid->fmt->features & FMT_UNLIMITED_VOLS &&
+ !(cmd->fmt->features & FMT_UNLIMITED_VOLS)) {
+ if (!vg->max_lv)
+ vg->max_lv = 255;
+ if (!vg->max_pv)
+ vg->max_pv = 255;
+ }
+
+ /* If converting to restricted lvid, check if lvid is compatible */
+ if (!(vg->fid->fmt->features & FMT_RESTRICTED_LVIDS) &&
+ cmd->fmt->features & FMT_RESTRICTED_LVIDS)
+ dm_list_iterate_items(lvl, &vg->lvs)
+ if (!lvid_in_restricted_range(&lvl->lv->lvid)) {
+ log_error("Logical volume %s lvid format is"
+ " incompatible with requested"
+ " metadata format.", lvl->lv->name);
+ return ECMD_FAILED;
+ }
+
+ /* Attempt to change any LVIDs that are too big */
+ if (cmd->fmt->features & FMT_RESTRICTED_LVIDS) {
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ lv = lvl->lv;
+ if (lv->status & SNAPSHOT)
+ continue;
+ if (lvnum_from_lvid(&lv->lvid) < MAX_RESTRICTED_LVS)
+ continue;
+ if (lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) {
+ log_error("Logical volume %s must be "
+ "deactivated before conversion.",
+ lv->name);
+ active++;
+ continue;
+ }
+ lvid_from_lvnum(&lv->lvid, &lv->vg->id, find_free_lvnum(lv));
+
+ }
+ }
+
+ if (active) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ existing_pv = pvl->pv;
+
+ pe_start = pv_pe_start(existing_pv);
+ pe_end = pv_pe_count(existing_pv) * pv_pe_size(existing_pv)
+ + pe_start - 1;
+
+ dm_list_init(&mdas);
+ if (!(pv = pv_create(cmd, pv_dev(existing_pv),
+ &existing_pv->id, size, 0, 0,
+ pe_start, pv_pe_count(existing_pv),
+ pv_pe_size(existing_pv), pvmetadatacopies,
+ pvmetadatasize, 0, &mdas))) {
+ log_error("Failed to setup physical volume \"%s\"",
+ pv_dev_name(existing_pv));
+ if (change_made)
+ log_error("Use pvcreate and vgcfgrestore to "
+ "repair from archived metadata.");
+ return ECMD_FAILED;
+ }
+
+ /* Need to revert manually if it fails after this point */
+ change_made = 1;
+
+ log_verbose("Set up physical volume for \"%s\" with %" PRIu64
+ " available sectors", pv_dev_name(pv), pv_size(pv));
+
+ /* Wipe existing label first */
+ if (!label_remove(pv_dev(pv))) {
+ log_error("Failed to wipe existing label on %s",
+ pv_dev_name(pv));
+ log_error("Use pvcreate and vgcfgrestore to repair "
+ "from archived metadata.");
+ return ECMD_FAILED;
+ }
+
+ log_very_verbose("Writing physical volume data to disk \"%s\"",
+ pv_dev_name(pv));
+ if (!(pv_write(cmd, pv, &mdas,
+ arg_int64_value(cmd, labelsector_ARG,
+ DEFAULT_LABELSECTOR)))) {
+ log_error("Failed to write physical volume \"%s\"",
+ pv_dev_name(pv));
+ log_error("Use pvcreate and vgcfgrestore to repair "
+ "from archived metadata.");
+ return ECMD_FAILED;
+ }
+ log_verbose("Physical volume \"%s\" successfully created",
+ pv_dev_name(pv));
+
+ }
+
+ log_verbose("Deleting existing metadata for VG %s", vg_name);
+ if (!vg_remove_mdas(vg)) {
+ log_error("Removal of existing metadata for %s failed.",
+ vg_name);
+ log_error("Use pvcreate and vgcfgrestore to repair "
+ "from archived metadata.");
+ return ECMD_FAILED;
+ }
+
+ /* FIXME Cache the label format change so we don't have to skip this */
+ if (test_mode()) {
+ log_verbose("Test mode: Skipping metadata writing for VG %s in"
+ " format %s", vg_name, cmd->fmt->name);
+ return ECMD_PROCESSED;
+ }
+
+ log_verbose("Writing metadata for VG %s using format %s", vg_name,
+ cmd->fmt->name);
+ if (!backup_restore_vg(cmd, vg)) {
+ log_error("Conversion failed for volume group %s.", vg_name);
+ log_error("Use pvcreate and vgcfgrestore to repair from "
+ "archived metadata.");
+ return ECMD_FAILED;
+ }
+ log_print("Volume group %s successfully converted", vg_name);
+
+ backup(vg);
+
+ return ECMD_PROCESSED;
+}
+
+int vgconvert(struct cmd_context *cmd, int argc, char **argv)
+{
+ if (!argc) {
+ log_error("Please enter volume group(s)");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_int_value(cmd, labelsector_ARG, 0) >= LABEL_SCAN_SECTORS) {
+ log_error("labelsector must be less than %lu",
+ LABEL_SCAN_SECTORS);
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, metadatacopies_ARG)) {
+ log_error("Invalid option --metadatacopies, "
+ "use --pvmetadatacopies instead.");
+ return EINVALID_CMD_LINE;
+ }
+ if (!(cmd->fmt->features & FMT_MDAS) &&
+ (arg_count(cmd, pvmetadatacopies_ARG) ||
+ arg_count(cmd, metadatasize_ARG))) {
+ log_error("Metadata parameters only apply to text format");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, pvmetadatacopies_ARG) &&
+ arg_int_value(cmd, pvmetadatacopies_ARG, -1) > 2) {
+ log_error("Metadatacopies may only be 0, 1 or 2");
+ return EINVALID_CMD_LINE;
+ }
+
+ return process_each_vg(cmd, argc, argv, READ_FOR_UPDATE, NULL,
+ &vgconvert_single);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+int vgcreate(struct cmd_context *cmd, int argc, char **argv)
+{
+ struct vgcreate_params vp_new;
+ struct vgcreate_params vp_def;
+ struct volume_group *vg;
+ const char *tag;
+ const char *clustered_message = "";
+ char *vg_name;
+ struct pvcreate_params pp;
+ struct arg_value_group_list *current_group;
+
+ if (!argc) {
+ log_error("Please provide volume group name and "
+ "physical volumes");
+ return EINVALID_CMD_LINE;
+ }
+
+ vg_name = argv[0];
+ argc--;
+ argv++;
+
+ pvcreate_params_set_defaults(&pp);
+ if (!pvcreate_params_validate(cmd, argc, argv, &pp)) {
+ return EINVALID_CMD_LINE;
+ }
+
+ vgcreate_params_set_defaults(&vp_def, NULL);
+ vp_def.vg_name = vg_name;
+ if (vgcreate_params_set_from_args(cmd, &vp_new, &vp_def))
+ return EINVALID_CMD_LINE;
+
+ if (vgcreate_params_validate(cmd, &vp_new))
+ return EINVALID_CMD_LINE;
+
+ /* Create the new VG */
+ vg = vg_create(cmd, vp_new.vg_name);
+ if (vg_read_error(vg)) {
+ if (vg_read_error(vg) == FAILED_EXIST)
+ log_error("A volume group called %s already exists.", vp_new.vg_name);
+ else
+ log_error("Can't get lock for %s.", vp_new.vg_name);
+ free_vg(vg);
+ return ECMD_FAILED;
+ }
+
+ if (!vg_set_extent_size(vg, vp_new.extent_size) ||
+ !vg_set_max_lv(vg, vp_new.max_lv) ||
+ !vg_set_max_pv(vg, vp_new.max_pv) ||
+ !vg_set_alloc_policy(vg, vp_new.alloc) ||
+ !vg_set_clustered(vg, vp_new.clustered) ||
+ !vg_set_mda_copies(vg, vp_new.vgmetadatacopies))
+ goto bad_orphan;
+
+ if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+ log_error("Can't get lock for orphan PVs");
+ goto bad_orphan;
+ }
+
+ /* attach the pv's */
+ if (!vg_extend(vg, argc, argv, &pp))
+ goto_bad;
+
+ if (vp_new.max_lv != vg->max_lv)
+ log_warn("WARNING: Setting maxlogicalvolumes to %d "
+ "(0 means unlimited)", vg->max_lv);
+
+ if (vp_new.max_pv != vg->max_pv)
+ log_warn("WARNING: Setting maxphysicalvolumes to %d "
+ "(0 means unlimited)", vg->max_pv);
+
+ if (arg_count(cmd, addtag_ARG)) {
+ dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
+ if (!grouped_arg_is_set(current_group->arg_values, addtag_ARG))
+ continue;
+
+ if (!(tag = grouped_arg_str_value(current_group->arg_values, addtag_ARG, NULL))) {
+ log_error("Failed to get tag");
+ goto bad;
+ }
+
+ if (!vg_change_tag(vg, tag, 1))
+ goto_bad;
+ }
+ }
+
+ if (vg_is_clustered(vg))
+ clustered_message = "Clustered ";
+ else if (locking_is_clustered())
+ clustered_message = "Non-clustered ";
+
+ if (!archive(vg))
+ goto_bad;
+
+ /* Store VG on disk(s) */
+ if (!vg_write(vg) || !vg_commit(vg))
+ goto_bad;
+
+ unlock_vg(cmd, VG_ORPHANS);
+ unlock_vg(cmd, vp_new.vg_name);
+
+ backup(vg);
+
+ log_print("%s%colume group \"%s\" successfully created",
+ clustered_message, *clustered_message ? 'v' : 'V', vg->name);
+
+ free_vg(vg);
+ return ECMD_PROCESSED;
+
+bad:
+ unlock_vg(cmd, VG_ORPHANS);
+bad_orphan:
+ free_vg(vg);
+ unlock_vg(cmd, vp_new.vg_name);
+ return ECMD_FAILED;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static int vgdisplay_single(struct cmd_context *cmd, const char *vg_name,
+ struct volume_group *vg,
+ void *handle __attribute__((unused)))
+{
+ /* FIXME Do the active check here if activevolumegroups_ARG ? */
+ vg_check_status(vg, EXPORTED_VG);
+
+ if (arg_count(cmd, colon_ARG)) {
+ vgdisplay_colons(vg);
+ return ECMD_PROCESSED;
+ }
+
+ if (arg_count(cmd, short_ARG)) {
+ vgdisplay_short(vg);
+ return ECMD_PROCESSED;
+ }
+
+ vgdisplay_full(vg); /* was vg_show */
+
+ if (arg_count(cmd, verbose_ARG)) {
+ vgdisplay_extents(vg);
+
+ process_each_lv_in_vg(cmd, vg, NULL, NULL, NULL, NULL,
+ (process_single_lv_fn_t)lvdisplay_full);
+
+ log_print("--- Physical volumes ---");
+ process_each_pv_in_vg(cmd, vg, NULL, NULL,
+ (process_single_pv_fn_t)pvdisplay_short);
+ }
+
+ check_current_backup(vg);
+
+ return ECMD_PROCESSED;
+}
+
+int vgdisplay(struct cmd_context *cmd, int argc, char **argv)
+{
+ if (arg_count(cmd, columns_ARG)) {
+ if (arg_count(cmd, colon_ARG) ||
+ arg_count(cmd, activevolumegroups_ARG) ||
+ arg_count(cmd, short_ARG)) {
+ log_error("Incompatible options selected");
+ return EINVALID_CMD_LINE;
+ }
+ return vgs(cmd, argc, argv);
+ } else if (arg_count(cmd, aligned_ARG) ||
+ arg_count(cmd, noheadings_ARG) ||
+ arg_count(cmd, options_ARG) ||
+ arg_count(cmd, separator_ARG) ||
+ arg_count(cmd, sort_ARG) || arg_count(cmd, unbuffered_ARG)) {
+ log_error("Incompatible options selected");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, colon_ARG) && arg_count(cmd, short_ARG)) {
+ log_error("Option -c is not allowed with option -s");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (argc && arg_count(cmd, activevolumegroups_ARG)) {
+ log_error("Option -A is not allowed with volume group names");
+ return EINVALID_CMD_LINE;
+ }
+
+/********* FIXME: Do without this - or else 2(+) passes!
+ Figure out longest volume group name
+ for (c = opt; opt < argc; opt++) {
+ len = strlen(argv[opt]);
+ if (len > max_len)
+ max_len = len;
+ }
+**********/
+
+ return process_each_vg(cmd, argc, argv, 0, NULL,
+ vgdisplay_single);
+
+/******** FIXME Need to count number processed
+ Add this to process_each_vg if arg_count(cmd,activevolumegroups_ARG) ?
+
+ if (opt == argc) {
+ log_print("no ");
+ if (arg_count(cmd,activevolumegroups_ARG))
+ printf("active ");
+ printf("volume groups found\n\n");
+ return LVM_E_NO_VG;
+ }
+************/
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static int vgexport_single(struct cmd_context *cmd __attribute__((unused)),
+ const char *vg_name,
+ struct volume_group *vg,
+ void *handle __attribute__((unused)))
+{
+ struct pv_list *pvl;
+ struct physical_volume *pv;
+
+ if (lvs_in_vg_activated(vg)) {
+ log_error("Volume group \"%s\" has active logical volumes",
+ vg_name);
+ goto bad;
+ }
+
+ if (!archive(vg))
+ goto_bad;
+
+ vg->status |= EXPORTED_VG;
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ pv = pvl->pv;
+ pv->status |= EXPORTED_VG;
+ }
+
+ if (!vg_write(vg) || !vg_commit(vg))
+ goto_bad;
+
+ backup(vg);
+
+ log_print("Volume group \"%s\" successfully exported", vg->name);
+
+ return ECMD_PROCESSED;
+
+bad:
+ return ECMD_FAILED;
+}
+
+int vgexport(struct cmd_context *cmd, int argc, char **argv)
+{
+ if (!argc && !arg_count(cmd, all_ARG)) {
+ log_error("Please supply volume groups or use -a for all.");
+ return ECMD_FAILED;
+ }
+
+ if (argc && arg_count(cmd, all_ARG)) {
+ log_error("No arguments permitted when using -a for all.");
+ return ECMD_FAILED;
+ }
+
+ return process_each_vg(cmd, argc, argv, READ_FOR_UPDATE, NULL,
+ &vgexport_single);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static int _restore_pv(struct volume_group *vg, char *pv_name)
+{
+ struct pv_list *pvl = NULL;
+ pvl = find_pv_in_vg(vg, pv_name);
+ if (!pvl) {
+ log_warn("WARNING: PV %s not found in VG %s", pv_name, vg->name);
+ return 0;
+ }
+
+ if (!(pvl->pv->status & MISSING_PV)) {
+ log_warn("WARNING: PV %s was not missing in VG %s", pv_name, vg->name);
+ return 0;
+ }
+
+ if (!pvl->pv->dev) {
+ log_warn("WARNING: The PV %s is still missing.", pv_name);
+ return 0;
+ }
+
+ pvl->pv->status &= ~MISSING_PV;
+ return 1;
+}
+
+int vgextend(struct cmd_context *cmd, int argc, char **argv)
+{
+ char *vg_name;
+ struct volume_group *vg = NULL;
+ int r = ECMD_FAILED;
+ struct pvcreate_params pp;
+ int fixed = 0, i = 0;
+
+ if (!argc) {
+ log_error("Please enter volume group name and "
+ "physical volume(s)");
+ return EINVALID_CMD_LINE;
+ }
+
+ vg_name = skip_dev_dir(cmd, argv[0], NULL);
+ argc--;
+ argv++;
+
+ if (arg_count(cmd, metadatacopies_ARG)) {
+ log_error("Invalid option --metadatacopies, "
+ "use --pvmetadatacopies instead.");
+ return EINVALID_CMD_LINE;
+ }
+ pvcreate_params_set_defaults(&pp);
+ if (!pvcreate_params_validate(cmd, argc, argv, &pp)) {
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, restoremissing_ARG))
+ cmd->handles_missing_pvs = 1;
+
+ log_verbose("Checking for volume group \"%s\"", vg_name);
+ vg = vg_read_for_update(cmd, vg_name, NULL, 0);
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (!archive(vg))
+ goto_bad;
+
+ if (arg_count(cmd, restoremissing_ARG)) {
+ for (i = 0; i < argc; ++i) {
+ if (_restore_pv(vg, argv[i]))
+ ++ fixed;
+ }
+ if (!fixed) {
+ log_error("No PV has been restored.");
+ goto_bad;
+ }
+ } else { /* no --restore, normal vgextend */
+ if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+ log_error("Can't get lock for orphan PVs");
+ unlock_and_free_vg(cmd, vg, vg_name);
+ return ECMD_FAILED;
+ }
+
+ if (arg_count(cmd, metadataignore_ARG) &&
+ (vg_mda_copies(vg) != VGMETADATACOPIES_UNMANAGED) &&
+ (pp.force == PROMPT) &&
+ yes_no_prompt("Override preferred number of copies "
+ "of VG %s metadata? [y/n]: ",
+ vg_name) == 'n') {
+ log_error("Volume group %s not changed", vg_name);
+ goto_bad;
+ }
+
+ /* extend vg */
+ if (!vg_extend(vg, argc, argv, &pp))
+ goto_bad;
+
+ if (arg_count(cmd, metadataignore_ARG) &&
+ (vg_mda_copies(vg) != VGMETADATACOPIES_UNMANAGED) &&
+ (vg_mda_copies(vg) != vg_mda_used_count(vg))) {
+ log_warn("WARNING: Changing preferred number of copies of VG %s "
+ "metadata from %"PRIu32" to %"PRIu32, vg_name,
+ vg_mda_copies(vg), vg_mda_used_count(vg));
+ vg_set_mda_copies(vg, vg_mda_used_count(vg));
+ }
+
+ /* ret > 0 */
+ log_verbose("Volume group \"%s\" will be extended by %d new "
+ "physical volumes", vg_name, argc);
+ }
+
+ /* store vg on disk(s) */
+ if (!vg_write(vg) || !vg_commit(vg))
+ goto_bad;
+
+ backup(vg);
+ log_print("Volume group \"%s\" successfully extended", vg_name);
+ r = ECMD_PROCESSED;
+
+bad:
+ if (!arg_count(cmd, restoremissing_ARG))
+ unlock_vg(cmd, VG_ORPHANS);
+ unlock_and_free_vg(cmd, vg, vg_name);
+ return r;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static int vgimport_single(struct cmd_context *cmd __attribute__((unused)),
+ const char *vg_name,
+ struct volume_group *vg,
+ void *handle __attribute__((unused)))
+{
+ struct pv_list *pvl;
+ struct physical_volume *pv;
+
+ if (!vg_is_exported(vg)) {
+ log_error("Volume group \"%s\" is not exported", vg_name);
+ goto bad;
+ }
+
+ if (vg_status(vg) & PARTIAL_VG) {
+ log_error("Volume group \"%s\" is partially missing", vg_name);
+ goto bad;
+ }
+
+ if (!archive(vg))
+ goto_bad;
+
+ vg->status &= ~EXPORTED_VG;
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ pv = pvl->pv;
+ pv->status &= ~EXPORTED_VG;
+ }
+
+ if (!vg_write(vg) || !vg_commit(vg))
+ goto_bad;
+
+ backup(vg);
+
+ log_print("Volume group \"%s\" successfully imported", vg->name);
+
+ return ECMD_PROCESSED;
+
+bad:
+ return ECMD_FAILED;
+}
+
+int vgimport(struct cmd_context *cmd, int argc, char **argv)
+{
+ if (!argc && !arg_count(cmd, all_ARG)) {
+ log_error("Please supply volume groups or use -a for all.");
+ return ECMD_FAILED;
+ }
+
+ if (argc && arg_count(cmd, all_ARG)) {
+ log_error("No arguments permitted when using -a for all.");
+ return ECMD_FAILED;
+ }
+
+ return process_each_vg(cmd, argc, argv,
+ READ_FOR_UPDATE | READ_ALLOW_EXPORTED,
+ NULL,
+ &vgimport_single);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static struct volume_group *_vgmerge_vg_read(struct cmd_context *cmd,
+ const char *vg_name)
+{
+ struct volume_group *vg;
+ log_verbose("Checking for volume group \"%s\"", vg_name);
+ vg = vg_read_for_update(cmd, vg_name, NULL, 0);
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ return NULL;
+ }
+ return vg;
+}
+
+static int _vgmerge_single(struct cmd_context *cmd, const char *vg_name_to,
+ const char *vg_name_from)
+{
+ struct pv_list *pvl, *tpvl;
+ struct volume_group *vg_to, *vg_from;
+ struct lv_list *lvl1, *lvl2;
+ int r = ECMD_FAILED;
+ int lock_vg_from_first = 0;
+
+ if (!strcmp(vg_name_to, vg_name_from)) {
+ log_error("Duplicate volume group name \"%s\"", vg_name_from);
+ return ECMD_FAILED;
+ }
+
+ if (strcmp(vg_name_to, vg_name_from) > 0)
+ lock_vg_from_first = 1;
+
+ if (lock_vg_from_first) {
+ vg_from = _vgmerge_vg_read(cmd, vg_name_from);
+ if (!vg_from) {
+ stack;
+ return ECMD_FAILED;
+ }
+ vg_to = _vgmerge_vg_read(cmd, vg_name_to);
+ if (!vg_to) {
+ stack;
+ unlock_and_free_vg(cmd, vg_from, vg_name_from);
+ return ECMD_FAILED;
+ }
+ } else {
+ vg_to = _vgmerge_vg_read(cmd, vg_name_to);
+ if (!vg_to) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ vg_from = _vgmerge_vg_read(cmd, vg_name_from);
+ if (!vg_from) {
+ stack;
+ unlock_and_free_vg(cmd, vg_to, vg_name_to);
+ return ECMD_FAILED;
+ }
+ }
+
+ if (!vgs_are_compatible(cmd, vg_from, vg_to))
+ goto_bad;
+
+ /* FIXME List arg: vg_show_with_pv_and_lv(vg_to); */
+
+ if (!archive(vg_from) || !archive(vg_to))
+ goto_bad;
+
+ drop_cached_metadata(vg_from);
+
+ /* Merge volume groups */
+ dm_list_iterate_items_safe(pvl, tpvl, &vg_from->pvs) {
+ del_pvl_from_vgs(vg_from, pvl);
+ add_pvl_to_vgs(vg_to, pvl);
+ pvl->pv->vg_name = dm_pool_strdup(cmd->mem, vg_to->name);
+ }
+
+ /* Fix up LVIDs */
+ dm_list_iterate_items(lvl1, &vg_to->lvs) {
+ union lvid *lvid1 = &lvl1->lv->lvid;
+ char uuid[64] __attribute__((aligned(8)));
+
+ dm_list_iterate_items(lvl2, &vg_from->lvs) {
+ union lvid *lvid2 = &lvl2->lv->lvid;
+
+ if (id_equal(&lvid1->id[1], &lvid2->id[1])) {
+ if (!id_create(&lvid2->id[1])) {
+ log_error("Failed to generate new "
+ "random LVID for %s",
+ lvl2->lv->name);
+ goto bad;
+ }
+ if (!id_write_format(&lvid2->id[1], uuid,
+ sizeof(uuid)))
+ goto_bad;
+
+ log_verbose("Changed LVID for %s to %s",
+ lvl2->lv->name, uuid);
+ }
+ }
+ }
+
+ dm_list_iterate_items(lvl1, &vg_from->lvs) {
+ lvl1->lv->vg = vg_to;
+ }
+
+ while (!dm_list_empty(&vg_from->lvs)) {
+ struct dm_list *lvh = vg_from->lvs.n;
+
+ dm_list_move(&vg_to->lvs, lvh);
+ }
+
+ while (!dm_list_empty(&vg_from->fid->metadata_areas_in_use)) {
+ struct dm_list *mdah = vg_from->fid->metadata_areas_in_use.n;
+
+ dm_list_move(&vg_to->fid->metadata_areas_in_use, mdah);
+ }
+
+ while (!dm_list_empty(&vg_from->fid->metadata_areas_ignored)) {
+ struct dm_list *mdah = vg_from->fid->metadata_areas_ignored.n;
+
+ dm_list_move(&vg_to->fid->metadata_areas_ignored, mdah);
+ }
+
+ vg_to->extent_count += vg_from->extent_count;
+ vg_to->free_count += vg_from->free_count;
+
+ /* store it on disks */
+ log_verbose("Writing out updated volume group");
+ if (!vg_write(vg_to) || !vg_commit(vg_to))
+ goto_bad;
+
+ /* FIXME Remove /dev/vgfrom */
+
+ backup(vg_to);
+ log_print("Volume group \"%s\" successfully merged into \"%s\"",
+ vg_from->name, vg_to->name);
+ r = ECMD_PROCESSED;
+bad:
+ if (lock_vg_from_first) {
+ unlock_and_free_vg(cmd, vg_to, vg_name_to);
+ unlock_and_free_vg(cmd, vg_from, vg_name_from);
+ } else {
+ unlock_and_free_vg(cmd, vg_from, vg_name_from);
+ unlock_and_free_vg(cmd, vg_to, vg_name_to);
+ }
+ return r;
+}
+
+int vgmerge(struct cmd_context *cmd, int argc, char **argv)
+{
+ char *vg_name_to, *vg_name_from;
+ int opt = 0;
+ int ret = 0, ret_max = 0;
+
+ if (argc < 2) {
+ log_error("Please enter 2 or more volume groups to merge");
+ return EINVALID_CMD_LINE;
+ }
+
+ vg_name_to = skip_dev_dir(cmd, argv[0], NULL);
+ argc--;
+ argv++;
+
+ for (; opt < argc; opt++) {
+ vg_name_from = skip_dev_dir(cmd, argv[opt], NULL);
+
+ ret = _vgmerge_single(cmd, vg_name_to, vg_name_from);
+ if (ret > ret_max)
+ ret_max = ret;
+ }
+
+ return ret_max;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static int _vgmknodes_single(struct cmd_context *cmd, struct logical_volume *lv,
+ void *handle __attribute__((unused)))
+{
+ if (arg_count(cmd, refresh_ARG) && lv_is_visible(lv))
+ if (!lv_refresh(cmd, lv)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (!lv_mknodes(cmd, lv)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ return ECMD_PROCESSED;
+}
+
+int vgmknodes(struct cmd_context *cmd, int argc, char **argv)
+{
+ if (!lv_mknodes(cmd, NULL)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ return process_each_lv(cmd, argc, argv, LCK_VG_READ, NULL,
+ &_vgmknodes_single);
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+#include "lv_alloc.h"
+
+static int _remove_pv(struct volume_group *vg, struct pv_list *pvl, int silent)
+{
+ char uuid[64] __attribute__((aligned(8)));
+
+ if (vg->pv_count == 1) {
+ log_error("Volume Groups must always contain at least one PV");
+ return 0;
+ }
+
+ if (!id_write_format(&pvl->pv->id, uuid, sizeof(uuid)))
+ return_0;
+
+ log_verbose("Removing PV with UUID %s from VG %s", uuid, vg->name);
+
+ if (pvl->pv->pe_alloc_count) {
+ if (!silent)
+ log_error("LVs still present on PV with UUID %s: "
+ "Can't remove from VG %s", uuid, vg->name);
+ return 0;
+ }
+
+ vg->free_count -= pvl->pv->pe_count;
+ vg->extent_count -= pvl->pv->pe_count;
+ del_pvl_from_vgs(vg, pvl);
+
+ return 1;
+}
+
+static int _remove_lv(struct cmd_context *cmd, struct logical_volume *lv,
+ int *list_unsafe, struct dm_list *lvs_changed)
+{
+ struct lv_segment *snap_seg;
+ struct dm_list *snh, *snht;
+ struct logical_volume *cow;
+ struct lv_list *lvl;
+ struct lvinfo info;
+ int first = 1;
+
+ log_verbose("%s/%s has missing extents: removing (including "
+ "dependencies)", lv->vg->name, lv->name);
+
+ /* FIXME Cope properly with stacked devices & snapshots. */
+
+ /* If snapshot device is missing, deactivate origin. */
+ if (lv_is_cow(lv) && (snap_seg = find_cow(lv))) {
+ log_verbose("Deactivating (if active) logical volume %s "
+ "(origin of %s)", snap_seg->origin->name, lv->name);
+
+ if (!test_mode() && !deactivate_lv(cmd, snap_seg->origin)) {
+ log_error("Failed to deactivate LV %s",
+ snap_seg->origin->name);
+ return 0;
+ }
+
+ /* Use the origin LV */
+ lv = snap_seg->origin;
+ }
+
+ /* Remove snapshot dependencies */
+ dm_list_iterate_safe(snh, snht, &lv->snapshot_segs) {
+ snap_seg = dm_list_struct_base(snh, struct lv_segment,
+ origin_list);
+ cow = snap_seg->cow;
+
+ if (first && !test_mode() &&
+ !deactivate_lv(cmd, snap_seg->origin)) {
+ log_error("Failed to deactivate LV %s",
+ snap_seg->origin->name);
+ return 0;
+ }
+
+ *list_unsafe = 1; /* May remove caller's lvht! */
+ if (!vg_remove_snapshot(cow))
+ return_0;
+ log_verbose("Removing LV %s from VG %s", cow->name,
+ lv->vg->name);
+ if (!lv_remove(cow))
+ return_0;
+
+ first = 0;
+ }
+
+ /*
+ * If LV is active, replace it with error segment
+ * and add to list of LVs to be removed later.
+ * Doesn't apply to snapshots/origins yet - they're already deactivated.
+ */
+ /*
+ * If the LV is a part of mirror segment,
+ * the mirrored LV also should be cleaned up.
+ * Clean-up is currently done by caller (_make_vg_consistent()).
+ */
+ if ((lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) ||
+ find_mirror_seg(first_seg(lv))) {
+ if (!replace_lv_with_error_segment(lv))
+ return_0;
+
+ if (!(lvl = dm_pool_alloc(cmd->mem, sizeof(*lvl)))) {
+ log_error("lv_list alloc failed");
+ return 0;
+ }
+ lvl->lv = lv;
+ dm_list_add(lvs_changed, &lvl->list);
+ } else {
+ /* Remove LV immediately. */
+ log_verbose("Removing LV %s from VG %s", lv->name, lv->vg->name);
+ if (!lv_remove(lv))
+ return_0;
+ }
+
+ return 1;
+}
+
+static int _consolidate_vg(struct cmd_context *cmd, struct volume_group *vg)
+{
+ struct pv_list *pvl;
+ struct lv_list *lvl;
+ int r = 1;
+
+ dm_list_iterate_items(lvl, &vg->lvs)
+ if (lvl->lv->status & PARTIAL_LV) {
+ log_warn("WARNING: Partial LV %s needs to be repaired "
+ "or removed. ", lvl->lv->name);
+ r = 0;
+ }
+
+ if (!r) {
+ cmd->handles_missing_pvs = 1;
+ log_warn("WARNING: There are still partial LVs in VG %s.", vg->name);
+ log_warn("To remove them unconditionally use: vgreduce --removemissing --force.");
+ log_warn("Proceeding to remove empty missing PVs.");
+ }
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (pvl->pv->dev && !is_missing_pv(pvl->pv))
+ continue;
+ if (r && !_remove_pv(vg, pvl, 0))
+ return_0;
+ }
+
+ return r;
+}
+
+static int _make_vg_consistent(struct cmd_context *cmd, struct volume_group *vg)
+{
+ struct dm_list *pvh, *pvht;
+ struct dm_list *lvh, *lvht;
+ struct pv_list *pvl;
+ struct lv_list *lvl, *lvl2, *lvlt;
+ struct logical_volume *lv;
+ struct physical_volume *pv;
+ struct lv_segment *seg, *mirrored_seg;
+ unsigned s;
+ uint32_t mimages, remove_log;
+ int list_unsafe, only_mirror_images_found;
+ DM_LIST_INIT(lvs_changed);
+ only_mirror_images_found = 1;
+
+ /* Deactivate & remove necessary LVs */
+ restart_loop:
+ list_unsafe = 0; /* Set if we delete a different list-member */
+
+ dm_list_iterate_safe(lvh, lvht, &vg->lvs) {
+ lv = dm_list_item(lvh, struct lv_list)->lv;
+
+ /* Are any segments of this LV on missing PVs? */
+ dm_list_iterate_items(seg, &lv->segments) {
+ for (s = 0; s < seg->area_count; s++) {
+ if (seg_type(seg, s) != AREA_PV)
+ continue;
+
+ /* FIXME Also check for segs on deleted LVs (incl pvmove) */
+
+ pv = seg_pv(seg, s);
+ if (!pv || !pv_dev(pv) ||
+ is_missing_pv(pv)) {
+ if (arg_count(cmd, mirrorsonly_ARG) &&
+ !(lv->status & MIRROR_IMAGE)) {
+ log_error("Non-mirror-image LV %s found: can't remove.", lv->name);
+ only_mirror_images_found = 0;
+ continue;
+ }
+ if (!_remove_lv(cmd, lv, &list_unsafe, &lvs_changed))
+ return_0;
+ if (list_unsafe)
+ goto restart_loop;
+ }
+ }
+ }
+ }
+
+ if (!only_mirror_images_found) {
+ log_error("Aborting because --mirrorsonly was specified.");
+ return 0;
+ }
+
+ /*
+ * Remove missing PVs. FIXME: This duplicates _consolidate_vg above,
+ * but we cannot use that right now, since the LV removal code in this
+ * function leaves the VG in a "somewhat inconsistent" state and
+ * _consolidate_vg doesn't like that -- specifically, mirrors are fixed
+ * up *after* the PVs are removed. All this should be gradually
+ * superseded by lvconvert --repair.
+ */
+ dm_list_iterate_safe(pvh, pvht, &vg->pvs) {
+ pvl = dm_list_item(pvh, struct pv_list);
+ if (pvl->pv->dev && !is_missing_pv(pvl->pv))
+ continue;
+ if (!_remove_pv(vg, pvl, 0))
+ return_0;
+ }
+
+ /* FIXME Recovery. For now people must clean up by hand. */
+
+ if (!dm_list_empty(&lvs_changed)) {
+ if (!vg_write(vg)) {
+ log_error("Failed to write out a consistent VG for %s",
+ vg->name);
+ return 0;
+ }
+
+ if (!test_mode()) {
+ /* Suspend lvs_changed */
+ if (!suspend_lvs(cmd, &lvs_changed)) {
+ stack;
+ vg_revert(vg);
+ return 0;
+ }
+ }
+
+ if (!vg_commit(vg)) {
+ log_error("Failed to commit consistent VG for %s",
+ vg->name);
+ vg_revert(vg);
+ return 0;
+ }
+
+ if (!test_mode()) {
+ if (!resume_lvs(cmd, &lvs_changed)) {
+ log_error("Failed to resume LVs using error segments.");
+ return 0;
+ }
+ }
+
+ lvs_changed_altered:
+ /* Remove lost mirror images from mirrors */
+ dm_list_iterate_items(lvl, &vg->lvs) {
+ mirrored_seg_altered:
+ mirrored_seg = first_seg(lvl->lv);
+ if (!seg_is_mirrored(mirrored_seg))
+ continue;
+
+ mimages = mirrored_seg->area_count;
+ remove_log = 0;
+
+ for (s = 0; s < mirrored_seg->area_count; s++) {
+ dm_list_iterate_items_safe(lvl2, lvlt, &lvs_changed) {
+ if (seg_type(mirrored_seg, s) != AREA_LV ||
+ lvl2->lv != seg_lv(mirrored_seg, s))
+ continue;
+ dm_list_del(&lvl2->list);
+ if (!shift_mirror_images(mirrored_seg, s))
+ return_0;
+ mimages--; /* FIXME Assumes uniqueness */
+ }
+ }
+
+ if (mirrored_seg->log_lv) {
+ dm_list_iterate_items(seg, &mirrored_seg->log_lv->segments) {
+ /* FIXME: The second test shouldn't be required */
+ if ((seg->segtype ==
+ get_segtype_from_string(vg->cmd, "error"))) {
+ log_print("The log device for %s/%s has failed.",
+ vg->name, mirrored_seg->lv->name);
+ remove_log = 1;
+ break;
+ }
+ if (!strcmp(seg->segtype->name, "error")) {
+ log_print("Log device for %s/%s has failed.",
+ vg->name, mirrored_seg->lv->name);
+ remove_log = 1;
+ break;
+ }
+ }
+ }
+
+ if ((mimages != mirrored_seg->area_count) || remove_log){
+ if (!reconfigure_mirror_images(mirrored_seg, mimages,
+ NULL, remove_log))
+ return_0;
+
+ if (!vg_write(vg)) {
+ log_error("Failed to write out updated "
+ "VG for %s", vg->name);
+ return 0;
+ }
+
+ if (!vg_commit(vg)) {
+ log_error("Failed to commit updated VG "
+ "for %s", vg->name);
+ vg_revert(vg);
+ return 0;
+ }
+
+ /* mirrored LV no longer has valid mimages.
+ * So add it to lvs_changed for removal.
+ * For this LV may be an area of other mirror,
+ * restart the loop. */
+ if (!mimages) {
+ if (!_remove_lv(cmd, lvl->lv,
+ &list_unsafe, &lvs_changed))
+ return_0;
+ goto lvs_changed_altered;
+ }
+
+ /* As a result of reconfigure_mirror_images(),
+ * first_seg(lv) may now be different seg.
+ * e.g. a temporary layer might be removed.
+ * So check the mirrored_seg again. */
+ goto mirrored_seg_altered;
+ }
+ }
+
+ /* Deactivate error LVs */
+ if (!test_mode()) {
+ dm_list_iterate_items_safe(lvl, lvlt, &lvs_changed) {
+ log_verbose("Deactivating (if active) logical volume %s",
+ lvl->lv->name);
+
+ if (!deactivate_lv(cmd, lvl->lv)) {
+ log_error("Failed to deactivate LV %s",
+ lvl->lv->name);
+ /*
+ * We failed to deactivate.
+ * Probably because this was a mirror log.
+ * Don't try to lv_remove it.
+ * Continue work on others.
+ */
+ dm_list_del(&lvl->list);
+ }
+ }
+ }
+
+ /* Remove remaining LVs */
+ dm_list_iterate_items(lvl, &lvs_changed) {
+ log_verbose("Removing LV %s from VG %s", lvl->lv->name,
+ lvl->lv->vg->name);
+ /* Skip LVs already removed by mirror code */
+ if (find_lv_in_vg(vg, lvl->lv->name) &&
+ !lv_remove(lvl->lv))
+ return_0;
+ }
+ }
+
+ return 1;
+}
+
+/* Or take pv_name instead? */
+static int _vgreduce_single(struct cmd_context *cmd, struct volume_group *vg,
+ struct physical_volume *pv,
+ void *handle __attribute__((unused)))
+{
+ struct pv_list *pvl;
+ struct volume_group *orphan_vg = NULL;
+ int r = ECMD_FAILED;
+ const char *name = pv_dev_name(pv);
+
+ if (pv_pe_alloc_count(pv)) {
+ log_error("Physical volume \"%s\" still in use", name);
+ return ECMD_FAILED;
+ }
+
+ if (vg->pv_count == 1) {
+ log_error("Can't remove final physical volume \"%s\" from "
+ "volume group \"%s\"", name, vg->name);
+ return ECMD_FAILED;
+ }
+
+ if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+ log_error("Can't get lock for orphan PVs");
+ return ECMD_FAILED;
+ }
+
+ pvl = find_pv_in_vg(vg, name);
+
+ if (!archive(vg))
+ goto_bad;
+
+ log_verbose("Removing \"%s\" from volume group \"%s\"", name, vg->name);
+
+ if (pvl)
+ del_pvl_from_vgs(vg, pvl);
+
+ pv->vg_name = vg->fid->fmt->orphan_vg_name;
+ pv->status = ALLOCATABLE_PV;
+
+ if (!dev_get_size(pv_dev(pv), &pv->size)) {
+ log_error("%s: Couldn't get size.", pv_dev_name(pv));
+ goto bad;
+ }
+
+ vg->free_count -= pv_pe_count(pv) - pv_pe_alloc_count(pv);
+ vg->extent_count -= pv_pe_count(pv);
+
+ orphan_vg = vg_read_for_update(cmd, vg->fid->fmt->orphan_vg_name,
+ NULL, 0);
+
+ if (vg_read_error(orphan_vg))
+ goto bad;
+
+ if (!vg_split_mdas(cmd, vg, orphan_vg) || !vg->pv_count) {
+ log_error("Cannot remove final metadata area on \"%s\" from \"%s\"",
+ name, vg->name);
+ goto bad;
+ }
+
+ if (!vg_write(vg) || !vg_commit(vg)) {
+ log_error("Removal of physical volume \"%s\" from "
+ "\"%s\" failed", name, vg->name);
+ goto bad;
+ }
+
+ if (!pv_write(cmd, pv, NULL, INT64_C(-1))) {
+ log_error("Failed to clear metadata from physical "
+ "volume \"%s\" "
+ "after removal from \"%s\"", name, vg->name);
+ goto bad;
+ }
+
+ backup(vg);
+
+ log_print("Removed \"%s\" from volume group \"%s\"", name, vg->name);
+ r = ECMD_PROCESSED;
+bad:
+ unlock_and_free_vg(cmd, orphan_vg, VG_ORPHANS);
+ return r;
+}
+
+int vgreduce(struct cmd_context *cmd, int argc, char **argv)
+{
+ struct volume_group *vg;
+ char *vg_name;
+ int ret = ECMD_FAILED;
+ int fixed = 1;
+ int repairing = arg_count(cmd, removemissing_ARG);
+ int saved_ignore_suspended_devices = ignore_suspended_devices();
+
+ if (!argc && !repairing) {
+ log_error("Please give volume group name and "
+ "physical volume paths");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (!argc && repairing) {
+ log_error("Please give volume group name");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, mirrorsonly_ARG) && !repairing) {
+ log_error("--mirrorsonly requires --removemissing");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (argc == 1 && !arg_count(cmd, all_ARG) && !repairing) {
+ log_error("Please enter physical volume paths or option -a");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (argc > 1 && arg_count(cmd, all_ARG)) {
+ log_error("Option -a and physical volume paths mutually "
+ "exclusive");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (argc > 1 && repairing) {
+ log_error("Please only specify the volume group");
+ return EINVALID_CMD_LINE;
+ }
+
+ vg_name = skip_dev_dir(cmd, argv[0], NULL);
+ argv++;
+ argc--;
+
+ log_verbose("Finding volume group \"%s\"", vg_name);
+
+ if (repairing) {
+ init_ignore_suspended_devices(1);
+ cmd->handles_missing_pvs = 1;
+ }
+
+ vg = vg_read_for_update(cmd, vg_name, NULL, READ_ALLOW_EXPORTED);
+ if (vg_read_error(vg) == FAILED_ALLOCATION ||
+ vg_read_error(vg) == FAILED_NOTFOUND)
+ goto_out;
+
+ /* FIXME We want to allow read-only VGs to be changed here? */
+ if (vg_read_error(vg) && vg_read_error(vg) != FAILED_READ_ONLY
+ && !arg_count(cmd, removemissing_ARG))
+ goto_out;
+
+ if (repairing) {
+ if (!vg_read_error(vg) && !vg_missing_pv_count(vg)) {
+ log_error("Volume group \"%s\" is already consistent",
+ vg_name);
+ ret = ECMD_PROCESSED;
+ goto out;
+ }
+
+ free_vg(vg);
+ log_verbose("Trying to open VG %s for recovery...", vg_name);
+
+ vg = vg_read_for_update(cmd, vg_name, NULL,
+ READ_ALLOW_INCONSISTENT
+ | READ_ALLOW_EXPORTED);
+
+ if (vg_read_error(vg) && vg_read_error(vg) != FAILED_READ_ONLY
+ && vg_read_error(vg) != FAILED_INCONSISTENT)
+ goto_out;
+
+ if (!archive(vg))
+ goto_out;
+
+ if (arg_count(cmd, force_ARG)) {
+ if (!_make_vg_consistent(cmd, vg))
+ goto_out;
+ } else
+ fixed = _consolidate_vg(cmd, vg);
+
+ if (!vg_write(vg) || !vg_commit(vg)) {
+ log_error("Failed to write out a consistent VG for %s",
+ vg_name);
+ goto out;
+ }
+ backup(vg);
+
+ if (fixed) {
+ log_print("Wrote out consistent volume group %s",
+ vg_name);
+ ret = ECMD_PROCESSED;
+ } else
+ ret = ECMD_FAILED;
+
+ } else {
+ if (!vg_check_status(vg, EXPORTED_VG | LVM_WRITE | RESIZEABLE_VG))
+ goto_out;
+
+ /* FIXME: Pass private struct through to all these functions */
+ /* and update in batch here? */
+ ret = process_each_pv(cmd, argc, argv, vg, READ_FOR_UPDATE, 0, NULL,
+ _vgreduce_single);
+
+ }
+out:
+ init_ignore_suspended_devices(saved_ignore_suspended_devices);
+ unlock_and_free_vg(cmd, vg, vg_name);
+
+ return ret;
+
+/******* FIXME
+ log_error ("no empty physical volumes found in volume group \"%s\"", vg_name);
+
+ log_verbose
+ ("volume group \"%s\" will be reduced by %d physical volume%s",
+ vg_name, np, np > 1 ? "s" : "");
+ log_verbose ("reducing volume group \"%s\" by physical volume \"%s\"",
+ vg_name, pv_names[p]);
+
+ log_print
+ ("volume group \"%s\" %ssuccessfully reduced by physical volume%s:",
+ vg_name, error > 0 ? "NOT " : "", p > 1 ? "s" : "");
+ log_print("%s", pv_this[p]->pv_name);
+********/
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static int vgremove_single(struct cmd_context *cmd, const char *vg_name,
+ struct volume_group *vg,
+ void *handle __attribute__((unused)))
+{
+ unsigned lv_count, missing;
+ force_t force;
+
+ if (!vg_check_status(vg, EXPORTED_VG)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ lv_count = vg_visible_lvs(vg);
+
+ force = arg_count(cmd, force_ARG);
+ if (lv_count) {
+ if (force == PROMPT) {
+ if ((missing = vg_missing_pv_count(vg)))
+ log_warn("WARNING: %d physical volumes are currently missing "
+ "from the system.", missing);
+ if (yes_no_prompt("Do you really want to remove volume "
+ "group \"%s\" containing %u "
+ "logical volumes? [y/n]: ",
+ vg_name, lv_count) == 'n') {
+ log_error("Volume group \"%s\" not removed", vg_name);
+ return ECMD_FAILED;
+ }
+ }
+ if (!remove_lvs_in_vg(cmd, vg, force)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ }
+
+ if (!force && !vg_remove_check(vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ vg_remove_pvs(vg);
+
+ if (!vg_remove(vg)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ return ECMD_PROCESSED;
+}
+
+int vgremove(struct cmd_context *cmd, int argc, char **argv)
+{
+ int ret;
+
+ if (!argc) {
+ log_error("Please enter one or more volume group paths");
+ return EINVALID_CMD_LINE;
+ }
+
+ cmd->handles_missing_pvs = 1;
+ ret = process_each_vg(cmd, argc, argv,
+ READ_FOR_UPDATE,
+ NULL, &vgremove_single);
+
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static struct volume_group *_get_old_vg_for_rename(struct cmd_context *cmd,
+ const char *vg_name_old,
+ const char *vgid)
+{
+ struct volume_group *vg;
+
+ /* FIXME we used to print an error about EXPORTED, but proceeded
+ nevertheless. */
+ vg = vg_read_for_update(cmd, vg_name_old, vgid, READ_ALLOW_EXPORTED);
+ if (vg_read_error(vg)) {
+ free_vg(vg);
+ return_NULL;
+ }
+
+ return vg;
+}
+
+static int _lock_new_vg_for_rename(struct cmd_context *cmd,
+ const char *vg_name_new)
+{
+ int rc;
+
+ log_verbose("Checking for new volume group \"%s\"", vg_name_new);
+
+ rc = vg_lock_newname(cmd, vg_name_new);
+
+ if (rc == FAILED_LOCKING) {
+ log_error("Can't get lock for %s", vg_name_new);
+ return 0;
+ }
+
+ if (rc == FAILED_EXIST) {
+ log_error("New volume group \"%s\" already exists",
+ vg_name_new);
+ return 0;
+ }
+ return 1;
+}
+
+static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path,
+ const char *new_vg_path)
+{
+ char *dev_dir;
+ struct id id;
+ int match = 0;
+ int found_id = 0;
+ struct dm_list *vgids;
+ struct str_list *sl;
+ char *vg_name_new;
+ const char *vgid = NULL, *vg_name, *vg_name_old;
+ char old_path[NAME_LEN], new_path[NAME_LEN];
+ struct volume_group *vg = NULL;
+ int lock_vg_old_first = 1;
+
+ vg_name_old = skip_dev_dir(cmd, old_vg_path, NULL);
+ vg_name_new = skip_dev_dir(cmd, new_vg_path, NULL);
+
+ dev_dir = cmd->dev_dir;
+
+ if (!validate_vg_rename_params(cmd, vg_name_old, vg_name_new))
+ return_0;
+
+ log_verbose("Checking for existing volume group \"%s\"", vg_name_old);
+
+ /* Avoid duplicates */
+ if (!(vgids = get_vgids(cmd, 0)) || dm_list_empty(vgids)) {
+ log_error("No complete volume groups found");
+ return 0;
+ }
+
+ dm_list_iterate_items(sl, vgids) {
+ vgid = sl->str;
+ if (!vgid || !(vg_name = vgname_from_vgid(NULL, vgid)))
+ continue;
+ if (!strcmp(vg_name, vg_name_old)) {
+ if (match) {
+ log_error("Found more than one VG called %s. "
+ "Please supply VG uuid.", vg_name_old);
+ return 0;
+ }
+ match = 1;
+ }
+ }
+
+ log_suppress(2);
+ found_id = id_read_format(&id, vg_name_old);
+ log_suppress(0);
+ if (found_id && (vg_name = vgname_from_vgid(cmd->mem, (char *)id.uuid))) {
+ vg_name_old = vg_name;
+ vgid = (char *)id.uuid;
+ } else
+ vgid = NULL;
+
+ if (strcmp(vg_name_new, vg_name_old) < 0)
+ lock_vg_old_first = 0;
+
+ if (lock_vg_old_first) {
+ vg = _get_old_vg_for_rename(cmd, vg_name_old, vgid);
+ if (!vg)
+ return_0;
+
+ if (!_lock_new_vg_for_rename(cmd, vg_name_new)) {
+ unlock_and_free_vg(cmd, vg, vg_name_old);
+ return_0;
+ }
+ } else {
+ if (!_lock_new_vg_for_rename(cmd, vg_name_new))
+ return_0;
+
+ vg = _get_old_vg_for_rename(cmd, vg_name_old, vgid);
+ if (!vg) {
+ unlock_vg(cmd, vg_name_new);
+ return_0;
+ }
+ }
+
+ if (!archive(vg))
+ goto error;
+
+ /* Remove references based on old name */
+ drop_cached_metadata(vg);
+
+ /* Change the volume group name */
+ vg_rename(cmd, vg, vg_name_new);
+
+ /* store it on disks */
+ log_verbose("Writing out updated volume group");
+ if (!vg_write(vg) || !vg_commit(vg)) {
+ goto error;
+ }
+
+ sprintf(old_path, "%s%s", dev_dir, vg_name_old);
+ sprintf(new_path, "%s%s", dev_dir, vg_name_new);
+
+ if (activation() && dir_exists(old_path)) {
+ log_verbose("Renaming \"%s\" to \"%s\"", old_path, new_path);
+
+ if (test_mode())
+ log_verbose("Test mode: Skipping rename.");
+
+ else if (lvs_in_vg_activated(vg)) {
+ if (!vg_refresh_visible(cmd, vg)) {
+ log_error("Renaming \"%s\" to \"%s\" failed",
+ old_path, new_path);
+ goto error;
+ }
+ }
+ }
+
+ backup(vg);
+ backup_remove(cmd, vg_name_old);
+
+ unlock_vg(cmd, vg_name_new);
+ unlock_and_free_vg(cmd, vg, vg_name_old);
+
+ log_print("Volume group \"%s\" successfully renamed to \"%s\"",
+ vg_name_old, vg_name_new);
+
+ /* FIXME lvmcache corruption - vginfo duplicated instead of renamed */
+ persistent_filter_wipe(cmd->filter);
+ lvmcache_destroy(cmd, 1);
+
+ return 1;
+
+ error:
+ if (lock_vg_old_first) {
+ unlock_vg(cmd, vg_name_new);
+ unlock_and_free_vg(cmd, vg, vg_name_old);
+ } else {
+ unlock_and_free_vg(cmd, vg, vg_name_old);
+ unlock_vg(cmd, vg_name_new);
+ }
+ return 0;
+}
+
+int vgrename(struct cmd_context *cmd, int argc, char **argv)
+{
+ if (argc != 2) {
+ log_error("Old and new volume group names need specifying");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (!vg_rename_path(cmd, argv[0], argv[1])) {
+ stack;
+ return ECMD_FAILED;
+ }
+
+ return ECMD_PROCESSED;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+static int vgscan_single(struct cmd_context *cmd, const char *vg_name,
+ struct volume_group *vg,
+ void *handle __attribute__((unused)))
+{
+ log_print("Found %svolume group \"%s\" using metadata type %s",
+ vg_is_exported(vg) ? "exported " : "", vg_name,
+ vg->fid->fmt->name);
+
+ check_current_backup(vg);
+
+ return ECMD_PROCESSED;
+}
+
+int vgscan(struct cmd_context *cmd, int argc, char **argv)
+{
+ int maxret, ret;
+
+ if (argc) {
+ log_error("Too many parameters on command line");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (!lock_vol(cmd, VG_GLOBAL, LCK_VG_WRITE)) {
+ log_error("Unable to obtain global lock.");
+ return ECMD_FAILED;
+ }
+
+ persistent_filter_wipe(cmd->filter);
+ lvmcache_destroy(cmd, 1);
+
+ log_print("Reading all physical volumes. This may take a while...");
+
+ maxret = process_each_vg(cmd, argc, argv, 0, NULL,
+ &vgscan_single);
+
+ if (arg_count(cmd, mknodes_ARG)) {
+ ret = vgmknodes(cmd, argc, argv);
+ if (ret > maxret)
+ maxret = ret;
+ }
+
+ unlock_vg(cmd, VG_GLOBAL);
+ return maxret;
+}
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tools.h"
+
+/* FIXME Why not (lv->vg == vg) ? */
+static int _lv_is_in_vg(struct volume_group *vg, struct logical_volume *lv)
+{
+ struct lv_list *lvl;
+
+ dm_list_iterate_items(lvl, &vg->lvs)
+ if (lv == lvl->lv)
+ return 1;
+
+ return 0;
+}
+
+static int _move_one_lv(struct volume_group *vg_from,
+ struct volume_group *vg_to,
+ struct dm_list *lvh)
+{
+ struct logical_volume *lv = dm_list_item(lvh, struct lv_list)->lv;
+
+ dm_list_move(&vg_to->lvs, lvh);
+ lv->vg = vg_to;
+
+ if (lv_is_active(lv)) {
+ log_error("Logical volume \"%s\" must be inactive", lv->name);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _move_lvs(struct volume_group *vg_from, struct volume_group *vg_to)
+{
+ struct dm_list *lvh, *lvht;
+ struct logical_volume *lv;
+ struct lv_segment *seg;
+ struct physical_volume *pv;
+ struct volume_group *vg_with;
+ unsigned s;
+
+ dm_list_iterate_safe(lvh, lvht, &vg_from->lvs) {
+ lv = dm_list_item(lvh, struct lv_list)->lv;
+
+ if ((lv->status & SNAPSHOT))
+ continue;
+
+ if ((lv->status & MIRRORED))
+ continue;
+
+ /* Ensure all the PVs used by this LV remain in the same */
+ /* VG as each other */
+ vg_with = NULL;
+ dm_list_iterate_items(seg, &lv->segments) {
+ for (s = 0; s < seg->area_count; s++) {
+ /* FIXME Check AREA_LV too */
+ if (seg_type(seg, s) != AREA_PV)
+ continue;
+
+ pv = seg_pv(seg, s);
+ if (vg_with) {
+ if (!pv_is_in_vg(vg_with, pv)) {
+ log_error("Can't split Logical "
+ "Volume %s between "
+ "two Volume Groups",
+ lv->name);
+ return 0;
+ }
+ continue;
+ }
+
+ if (pv_is_in_vg(vg_from, pv)) {
+ vg_with = vg_from;
+ continue;
+ }
+ if (pv_is_in_vg(vg_to, pv)) {
+ vg_with = vg_to;
+ continue;
+ }
+ log_error("Physical Volume %s not found",
+ pv_dev_name(pv));
+ return 0;
+ }
+
+ }
+
+ if (vg_with == vg_from)
+ continue;
+
+ /* Move this LV */
+ if (!_move_one_lv(vg_from, vg_to, lvh))
+ return_0;
+ }
+
+ /* FIXME Ensure no LVs contain segs pointing at LVs in the other VG */
+
+ return 1;
+}
+
+/*
+ * Move the hidden / internal "snapshotN" LVs.from 'vg_from' to 'vg_to'.
+ */
+static int _move_snapshots(struct volume_group *vg_from,
+ struct volume_group *vg_to)
+{
+ struct dm_list *lvh, *lvht;
+ struct logical_volume *lv;
+ struct lv_segment *seg;
+ int cow_from = 0;
+ int origin_from = 0;
+
+ dm_list_iterate_safe(lvh, lvht, &vg_from->lvs) {
+ lv = dm_list_item(lvh, struct lv_list)->lv;
+
+ if (!(lv->status & SNAPSHOT))
+ continue;
+
+ dm_list_iterate_items(seg, &lv->segments) {
+ cow_from = _lv_is_in_vg(vg_from, seg->cow);
+ origin_from = _lv_is_in_vg(vg_from, seg->origin);
+
+ if (cow_from && origin_from)
+ continue;
+ if ((!cow_from && origin_from) ||
+ (cow_from && !origin_from)) {
+ log_error("Can't split snapshot %s between"
+ " two Volume Groups", seg->cow->name);
+ return 0;
+ }
+
+ /*
+ * At this point, the cow and origin should already be
+ * in vg_to.
+ */
+ if (_lv_is_in_vg(vg_to, seg->cow) &&
+ _lv_is_in_vg(vg_to, seg->origin)) {
+ if (!_move_one_lv(vg_from, vg_to, lvh))
+ return_0;
+ }
+ }
+
+ }
+
+ return 1;
+}
+
+static int _move_mirrors(struct volume_group *vg_from,
+ struct volume_group *vg_to)
+{
+ struct dm_list *lvh, *lvht;
+ struct logical_volume *lv;
+ struct lv_segment *seg;
+ unsigned s, seg_in, log_in;
+
+ dm_list_iterate_safe(lvh, lvht, &vg_from->lvs) {
+ lv = dm_list_item(lvh, struct lv_list)->lv;
+
+ if (!(lv->status & MIRRORED))
+ continue;
+
+ seg = first_seg(lv);
+
+ seg_in = 0;
+ for (s = 0; s < seg->area_count; s++)
+ if (_lv_is_in_vg(vg_to, seg_lv(seg, s)))
+ seg_in++;
+
+ log_in = (!seg->log_lv || _lv_is_in_vg(vg_to, seg->log_lv));
+
+ if ((seg_in && seg_in < seg->area_count) ||
+ (seg_in && seg->log_lv && !log_in) ||
+ (!seg_in && seg->log_lv && log_in)) {
+ log_error("Can't split mirror %s between "
+ "two Volume Groups", lv->name);
+ return 0;
+ }
+
+ if (seg_in == seg->area_count && log_in) {
+ if (!_move_one_lv(vg_from, vg_to, lvh))
+ return_0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Create or open the destination of the vgsplit operation.
+ * Returns
+ * - non-NULL: VG handle w/VG lock held
+ * - NULL: no VG lock held
+ */
+static struct volume_group *_vgsplit_to(struct cmd_context *cmd,
+ const char *vg_name_to,
+ int *existing_vg)
+{
+ struct volume_group *vg_to = NULL;
+
+ log_verbose("Checking for new volume group \"%s\"", vg_name_to);
+ /*
+ * First try to create a new VG. If we cannot create it,
+ * and we get FAILED_EXIST (we will not be holding a lock),
+ * a VG must already exist with this name. We then try to
+ * read the existing VG - the vgsplit will be into an existing VG.
+ *
+ * Otherwise, if the lock was successful, it must be the case that
+ * we obtained a WRITE lock and could not find the vgname in the
+ * system. Thus, the split will be into a new VG.
+ */
+ vg_to = vg_create(cmd, vg_name_to);
+ if (vg_read_error(vg_to) == FAILED_LOCKING) {
+ log_error("Can't get lock for %s", vg_name_to);
+ free_vg(vg_to);
+ return NULL;
+ }
+ if (vg_read_error(vg_to) == FAILED_EXIST) {
+ *existing_vg = 1;
+ free_vg(vg_to);
+ vg_to = vg_read_for_update(cmd, vg_name_to, NULL, 0);
+
+ if (vg_read_error(vg_to)) {
+ free_vg(vg_to);
+ stack;
+ return NULL;
+ }
+
+ } else if (vg_read_error(vg_to) == SUCCESS) {
+ *existing_vg = 0;
+ }
+ return vg_to;
+}
+
+/*
+ * Open the source of the vgsplit operation.
+ * Returns
+ * - non-NULL: VG handle w/VG lock held
+ * - NULL: no VG lock held
+ */
+static struct volume_group *_vgsplit_from(struct cmd_context *cmd,
+ const char *vg_name_from)
+{
+ struct volume_group *vg_from;
+
+ log_verbose("Checking for volume group \"%s\"", vg_name_from);
+
+ vg_from = vg_read_for_update(cmd, vg_name_from, NULL, 0);
+ if (vg_read_error(vg_from)) {
+ free_vg(vg_from);
+ return NULL;
+ }
+ return vg_from;
+}
+
+/*
+ * Has the user given an option related to a new vg as the split destination?
+ */
+static int new_vg_option_specified(struct cmd_context *cmd)
+{
+ return(arg_count(cmd, clustered_ARG) ||
+ arg_count(cmd, alloc_ARG) ||
+ arg_count(cmd, maxphysicalvolumes_ARG) ||
+ arg_count(cmd, maxlogicalvolumes_ARG) ||
+ arg_count(cmd, vgmetadatacopies_ARG));
+}
+
+int vgsplit(struct cmd_context *cmd, int argc, char **argv)
+{
+ struct vgcreate_params vp_new;
+ struct vgcreate_params vp_def;
+ char *vg_name_from, *vg_name_to;
+ struct volume_group *vg_to = NULL, *vg_from = NULL;
+ int opt;
+ int existing_vg = 0;
+ int r = ECMD_FAILED;
+ const char *lv_name;
+ int lock_vg_from_first = 1;
+
+ if ((arg_count(cmd, name_ARG) + argc) < 3) {
+ log_error("Existing VG, new VG and either physical volumes "
+ "or logical volume required.");
+ return EINVALID_CMD_LINE;
+ }
+
+ if (arg_count(cmd, name_ARG) && (argc > 2)) {
+ log_error("A logical volume name cannot be given with "
+ "physical volumes.");
+ return ECMD_FAILED;
+ }
+
+ if (arg_count(cmd, name_ARG))
+ lv_name = arg_value(cmd, name_ARG);
+ else
+ lv_name = NULL;
+
+ vg_name_from = skip_dev_dir(cmd, argv[0], NULL);
+ vg_name_to = skip_dev_dir(cmd, argv[1], NULL);
+ argc -= 2;
+ argv += 2;
+
+ if (!strcmp(vg_name_to, vg_name_from)) {
+ log_error("Duplicate volume group name \"%s\"", vg_name_from);
+ return ECMD_FAILED;
+ }
+
+ if (strcmp(vg_name_to, vg_name_from) < 0)
+ lock_vg_from_first = 0;
+
+ if (lock_vg_from_first) {
+ vg_from = _vgsplit_from(cmd, vg_name_from);
+ if (!vg_from) {
+ stack;
+ return ECMD_FAILED;
+ }
+ /*
+ * Set metadata format of original VG.
+ * NOTE: We must set the format before calling vg_create()
+ * since vg_create() calls the per-format constructor.
+ */
+ cmd->fmt = vg_from->fid->fmt;
+
+ vg_to = _vgsplit_to(cmd, vg_name_to, &existing_vg);
+ if (!vg_to) {
+ unlock_and_free_vg(cmd, vg_from, vg_name_from);
+ stack;
+ return ECMD_FAILED;
+ }
+ } else {
+ vg_to = _vgsplit_to(cmd, vg_name_to, &existing_vg);
+ if (!vg_to) {
+ stack;
+ return ECMD_FAILED;
+ }
+ vg_from = _vgsplit_from(cmd, vg_name_from);
+ if (!vg_from) {
+ unlock_and_free_vg(cmd, vg_to, vg_name_to);
+ stack;
+ return ECMD_FAILED;
+ }
+
+ if (cmd->fmt != vg_from->fid->fmt) {
+ /* In this case we don't know the vg_from->fid->fmt */
+ log_error("Unable to set new VG metadata type based on "
+ "source VG format - use -M option.");
+ goto bad;
+ }
+ }
+
+ if (existing_vg) {
+ if (new_vg_option_specified(cmd)) {
+ log_error("Volume group \"%s\" exists, but new VG "
+ "option specified", vg_name_to);
+ goto bad;
+ }
+ if (!vgs_are_compatible(cmd, vg_from,vg_to))
+ goto_bad;
+ } else {
+ vgcreate_params_set_defaults(&vp_def, vg_from);
+ vp_def.vg_name = vg_name_to;
+ if (vgcreate_params_set_from_args(cmd, &vp_new, &vp_def)) {
+ r = EINVALID_CMD_LINE;
+ goto_bad;
+ }
+
+ if (vgcreate_params_validate(cmd, &vp_new)) {
+ r = EINVALID_CMD_LINE;
+ goto_bad;
+ }
+
+ if (!vg_set_extent_size(vg_to, vp_new.extent_size) ||
+ !vg_set_max_lv(vg_to, vp_new.max_lv) ||
+ !vg_set_max_pv(vg_to, vp_new.max_pv) ||
+ !vg_set_alloc_policy(vg_to, vp_new.alloc) ||
+ !vg_set_clustered(vg_to, vp_new.clustered) ||
+ !vg_set_mda_copies(vg_to, vp_new.vgmetadatacopies))
+ goto_bad;
+ }
+
+ /* Archive vg_from before changing it */
+ if (!archive(vg_from))
+ goto_bad;
+
+ /* Move PVs across to new structure */
+ for (opt = 0; opt < argc; opt++) {
+ unescape_colons_and_at_signs(argv[opt], NULL, NULL);
+ if (!move_pv(vg_from, vg_to, argv[opt]))
+ goto_bad;
+ }
+
+ /* If an LV given on the cmdline, move used_by PVs */
+ if (lv_name && !move_pvs_used_by_lv(vg_from, vg_to, lv_name))
+ goto_bad;
+
+ /* Move required LVs across, checking consistency */
+ if (!(_move_lvs(vg_from, vg_to)))
+ goto_bad;
+
+ /* FIXME Separate the 'move' from the 'validation' to fix dev stacks */
+ /* Move required mirrors across */
+ if (!(_move_mirrors(vg_from, vg_to)))
+ goto_bad;
+
+ /* Move required snapshots across */
+ if (!(_move_snapshots(vg_from, vg_to)))
+ goto_bad;
+
+ /* Split metadata areas and check if both vgs have at least one area */
+ if (!(vg_split_mdas(cmd, vg_from, vg_to)) && vg_from->pv_count) {
+ log_error("Cannot split: Nowhere to store metadata for new Volume Group");
+ goto bad;
+ }
+
+ /* Set proper name for all PVs in new VG */
+ if (!vg_rename(cmd, vg_to, vg_name_to))
+ goto_bad;
+
+ /* store it on disks */
+ log_verbose("Writing out updated volume groups");
+
+ /*
+ * First, write out the new VG as EXPORTED. We do this first in case
+ * there is a crash - we will still have the new VG information, in an
+ * exported state. Recovery after this point would be removal of the
+ * new VG and redoing the vgsplit.
+ * FIXME: recover automatically or instruct the user?
+ */
+ vg_to->status |= EXPORTED_VG;
+
+ if (!archive(vg_to))
+ goto_bad;
+
+ if (!vg_write(vg_to) || !vg_commit(vg_to))
+ goto_bad;
+
+ backup(vg_to);
+
+ /*
+ * Next, write out the updated old VG. If we crash after this point,
+ * recovery is a vgimport on the new VG.
+ * FIXME: recover automatically or instruct the user?
+ */
+ if (vg_from->pv_count) {
+ if (!vg_write(vg_from) || !vg_commit(vg_from))
+ goto_bad;
+
+ backup(vg_from);
+ }
+
+ /*
+ * Finally, remove the EXPORTED flag from the new VG and write it out.
+ */
+ if (!test_mode()) {
+ free_vg(vg_to);
+ vg_to = vg_read_for_update(cmd, vg_name_to, NULL,
+ READ_ALLOW_EXPORTED);
+ if (vg_read_error(vg_to)) {
+ log_error("Volume group \"%s\" became inconsistent: "
+ "please fix manually", vg_name_to);
+ goto bad;
+ }
+ }
+
+ vg_to->status &= ~EXPORTED_VG;
+
+ if (!vg_write(vg_to) || !vg_commit(vg_to))
+ goto_bad;
+
+ backup(vg_to);
+
+ log_print("%s volume group \"%s\" successfully split from \"%s\"",
+ existing_vg ? "Existing" : "New",
+ vg_to->name, vg_from->name);
+
+ r = ECMD_PROCESSED;
+
+bad:
+ if (lock_vg_from_first) {
+ unlock_and_free_vg(cmd, vg_to, vg_name_to);
+ unlock_and_free_vg(cmd, vg_from, vg_name_from);
+ } else {
+ unlock_and_free_vg(cmd, vg_from, vg_name_from);
+ unlock_and_free_vg(cmd, vg_to, vg_name_to);
+ }
+ return r;
+}
--- /dev/null
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+
+# Udev rules for device-mapper devices.
+#
+# These rules create a DM control node in /dev/(DM_DIR) directory.
+# The rules also create nodes named dm-x (x is a number) in /dev
+# directory and symlinks to these nodes with names given by
+# the actual DM names. Some udev environment variables are set
+# for use in later rules:
+# DM_NAME - actual DM device's name
+# DM_UUID - UUID set for DM device (blank if not specified)
+# DM_SUSPENDED - suspended state of DM device (0 or 1)
+# DM_UDEV_RULES_VSN - DM udev rules version
+
+KERNEL=="device-mapper", NAME="(DM_DIR)/control"
+
+SUBSYSTEM!="block", GOTO="dm_end"
+KERNEL!="dm-[0-9]*", GOTO="dm_end"
+
+# Set proper sbin path, /sbin has higher priority than /usr/sbin.
+ENV{DM_SBIN_PATH}="/sbin"
+TEST!="$env{DM_SBIN_PATH}/dmsetup", ENV{DM_SBIN_PATH}="/usr/sbin"
+TEST!="$env{DM_SBIN_PATH}/dmsetup", GOTO="dm_end"
+
+# Decode udev control flags and set environment variables appropriately.
+# These flags are encoded in DM_COOKIE variable that was introduced in
+# kernel version 2.6.31. Therefore, we can use this feature with
+# kernels >= 2.6.31 only.
+ENV{DM_COOKIE}=="?*", IMPORT{program}="$env{DM_SBIN_PATH}/dmsetup udevflags $env{DM_COOKIE}"
+
+# Device created, major and minor number assigned - "add" event generated.
+# Table loaded - no event generated.
+# Device resumed (or renamed) - "change" event generated.
+# Device removed - "remove" event generated.
+#
+# The dm-X nodes are always created, even on "add" event, we can't suppress
+# that (the node is created even earlier with devtmpfs). All the symlinks
+# (e.g. /dev/mapper) are created in right time after a device has its table
+# loaded and is properly resumed. For this reason, direct use of dm-X nodes
+# is not recommended.
+ACTION!="add|change", GOTO="dm_end"
+
+# Rule out easy-to-detect inappropriate events first.
+ENV{DISK_RO}=="1", GOTO="dm_disable"
+
+# There is no cookie set nor any flags encoded in events not originating
+# in libdevmapper so we need to detect this and try to behave correctly.
+# For such spurious events, regenerate all flags from current udev database content
+# (this information would normally be inaccessible for spurious ADD and CHANGE events).
+ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}=="1", GOTO="dm_flags_done"
+IMPORT{db}="DM_UDEV_DISABLE_DM_RULES_FLAG"
+IMPORT{db}="DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG"
+IMPORT{db}="DM_UDEV_DISABLE_DISK_RULES_FLAG"
+IMPORT{db}="DM_UDEV_DISABLE_OTHER_RULES_FLAG"
+IMPORT{db}="DM_UDEV_LOW_PRIORITY_FLAG"
+IMPORT{db}="DM_UDEV_DISABLE_LIBRARY_FALLBACK_FLAG"
+IMPORT{db}="DM_UDEV_PRIMARY_SOURCE_FLAG"
+IMPORT{db}="DM_UDEV_FLAG7"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG0"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG1"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG2"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG3"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG4"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG5"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG6"
+IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG7"
+IMPORT{db}="DM_UDEV_RULES_VSN"
+LABEL="dm_flags_done"
+
+# Normally, we operate on "change" events. But when coldplugging, there's an
+# "add" event present. We have to recognize this and do our actions in this
+# particular situation, too. Also, we don't want the nodes to be created
+# prematurely on "add" events while not coldplugging. We check
+# DM_UDEV_PRIMARY_SOURCE_FLAG to see if the device was activated correctly
+# before and if not, we ignore the "add" event totally. This way we can support
+# udev triggers generating "add" events (e.g. "udevadm trigger --action=add" or
+# "echo add > /sys/block/<dm_device>/uevent"). The trigger with "add" event is
+# also used at boot to reevaluate udev rules for all existing devices activated
+# before (e.g. in initrd). If udev is used in initrd, we require the udev init
+# script to not remove the existing udev database so we can reuse the information
+# stored at the time of device activation in the initrd.
+ACTION=="add", ENV{DM_UDEV_RULES_VSN}!="1", ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}!="1", GOTO="dm_disable"
+
+# "dm" sysfs subdirectory is available in newer versions of DM
+# only (kernels >= 2.6.29). We have to check for its existence
+# and use dmsetup tool instead to get the DM name, uuid and
+# suspended state if the "dm" subdirectory is not present.
+# The "suspended" item was added even later (kernels >= 2.6.31),
+# so we also have to call dmsetup if the kernel version used
+# is in between these releases.
+TEST=="dm", ENV{DM_NAME}="$attr{dm/name}", ENV{DM_UUID}="$attr{dm/uuid}", ENV{DM_SUSPENDED}="$attr{dm/suspended}"
+TEST!="dm", IMPORT{program}="$env{DM_SBIN_PATH}/dmsetup info -j %M -m %m -c --nameprefixes --noheadings --rows -o name,uuid,suspended"
+ENV{DM_SUSPENDED}!="?*", IMPORT{program}="$env{DM_SBIN_PATH}/dmsetup info -j %M -m %m -c --nameprefixes --noheadings --rows -o suspended"
+
+# dmsetup tool provides suspended state information in textual
+# form with values "Suspended"/"Active". We translate it to
+# 0/1 respectively to be consistent with sysfs values.
+ENV{DM_SUSPENDED}=="Active", ENV{DM_SUSPENDED}="0"
+ENV{DM_SUSPENDED}=="Suspended", ENV{DM_SUSPENDED}="1"
+
+# This variable provides a reliable way to check that device-mapper
+# rules were installed. It means that all needed variables are set
+# by these rules directly so there's no need to acquire them again
+# later. Other rules can alternate the functionality based on this
+# fact (e.g. fallback to rules that behave correctly even without
+# these rules installed). It also provides versioning for any
+# possible future changes.
+# VSN 1 - original rules
+# VSN 2 - add support for synthesized events
+ENV{DM_UDEV_RULES_VSN}="2"
+
+ENV{DM_UDEV_DISABLE_DM_RULES_FLAG}!="1", ENV{DM_NAME}=="?*", SYMLINK+="(DM_DIR)/$env{DM_NAME}"
+
+# We have to ignore further rule application for inappropriate events
+# and devices. But still send the notification if cookie exists.
+ENV{DM_UUID}=="mpath-?*", ENV{DM_ACTION}=="PATH_FAILED", GOTO="dm_disable"
+ENV{DM_UUID}=="CRYPT-TEMP-?*", GOTO="dm_disable"
+ENV{DM_UUID}!="?*", ENV{DM_NAME}=="temporary-cryptsetup-?*", GOTO="dm_disable"
+
+GOTO="dm_end"
+
+LABEL="dm_disable"
+ENV{DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG}="1"
+ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}="1"
+ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1"
+OPTIONS:="nowatch"
+
+LABEL="dm_end"
--- /dev/null
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+
+# Udev rules for LVM.
+#
+# These rules create symlinks for LVM logical volumes in
+# /dev/VG directory (VG is an actual VG name). Some udev
+# environment variables are set (they can be used in later
+# rules as well):
+# DM_LV_NAME - logical volume name
+# DM_VG_NAME - volume group name
+# DM_LV_LAYER - logical volume layer (blank if not set)
+
+# "add" event is processed on coldplug only!
+ACTION!="add|change", GOTO="lvm_end"
+ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="lvm_end"
+ENV{DM_UUID}!="LVM-?*", GOTO="lvm_end"
+
+# Use DM name and split it up into its VG/LV/layer constituents.
+IMPORT{program}="$env{DM_SBIN_PATH}/dmsetup splitname --nameprefixes --noheadings --rows $env{DM_NAME}"
+
+ENV{DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG}=="1", GOTO="lvm_end"
+
+# Do not create symlinks for inappropriate subdevices.
+ENV{DM_LV_NAME}=="pvmove?*|?*_vorigin", GOTO="lvm_disable"
+ENV{DM_LV_LAYER}=="?*", GOTO="lvm_disable"
+
+# Create symlinks for top-level devices only.
+ENV{DM_VG_NAME}=="?*", ENV{DM_LV_NAME}=="?*", SYMLINK+="$env{DM_VG_NAME}/$env{DM_LV_NAME}", GOTO="lvm_end"
+
+LABEL="lvm_disable"
+ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}="1"
+ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1"
+OPTIONS:="nowatch"
+
+LABEL="lvm_end"
--- /dev/null
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+
+# Udev rules for device-mapper devices.
+#
+# These rules set permissions for DM devices.
+#
+# This file is considered to be a template where users can put their
+# own entries and then put a copy of it manually to a usual place with
+# user-edited udev rules (usually /etc/udev/rules.d).
+#
+# There are some environment variables set that can be used:
+# DM_UDEV_RULES_VSN - DM udev rules version
+# DM_NAME - actual DM device's name
+# DM_UUID - UUID set for DM device (blank if not specified)
+# DM_SUSPENDED - suspended state of DM device (0 or 1)
+# DM_LV_NAME - logical volume name (not set if LVM device not present)
+# DM_VG_NAME - volume group name (not set if LVM device not present)
+# DM_LV_LAYER - logical volume layer (not set if LVM device not present)
+
+# "add" event is processed on coldplug only!
+ACTION!="add|change", GOTO="dm_end"
+ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="dm_end"
+
+# A few demonstrational examples...
+
+
+# PLAIN DM DEVICES
+#
+# Set permissions for a DM device named 'my_device' exactly
+# ENV{DM_NAME}=="my_device", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for all DM devices having 'MY_UUID-' UUID prefix
+# ENV{DM_UUID}=="MY_UUID-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+
+# LVM DEVICES
+#
+# Set permissions for all LVM devices
+# ENV{DM_UUID}=="LVM-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for all devices that belong to one LVM VG
+# ENV{DM_VG_NAME}=="VolGroup00", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for an LVM device with VG named VolGroup00 and LV named LogVol00 exactly
+# ENV{DM_VG_NAME}=="VolGroup00", ENV{DM_LV_NAME}=="LogVol00", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for all LVM devices that does not belong to a VG named VolGroup00
+# ENV{DM_VG_NAME}!="VolGroup00", OWNER:="root", GROUP:="root", MODE:="660"
+
+
+# ENCRYPTED DEVICES (using cryptsetup >= 1.1)
+#
+# Set permissions for all encrypted devices created by cryptsetup (plain devices)
+# ENV{DM_UUID}=="CRYPT-PLAIN-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for all encrypted devices created by cryptsetup (LUKS extension)
+# ENV{DM_UUID}=="CRYPT-LUKS1-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for an encrypted device created by cryptsetup and having an exact luks UUID
+# ENV{DM_UUID}=="CRYPT-LUKS1-22fce5c8313c43c68d84b50a3b0fee78-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+
+# MULTIPATH DEVICES
+#
+# Set permissions for all multipath devices
+# ENV{DM_UUID}=="mpath-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+# Set permissions for first two partitions created on a multipath device (and detected by kpartx)
+# ENV{DM_UUID}=="part[1-2]-mpath-?*", OWNER:="root", GROUP:="root", MODE:="660"
+
+
+# ...you can use any combination of the comparisons with the environment variables
+# listed at the beginning of this file (udev provides simple pattern matching by
+# using *, ? and [] that you can use, see 'man udev' for more information).
+
+# Set default permissions for all DM devices if not set before.
+# OWNER:="root", GROUP:="root", MODE:="660"
+
+LABEL="dm_end"
--- /dev/null
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+
+# Udev rules for device-mapper devices.
+#
+# These rules create symlinks in /dev/disk directory.
+# Symlinks that depend on probing filesystem type,
+# label and uuid are created only if the device is not
+# suspended.
+
+# "add" event is processed on coldplug only!
+ACTION!="add|change", GOTO="dm_end"
+ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="dm_end"
+ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}=="1", GOTO="dm_end"
+
+SYMLINK+="disk/by-id/dm-name-$env{DM_NAME}"
+ENV{DM_UUID}=="?*", SYMLINK+="disk/by-id/dm-uuid-$env{DM_UUID}"
+
+ENV{DM_SUSPENDED}=="1", GOTO="dm_end"
+
+IMPORT{program}="$env{DM_SBIN_PATH}/blkid -o udev -p $tempnode"
+ENV{DM_UDEV_LOW_PRIORITY_FLAG}=="1", OPTIONS="link_priority=-100"
+ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}"
+ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}"
+
+LABEL="dm_end"
--- /dev/null
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+
+# Udev rules for device-mapper devices.
+#
+# These rules are responsible for sending a notification to a process
+# waiting for completion of udev rules. The process is identified by
+# a cookie value sent within "change" and "remove" events (the cookie
+# value is set before by that process for every action requested).
+
+ENV{DM_COOKIE}=="?*", RUN+="$env{DM_SBIN_PATH}/dmsetup udevcomplete $env{DM_COOKIE}"
--- /dev/null
+#
+# Copyright (C) 2009-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+DM_RULES=10-dm.rules 13-dm-disk.rules 95-dm-notify.rules
+LVM_RULES=11-dm-lvm.rules
+DM_DIR=$(shell grep "\#define DM_DIR" $(top_srcdir)/libdm/misc/dm-ioctl.h | awk '{print $$3}')
+
+CLEAN_TARGETS=10-dm.rules
+
+include $(top_builddir)/make.tmpl
+
+vpath %.rules $(srcdir)
+
+%.rules: %.rules.in
+ $(SED) -e "s/(DM_DIR)/$(DM_DIR)/" $< >$@
+
+%_install: %.rules
+ $(INSTALL_DATA) -D $< $(udevdir)/$(<F)
+
+install_device-mapper: $(DM_RULES:.rules=_install)
+install_lvm2: $(LVM_RULES:.rules=_install)
+
+install: install_lvm2 install_device-mapper
--- /dev/null
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES=\
+ bitset_t.c
+
+TARGETS=\
+ bitset_t
+
+include $(top_builddir)/make.tmpl
+
+INCLUDES += -I$(top_srcdir)/libdm
+DM_DEPS = $(top_builddir)/libdm/libdevmapper.so
+DM_LIBS = -ldevmapper $(LIBS)
+
+bitset_t: bitset_t.o $(DM_DEPS)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ bitset_t.o $(DM_LIBS)
--- /dev/null
+bitset iteration:$TEST_TOOL ./bitset_t
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "libdevmapper.h"
+
+#include <assert.h>
+
+enum {
+ NR_BITS = 137
+};
+
+static void test_get_next(struct dm_pool *mem)
+{
+ int i, j, last, first;
+ dm_bitset_t bs = dm_bitset_create(mem, NR_BITS);
+
+ for (i = 0; i < NR_BITS; i++)
+ assert(!dm_bit(bs, i));
+
+ for (i = 0, j = 1; i < NR_BITS; i += j, j++)
+ dm_bit_set(bs, i);
+
+ first = 1;
+ for (i = 0, j = 1; i < NR_BITS; i += j, j++) {
+ if (first) {
+ last = dm_bit_get_first(bs);
+ first = 0;
+ } else
+ last = dm_bit_get_next(bs, last);
+
+ assert(last == i);
+ }
+
+ assert(dm_bit_get_next(bs, last) == -1);
+}
+
+static void bit_flip(dm_bitset_t bs, int bit)
+{
+ int old = dm_bit(bs, bit);
+ if (old)
+ dm_bit_clear(bs, bit);
+ else
+ dm_bit_set(bs, bit);
+}
+
+static void test_equal(struct dm_pool *mem)
+{
+ dm_bitset_t bs1 = dm_bitset_create(mem, NR_BITS);
+ dm_bitset_t bs2 = dm_bitset_create(mem, NR_BITS);
+
+ int i, j;
+ for (i = 0, j = 1; i < NR_BITS; i += j, j++) {
+ dm_bit_set(bs1, i);
+ dm_bit_set(bs2, i);
+ }
+
+ assert(dm_bitset_equal(bs1, bs2));
+ assert(dm_bitset_equal(bs2, bs1));
+
+ for (i = 0; i < NR_BITS; i++) {
+ bit_flip(bs1, i);
+ assert(!dm_bitset_equal(bs1, bs2));
+ assert(!dm_bitset_equal(bs2, bs1));
+
+ assert(dm_bitset_equal(bs1, bs1)); /* comparing with self */
+ bit_flip(bs1, i);
+ }
+}
+
+static void test_and(struct dm_pool *mem)
+{
+ dm_bitset_t bs1 = dm_bitset_create(mem, NR_BITS);
+ dm_bitset_t bs2 = dm_bitset_create(mem, NR_BITS);
+ dm_bitset_t bs3 = dm_bitset_create(mem, NR_BITS);
+
+ int i, j;
+ for (i = 0, j = 1; i < NR_BITS; i += j, j++) {
+ dm_bit_set(bs1, i);
+ dm_bit_set(bs2, i);
+ }
+
+ dm_bit_and(bs3, bs1, bs2);
+
+ assert(dm_bitset_equal(bs1, bs2));
+ assert(dm_bitset_equal(bs1, bs3));
+ assert(dm_bitset_equal(bs2, bs3));
+
+ dm_bit_clear_all(bs1);
+ dm_bit_clear_all(bs2);
+
+ for (i = 0; i < NR_BITS; i++) {
+ if (i % 2)
+ dm_bit_set(bs1, i);
+ else
+ dm_bit_set(bs2, i);
+ }
+
+ dm_bit_and(bs3, bs1, bs2);
+ for (i = 0; i < NR_BITS; i++)
+ assert(!dm_bit(bs3, i));
+}
+
+int main(int argc, char **argv)
+{
+ typedef void (*test_fn)(struct dm_pool *);
+ static test_fn tests[] = {
+ test_get_next,
+ test_equal,
+ test_and
+ };
+
+ int i;
+ for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
+ struct dm_pool *mem = dm_pool_create("bitset test", 1024);
+ assert(mem);
+ tests[i](mem);
+ dm_pool_destroy(mem);
+ }
+
+ return 0;
+}
+
--- /dev/null
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+VPATH = @srcdir@
+
+SOURCES=\
+ pool_valgrind_t.c
+
+TARGETS=\
+ pool_valgrind_t
+
+include $(top_builddir)/make.tmpl
+DM_LIBS = -ldevmapper $(LIBS)
+
+pool_valgrind_t: pool_valgrind_t.o
+ $(CC) $(CFLAGS) -o $@ pool_valgrind_t.o $(LDFLAGS) $(DM_LIBS)
+
--- /dev/null
+valgrind pool awareness:valgrind ./pool_valgrind_t 2>&1 | ./check_results
--- /dev/null
+#!/usr/bin/env ruby1.9
+
+require 'pp'
+
+patterns = [
+ /Invalid read of size 1/,
+ /Invalid write of size 1/,
+ /Invalid read of size 1/,
+ /still reachable: [0-9,]+ bytes in 3 blocks/
+ ]
+
+lines = STDIN.readlines
+pp lines
+
+result = catch(:done) do
+ patterns.each do |pat|
+ loop do
+ throw(:done, false) if lines.size == 0
+
+ line = lines.shift
+ if line =~ pat
+ STDERR.puts "matched #{pat}"
+ break;
+ end
+ end
+ end
+
+ throw(:done, true)
+end
+
+exit(result ? 0 : 1)
--- /dev/null
+#include "libdevmapper.h"
+
+#include <assert.h>
+
+/*
+ * Checks that valgrind is picking up unallocated pool memory as
+ * uninitialised, even if the chunk has been recycled.
+ *
+ * $ valgrind --track-origins=yes ./pool_valgrind_t
+ *
+ * ==7023== Memcheck, a memory error detector
+ * ==7023== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
+ * ==7023== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
+ * ==7023== Command: ./pool_valgrind_t
+ * ==7023==
+ * first branch worked (as expected)
+ * ==7023== Conditional jump or move depends on uninitialised value(s)
+ * ==7023== at 0x4009AC: main (in /home/ejt/work/lvm2/unit-tests/mm/pool_valgrind_t)
+ * ==7023== Uninitialised value was created by a client request
+ * ==7023== at 0x4E40CB8: dm_pool_free (in /home/ejt/work/lvm2/libdm/ioctl/libdevmapper.so.1.02)
+ * ==7023== by 0x4009A8: main (in /home/ejt/work/lvm2/unit-tests/mm/pool_valgrind_t)
+ * ==7023==
+ * second branch worked (valgrind should have flagged this as an error)
+ * ==7023==
+ * ==7023== HEAP SUMMARY:
+ * ==7023== in use at exit: 0 bytes in 0 blocks
+ * ==7023== total heap usage: 2 allocs, 2 frees, 2,104 bytes allocated
+ * ==7023==
+ * ==7023== All heap blocks were freed -- no leaks are possible
+ * ==7023==
+ * ==7023== For counts of detected and suppressed errors, rerun with: -v
+ * ==7023== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
+ */
+
+#define COUNT 10
+
+static void check_free()
+{
+ int i;
+ char *blocks[COUNT];
+ struct dm_pool *p = dm_pool_create("blah", 1024);
+
+ for (i = 0; i < COUNT; i++)
+ blocks[i] = dm_pool_alloc(p, 37);
+
+ /* check we can access the last block */
+ blocks[COUNT - 1][0] = 'E';
+ if (blocks[COUNT - 1][0] == 'E')
+ printf("first branch worked (as expected)\n");
+
+ dm_pool_free(p, blocks[5]);
+
+ if (blocks[COUNT - 1][0] == 'E')
+ printf("second branch worked (valgrind should have flagged this as an error)\n");
+
+ dm_pool_destroy(p);
+}
+
+/* Checks that freed chunks are marked NOACCESS */
+static void check_free2()
+{
+ struct dm_pool *p = dm_pool_create("", 900); /* 900 will get
+ * rounded up to 1024,
+ * 1024 would have got
+ * rounded up to
+ * 2048 */
+ char *data1, *data2;
+
+ assert(p);
+ data1 = dm_pool_alloc(p, 123);
+ assert(data1);
+
+ data1 = dm_pool_alloc(p, 1024);
+ assert(data1);
+
+ data2 = dm_pool_alloc(p, 123);
+ assert(data2);
+
+ data2[0] = 'A'; /* should work fine */
+
+ dm_pool_free(p, data1);
+
+ /*
+ * so now the first chunk is active, the second chunk has become
+ * the free one.
+ */
+ data2[0] = 'B'; /* should prompt an invalid write error */
+
+ dm_pool_destroy(p);
+}
+
+static void check_alignment()
+{
+ /*
+ * Pool always tries to allocate blocks with particular alignment.
+ * So there are potentially small gaps between allocations. This
+ * test checks that valgrind is spotting illegal accesses to these
+ * gaps.
+ */
+
+ int i, sum;
+ struct dm_pool *p = dm_pool_create("blah", 1024);
+ char *data1, *data2;
+ char buffer[16];
+
+
+ data1 = dm_pool_alloc_aligned(p, 1, 4);
+ assert(data1);
+ data2 = dm_pool_alloc_aligned(p, 1, 4);
+ assert(data1);
+
+ snprintf(buffer, sizeof(buffer), "%c", *(data1 + 1)); /* invalid read size 1 */
+ dm_pool_destroy(p);
+}
+
+/*
+ * Looking at the code I'm not sure allocations that are near the chunk
+ * size are working. So this test is trying to exhibit a specific problem.
+ */
+static void check_allocation_near_chunk_size()
+{
+ int i;
+ char *data;
+ struct dm_pool *p = dm_pool_create("", 900);
+
+ /*
+ * allocate a lot and then free everything so we know there
+ * is a spare chunk.
+ */
+ for (i = 0; i < 1000; i++) {
+ data = dm_pool_alloc(p, 37);
+ memset(data, 0, 37);
+ assert(data);
+ }
+
+ dm_pool_empty(p);
+
+ /* now we allocate something close to the chunk size ... */
+ data = dm_pool_alloc(p, 1020);
+ assert(data);
+ memset(data, 0, 1020);
+
+ dm_pool_destroy(p);
+}
+
+/* FIXME: test the dbg_malloc at exit (this test should be in dbg_malloc) */
+static void check_leak_detection()
+{
+ int i;
+ struct dm_pool *p = dm_pool_create("", 1024);
+
+ for (i = 0; i < 10; i++)
+ dm_pool_alloc(p, (i + 1) * 37);
+}
+
+/* we shouldn't get any errors from this one */
+static void check_object_growth()
+{
+ int i;
+ struct dm_pool *p = dm_pool_create("", 32);
+ char data[100];
+ void *obj;
+
+ memset(data, 0, sizeof(data));
+
+ dm_pool_begin_object(p, 43);
+ for (i = 1; i < 100; i++)
+ dm_pool_grow_object(p, data, i);
+ obj = dm_pool_end_object(p);
+
+ dm_pool_destroy(p);
+}
+
+int main(int argc, char **argv)
+{
+ check_free();
+ check_free2();
+ check_alignment();
+ check_allocation_near_chunk_size();
+ check_leak_detection();
+ check_object_growth();
+ return 0;
+}
--- /dev/null
+#
+# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+# Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+
+SOURCES=\
+ parse_t.c \
+ matcher_t.c
+
+TARGETS=\
+ parse_t \
+ matcher_t
+
+include $(top_builddir)/make.tmpl
+
+INCLUDES += -I$(top_srcdir)/libdm
+DM_DEPS = $(top_builddir)/libdm/libdevmapper.so
+DM_LIBS = -ldevmapper $(LIBS)
+
+parse_t: parse_t.o $(DM_DEPS)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ parse_t.o $(DM_LIBS)
+
+matcher_t: matcher_t.o $(DM_DEPS)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ matcher_t.o $(DM_LIBS)
--- /dev/null
+dfa matching:$TEST_TOOL ./matcher_t --fingerprint dev_patterns < devices.list > matcher_t.output && diff -u matcher_t.expected matcher_t.output
+dfa matching:$TEST_TOOL ./matcher_t --fingerprint random_regexes < /dev/null > matcher_t.output && diff -u matcher_t.expected2 matcher_t.output
+dfa with non-print regex chars:$TEST_TOOL ./matcher_t nonprint_regexes < nonprint_input > matcher_t.output && diff -u matcher_t.expected3 matcher_t.output
\ No newline at end of file
--- /dev/null
+"loop/[0-9]+"
+"hd[a-d][0-5]+"
--- /dev/null
+/dev
+/dev/.devfsd
+/dev/cpu
+/dev/cpu/mtrr
+/dev/netlink
+/dev/netlink/route
+/dev/netlink/skip
+/dev/netlink/USERSOCK
+/dev/netlink/fwmonitor
+/dev/netlink/ARPD
+/dev/netlink/ROUTE6
+/dev/netlink/IP6_FW
+/dev/netlink/tap0
+/dev/netlink/tap1
+/dev/netlink/tap2
+/dev/netlink/tap3
+/dev/netlink/tap4
+/dev/netlink/tap5
+/dev/netlink/tap6
+/dev/netlink/tap7
+/dev/netlink/tap8
+/dev/netlink/tap9
+/dev/netlink/tap10
+/dev/netlink/tap11
+/dev/netlink/tap12
+/dev/netlink/tap13
+/dev/netlink/tap14
+/dev/netlink/tap15
+/dev/shm
+/dev/mem
+/dev/kmem
+/dev/null
+/dev/port
+/dev/zero
+/dev/full
+/dev/random
+/dev/urandom
+/dev/tty
+/dev/console
+/dev/vc
+/dev/vc/1
+/dev/vc/2
+/dev/vc/3
+/dev/vc/4
+/dev/vc/5
+/dev/vc/6
+/dev/vc/7
+/dev/vc/8
+/dev/vc/9
+/dev/vc/10
+/dev/vc/11
+/dev/vc/12
+/dev/vc/13
+/dev/vc/14
+/dev/vc/15
+/dev/vc/16
+/dev/vc/17
+/dev/vc/18
+/dev/vc/19
+/dev/vc/20
+/dev/vc/21
+/dev/vc/22
+/dev/vc/23
+/dev/vc/24
+/dev/vc/25
+/dev/vc/26
+/dev/vc/27
+/dev/vc/28
+/dev/vc/29
+/dev/vc/30
+/dev/vc/31
+/dev/vc/32
+/dev/vc/33
+/dev/vc/34
+/dev/vc/35
+/dev/vc/36
+/dev/vc/37
+/dev/vc/38
+/dev/vc/39
+/dev/vc/40
+/dev/vc/41
+/dev/vc/42
+/dev/vc/43
+/dev/vc/44
+/dev/vc/45
+/dev/vc/46
+/dev/vc/47
+/dev/vc/48
+/dev/vc/49
+/dev/vc/50
+/dev/vc/51
+/dev/vc/52
+/dev/vc/53
+/dev/vc/54
+/dev/vc/55
+/dev/vc/56
+/dev/vc/57
+/dev/vc/58
+/dev/vc/59
+/dev/vc/60
+/dev/vc/61
+/dev/vc/62
+/dev/vc/63
+/dev/vc/0
+/dev/ptmx
+/dev/misc
+/dev/misc/psaux
+/dev/pty
+/dev/pty/m0
+/dev/pty/m1
+/dev/pty/m2
+/dev/pty/m3
+/dev/pty/m4
+/dev/pty/m5
+/dev/pty/m6
+/dev/pty/m7
+/dev/pty/m8
+/dev/pty/m9
+/dev/pty/m10
+/dev/pty/m11
+/dev/pty/m12
+/dev/pty/m13
+/dev/pty/m14
+/dev/pty/m15
+/dev/pty/m16
+/dev/pty/m17
+/dev/pty/m18
+/dev/pty/m19
+/dev/pty/m20
+/dev/pty/m21
+/dev/pty/m22
+/dev/pty/m23
+/dev/pty/m24
+/dev/pty/m25
+/dev/pty/m26
+/dev/pty/m27
+/dev/pty/m28
+/dev/pty/m29
+/dev/pty/m30
+/dev/pty/m31
+/dev/pty/m32
+/dev/pty/m33
+/dev/pty/m34
+/dev/pty/m35
+/dev/pty/m36
+/dev/pty/m37
+/dev/pty/m38
+/dev/pty/m39
+/dev/pty/m40
+/dev/pty/m41
+/dev/pty/m42
+/dev/pty/m43
+/dev/pty/m44
+/dev/pty/m45
+/dev/pty/m46
+/dev/pty/m47
+/dev/pty/m48
+/dev/pty/m49
+/dev/pty/m50
+/dev/pty/m51
+/dev/pty/m52
+/dev/pty/m53
+/dev/pty/m54
+/dev/pty/m55
+/dev/pty/m56
+/dev/pty/m57
+/dev/pty/m58
+/dev/pty/m59
+/dev/pty/m60
+/dev/pty/m61
+/dev/pty/m62
+/dev/pty/m63
+/dev/pty/m64
+/dev/pty/m65
+/dev/pty/m66
+/dev/pty/m67
+/dev/pty/m68
+/dev/pty/m69
+/dev/pty/m70
+/dev/pty/m71
+/dev/pty/m72
+/dev/pty/m73
+/dev/pty/m74
+/dev/pty/m75
+/dev/pty/m76
+/dev/pty/m77
+/dev/pty/m78
+/dev/pty/m79
+/dev/pty/m80
+/dev/pty/m81
+/dev/pty/m82
+/dev/pty/m83
+/dev/pty/m84
+/dev/pty/m85
+/dev/pty/m86
+/dev/pty/m87
+/dev/pty/m88
+/dev/pty/m89
+/dev/pty/m90
+/dev/pty/m91
+/dev/pty/m92
+/dev/pty/m93
+/dev/pty/m94
+/dev/pty/m95
+/dev/pty/m96
+/dev/pty/m97
+/dev/pty/m98
+/dev/pty/m99
+/dev/pty/m100
+/dev/pty/m101
+/dev/pty/m102
+/dev/pty/m103
+/dev/pty/m104
+/dev/pty/m105
+/dev/pty/m106
+/dev/pty/m107
+/dev/pty/m108
+/dev/pty/m109
+/dev/pty/m110
+/dev/pty/m111
+/dev/pty/m112
+/dev/pty/m113
+/dev/pty/m114
+/dev/pty/m115
+/dev/pty/m116
+/dev/pty/m117
+/dev/pty/m118
+/dev/pty/m119
+/dev/pty/m120
+/dev/pty/m121
+/dev/pty/m122
+/dev/pty/m123
+/dev/pty/m124
+/dev/pty/m125
+/dev/pty/m126
+/dev/pty/m127
+/dev/pty/m128
+/dev/pty/m129
+/dev/pty/m130
+/dev/pty/m131
+/dev/pty/m132
+/dev/pty/m133
+/dev/pty/m134
+/dev/pty/m135
+/dev/pty/m136
+/dev/pty/m137
+/dev/pty/m138
+/dev/pty/m139
+/dev/pty/m140
+/dev/pty/m141
+/dev/pty/m142
+/dev/pty/m143
+/dev/pty/m144
+/dev/pty/m145
+/dev/pty/m146
+/dev/pty/m147
+/dev/pty/m148
+/dev/pty/m149
+/dev/pty/m150
+/dev/pty/m151
+/dev/pty/m152
+/dev/pty/m153
+/dev/pty/m154
+/dev/pty/m155
+/dev/pty/m156
+/dev/pty/m157
+/dev/pty/m158
+/dev/pty/m159
+/dev/pty/m160
+/dev/pty/m161
+/dev/pty/m162
+/dev/pty/m163
+/dev/pty/m164
+/dev/pty/m165
+/dev/pty/m166
+/dev/pty/m167
+/dev/pty/m168
+/dev/pty/m169
+/dev/pty/m170
+/dev/pty/m171
+/dev/pty/m172
+/dev/pty/m173
+/dev/pty/m174
+/dev/pty/m175
+/dev/pty/m176
+/dev/pty/m177
+/dev/pty/m178
+/dev/pty/m179
+/dev/pty/m180
+/dev/pty/m181
+/dev/pty/m182
+/dev/pty/m183
+/dev/pty/m184
+/dev/pty/m185
+/dev/pty/m186
+/dev/pty/m187
+/dev/pty/m188
+/dev/pty/m189
+/dev/pty/m190
+/dev/pty/m191
+/dev/pty/m192
+/dev/pty/m193
+/dev/pty/m194
+/dev/pty/m195
+/dev/pty/m196
+/dev/pty/m197
+/dev/pty/m198
+/dev/pty/m199
+/dev/pty/m200
+/dev/pty/m201
+/dev/pty/m202
+/dev/pty/m203
+/dev/pty/m204
+/dev/pty/m205
+/dev/pty/m206
+/dev/pty/m207
+/dev/pty/m208
+/dev/pty/m209
+/dev/pty/m210
+/dev/pty/m211
+/dev/pty/m212
+/dev/pty/m213
+/dev/pty/m214
+/dev/pty/m215
+/dev/pty/m216
+/dev/pty/m217
+/dev/pty/m218
+/dev/pty/m219
+/dev/pty/m220
+/dev/pty/m221
+/dev/pty/m222
+/dev/pty/m223
+/dev/pty/m224
+/dev/pty/m225
+/dev/pty/m226
+/dev/pty/m227
+/dev/pty/m228
+/dev/pty/m229
+/dev/pty/m230
+/dev/pty/m231
+/dev/pty/m232
+/dev/pty/m233
+/dev/pty/m234
+/dev/pty/m235
+/dev/pty/m236
+/dev/pty/m237
+/dev/pty/m238
+/dev/pty/m239
+/dev/pty/m240
+/dev/pty/m241
+/dev/pty/m242
+/dev/pty/m243
+/dev/pty/m244
+/dev/pty/m245
+/dev/pty/m246
+/dev/pty/m247
+/dev/pty/m248
+/dev/pty/m249
+/dev/pty/m250
+/dev/pty/m251
+/dev/pty/m252
+/dev/pty/m253
+/dev/pty/m254
+/dev/pty/m255
+/dev/pts
+/dev/pts/0
+/dev/pts/1
+/dev/pts/2
+/dev/pts/3
+/dev/pts/4
+/dev/pts/5
+/dev/pts/6
+/dev/pts/7
+/dev/vcc
+/dev/vcc/0
+/dev/vcc/a
+/dev/vcc/1
+/dev/vcc/a1
+/dev/vcc/2
+/dev/vcc/a2
+/dev/vcc/3
+/dev/vcc/a3
+/dev/vcc/5
+/dev/vcc/a5
+/dev/vcc/4
+/dev/vcc/a4
+/dev/vcc/6
+/dev/vcc/a6
+/dev/vcc/7
+/dev/vcc/a7
+/dev/tts
+/dev/tts/0
+/dev/cua
+/dev/cua/0
+/dev/ide
+/dev/ide/host0
+/dev/ide/host0/bus0
+/dev/ide/host0/bus0/target0
+/dev/ide/host0/bus0/target0/lun0
+/dev/ide/host0/bus0/target0/lun0/disc
+/dev/ide/host0/bus0/target0/lun0/part1
+/dev/ide/host0/bus0/target0/lun0/part2
+/dev/ide/host0/bus0/target0/lun0/part3
+/dev/ide/host0/bus0/target0/lun0/part4
+/dev/ide/host0/bus0/target0/lun0/part5
+/dev/ide/host0/bus0/target0/lun0/part6
+/dev/ide/host0/bus0/target0/lun0/part7
+/dev/ide/host0/bus0/target0/lun0/part8
+/dev/ide/host0/bus0/target1
+/dev/ide/host0/bus0/target1/lun0
+/dev/ide/host0/bus0/target1/lun0/disc
+/dev/ide/host0/bus0/target1/lun0/part1
+/dev/ide/host0/bus1
+/dev/ide/host0/bus1/target0
+/dev/ide/host0/bus1/target0/lun0
+/dev/ide/host0/bus1/target0/lun0/disc
+/dev/ide/host0/bus1/target0/lun0/part1
+/dev/ide/host0/bus1/target1
+/dev/ide/host0/bus1/target1/lun0
+/dev/discs
+/dev/discs/disc0
+/dev/discs/disc1
+/dev/discs/disc2
+/dev/floppy
+/dev/floppy/0u1440
+/dev/floppy/0u1680
+/dev/floppy/0u1722
+/dev/floppy/0u1743
+/dev/floppy/0u1760
+/dev/floppy/0u1920
+/dev/floppy/0u1840
+/dev/floppy/0u1600
+/dev/floppy/0u360
+/dev/floppy/0u720
+/dev/floppy/0u820
+/dev/floppy/0u830
+/dev/floppy/0u1040
+/dev/floppy/0u1120
+/dev/floppy/0u800
+/dev/floppy/0
+/dev/loop
+/dev/loop/0
+/dev/loop/1
+/dev/loop/2
+/dev/loop/3
+/dev/loop/4
+/dev/loop/5
+/dev/loop/6
+/dev/loop/7
+/dev/cdroms
+/dev/sound
+/dev/sound/dsp
+/dev/sound/dsp1
+/dev/sound/mixer
+/dev/sound/midi
+/dev/usb
+/dev/root
+/dev/initctl
+/dev/xconsole
+/dev/fd
+/dev/stdin
+/dev/stdout
+/dev/stderr
+/dev/route
+/dev/skip
+/dev/USERSOCK
+/dev/fwmonitor
+/dev/ARPD
+/dev/ROUTE6
+/dev/IP6_FW
+/dev/tap0
+/dev/tap1
+/dev/tap2
+/dev/tap3
+/dev/tap4
+/dev/tap5
+/dev/tap6
+/dev/tap7
+/dev/tap8
+/dev/tap9
+/dev/tap10
+/dev/tap11
+/dev/tap12
+/dev/tap13
+/dev/tap14
+/dev/tap15
+/dev/tty1
+/dev/tty2
+/dev/tty3
+/dev/tty4
+/dev/tty5
+/dev/tty6
+/dev/tty7
+/dev/tty8
+/dev/tty9
+/dev/tty10
+/dev/tty11
+/dev/tty12
+/dev/tty13
+/dev/tty14
+/dev/tty15
+/dev/tty16
+/dev/tty17
+/dev/tty18
+/dev/tty19
+/dev/tty20
+/dev/tty21
+/dev/tty22
+/dev/tty23
+/dev/tty24
+/dev/tty25
+/dev/tty26
+/dev/tty27
+/dev/tty28
+/dev/tty29
+/dev/tty30
+/dev/tty31
+/dev/tty32
+/dev/tty33
+/dev/tty34
+/dev/tty35
+/dev/tty36
+/dev/tty37
+/dev/tty38
+/dev/tty39
+/dev/tty40
+/dev/tty41
+/dev/tty42
+/dev/tty43
+/dev/tty44
+/dev/tty45
+/dev/tty46
+/dev/tty47
+/dev/tty48
+/dev/tty49
+/dev/tty50
+/dev/tty51
+/dev/tty52
+/dev/tty53
+/dev/tty54
+/dev/tty55
+/dev/tty56
+/dev/tty57
+/dev/tty58
+/dev/tty59
+/dev/tty60
+/dev/tty61
+/dev/tty62
+/dev/tty63
+/dev/tty0
+/dev/psaux
+/dev/ptyp0
+/dev/ptyp1
+/dev/ptyp2
+/dev/ptyp3
+/dev/ptyp4
+/dev/ptyp5
+/dev/ptyp6
+/dev/ptyp7
+/dev/ptyp8
+/dev/ptyp9
+/dev/ptypa
+/dev/ptypb
+/dev/ptypc
+/dev/ptypd
+/dev/ptype
+/dev/ptypf
+/dev/ptyq0
+/dev/ptyq1
+/dev/ptyq2
+/dev/ptyq3
+/dev/ptyq4
+/dev/ptyq5
+/dev/ptyq6
+/dev/ptyq7
+/dev/ptyq8
+/dev/ptyq9
+/dev/ptyqa
+/dev/ptyqb
+/dev/ptyqc
+/dev/ptyqd
+/dev/ptyqe
+/dev/ptyqf
+/dev/ptyr0
+/dev/ptyr1
+/dev/ptyr2
+/dev/ptyr3
+/dev/ptyr4
+/dev/ptyr5
+/dev/ptyr6
+/dev/ptyr7
+/dev/ptyr8
+/dev/ptyr9
+/dev/ptyra
+/dev/ptyrb
+/dev/ptyrc
+/dev/ptyrd
+/dev/ptyre
+/dev/ptyrf
+/dev/ptys0
+/dev/ptys1
+/dev/ptys2
+/dev/ptys3
+/dev/ptys4
+/dev/ptys5
+/dev/ptys6
+/dev/ptys7
+/dev/ptys8
+/dev/ptys9
+/dev/ptysa
+/dev/ptysb
+/dev/ptysc
+/dev/ptysd
+/dev/ptyse
+/dev/ptysf
+/dev/ptyt0
+/dev/ptyt1
+/dev/ptyt2
+/dev/ptyt3
+/dev/ptyt4
+/dev/ptyt5
+/dev/ptyt6
+/dev/ptyt7
+/dev/ptyt8
+/dev/ptyt9
+/dev/ptyta
+/dev/ptytb
+/dev/ptytc
+/dev/ptytd
+/dev/ptyte
+/dev/ptytf
+/dev/ptyu0
+/dev/ptyu1
+/dev/ptyu2
+/dev/ptyu3
+/dev/ptyu4
+/dev/ptyu5
+/dev/ptyu6
+/dev/ptyu7
+/dev/ptyu8
+/dev/ptyu9
+/dev/ptyua
+/dev/ptyub
+/dev/ptyuc
+/dev/ptyud
+/dev/ptyue
+/dev/ptyuf
+/dev/ptyv0
+/dev/ptyv1
+/dev/ptyv2
+/dev/ptyv3
+/dev/ptyv4
+/dev/ptyv5
+/dev/ptyv6
+/dev/ptyv7
+/dev/ptyv8
+/dev/ptyv9
+/dev/ptyva
+/dev/ptyvb
+/dev/ptyvc
+/dev/ptyvd
+/dev/ptyve
+/dev/ptyvf
+/dev/ptyw0
+/dev/ptyw1
+/dev/ptyw2
+/dev/ptyw3
+/dev/ptyw4
+/dev/ptyw5
+/dev/ptyw6
+/dev/ptyw7
+/dev/ptyw8
+/dev/ptyw9
+/dev/ptywa
+/dev/ptywb
+/dev/ptywc
+/dev/ptywd
+/dev/ptywe
+/dev/ptywf
+/dev/ptyx0
+/dev/ptyx1
+/dev/ptyx2
+/dev/ptyx3
+/dev/ptyx4
+/dev/ptyx5
+/dev/ptyx6
+/dev/ptyx7
+/dev/ptyx8
+/dev/ptyx9
+/dev/ptyxa
+/dev/ptyxb
+/dev/ptyxc
+/dev/ptyxd
+/dev/ptyxe
+/dev/ptyxf
+/dev/ptyy0
+/dev/ptyy1
+/dev/ptyy2
+/dev/ptyy3
+/dev/ptyy4
+/dev/ptyy5
+/dev/ptyy6
+/dev/ptyy7
+/dev/ptyy8
+/dev/ptyy9
+/dev/ptyya
+/dev/ptyyb
+/dev/ptyyc
+/dev/ptyyd
+/dev/ptyye
+/dev/ptyyf
+/dev/ptyz0
+/dev/ptyz1
+/dev/ptyz2
+/dev/ptyz3
+/dev/ptyz4
+/dev/ptyz5
+/dev/ptyz6
+/dev/ptyz7
+/dev/ptyz8
+/dev/ptyz9
+/dev/ptyza
+/dev/ptyzb
+/dev/ptyzc
+/dev/ptyzd
+/dev/ptyze
+/dev/ptyzf
+/dev/ptya0
+/dev/ptya1
+/dev/ptya2
+/dev/ptya3
+/dev/ptya4
+/dev/ptya5
+/dev/ptya6
+/dev/ptya7
+/dev/ptya8
+/dev/ptya9
+/dev/ptyaa
+/dev/ptyab
+/dev/ptyac
+/dev/ptyad
+/dev/ptyae
+/dev/ptyaf
+/dev/ptyb0
+/dev/ptyb1
+/dev/ptyb2
+/dev/ptyb3
+/dev/ptyb4
+/dev/ptyb5
+/dev/ptyb6
+/dev/ptyb7
+/dev/ptyb8
+/dev/ptyb9
+/dev/ptyba
+/dev/ptybb
+/dev/ptybc
+/dev/ptybd
+/dev/ptybe
+/dev/ptybf
+/dev/ptyc0
+/dev/ptyc1
+/dev/ptyc2
+/dev/ptyc3
+/dev/ptyc4
+/dev/ptyc5
+/dev/ptyc6
+/dev/ptyc7
+/dev/ptyc8
+/dev/ptyc9
+/dev/ptyca
+/dev/ptycb
+/dev/ptycc
+/dev/ptycd
+/dev/ptyce
+/dev/ptycf
+/dev/ptyd0
+/dev/ptyd1
+/dev/ptyd2
+/dev/ptyd3
+/dev/ptyd4
+/dev/ptyd5
+/dev/ptyd6
+/dev/ptyd7
+/dev/ptyd8
+/dev/ptyd9
+/dev/ptyda
+/dev/ptydb
+/dev/ptydc
+/dev/ptydd
+/dev/ptyde
+/dev/ptydf
+/dev/ptye0
+/dev/ptye1
+/dev/ptye2
+/dev/ptye3
+/dev/ptye4
+/dev/ptye5
+/dev/ptye6
+/dev/ptye7
+/dev/ptye8
+/dev/ptye9
+/dev/ptyea
+/dev/ptyeb
+/dev/ptyec
+/dev/ptyed
+/dev/ptyee
+/dev/ptyef
+/dev/vcs
+/dev/vcsa
+/dev/vcs1
+/dev/vcsa1
+/dev/ttyS0
+/dev/cua0
+/dev/hda
+/dev/hda1
+/dev/hda2
+/dev/hda3
+/dev/hda4
+/dev/hda5
+/dev/hda6
+/dev/hda7
+/dev/hda8
+/dev/hdb
+/dev/hdb1
+/dev/hdc
+/dev/hdc1
+/dev/fd0u1440
+/dev/fd0u1680
+/dev/fd0u1722
+/dev/fd0u1743
+/dev/fd0u1760
+/dev/fd0u1920
+/dev/fd0u1840
+/dev/fd0u1600
+/dev/fd0u360
+/dev/fd0u720
+/dev/fd0u820
+/dev/fd0u830
+/dev/fd0u1040
+/dev/fd0u1120
+/dev/fd0u800
+/dev/fd0
+/dev/loop0
+/dev/loop1
+/dev/loop2
+/dev/loop3
+/dev/loop4
+/dev/loop5
+/dev/loop6
+/dev/loop7
+/dev/dsp
+/dev/dsp1
+/dev/mixer
+/dev/midi
+/dev/lvm
+/dev/vg0
+/dev/vg0/group
+/dev/vg0/packages
+/dev/vg0/photos
+/dev/vg0/music
+/dev/log
+/dev/MAKEDEV
+/dev/printer
+/dev/vcs2
+/dev/vcsa2
+/dev/vcs3
+/dev/vcsa3
+/dev/vcs5
+/dev/vcsa5
+/dev/vcs4
+/dev/vcsa4
+/dev/vcs6
+/dev/vcsa6
+/dev/nvidia0
+/dev/nvidia1
+/dev/nvidia2
+/dev/nvidia3
+/dev/nvidiactl
+/dev/vcs7
+/dev/vcsa7
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "libdevmapper.h"
+#include "log.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+
+static int _read_spec(const char *file, char ***regex, int *nregex)
+{
+ char buffer[1024], *start, *ptr;
+ FILE *fp = fopen(file, "r");
+ int asize = 100;
+ char **rx = dm_malloc(sizeof(*rx) * asize);
+ int nr = 0;
+
+ if (!fp)
+ return 0;
+
+ while (fgets(buffer, sizeof(buffer),fp)) {
+
+ /* trim leading whitespace */
+ for (ptr = buffer; *ptr && isspace((int) *ptr); ptr++);
+
+ if (!*ptr || *ptr == '#')
+ continue;
+
+ if (*ptr == '\"') {
+ ptr++;
+ start = ptr;
+ while (*ptr && *ptr != '\"') {
+ if (*ptr == '\\')
+ ptr++;
+ ptr++;
+ }
+
+ if (!*ptr) {
+ fprintf(stderr, "Formatting error : "
+ "No terminating quote\n");
+ return 0;
+ }
+
+ rx[nr] = dm_malloc((ptr - start) + 1);
+ strncpy(rx[nr], start, ptr - start);
+ rx[nr][ptr - start] = '\0';
+ nr++;
+ } else {
+ fprintf(stderr, "%s", ptr);
+ fprintf(stderr, "Formatting error : \"<regex>\" "
+ "<token_name>\n");
+ return 0;
+ }
+ }
+
+ *regex = rx;
+ *nregex = nr;
+ return 1;
+}
+
+static void _free_regex(char **regex, int nregex)
+{
+ int i;
+ for (i = 0; i < nregex; i++)
+ dm_free(regex[i]);
+
+ dm_free(regex);
+}
+
+static void _scan_input(struct dm_regex *m, char **regex)
+{
+ char buffer[256], *ptr;
+ int r;
+
+ while (fgets(buffer, sizeof(buffer), stdin)) {
+ if ((ptr = strchr(buffer, '\n')))
+ *ptr = '\0';
+
+ r = dm_regex_match(m, buffer);
+
+ if (r >= 0)
+ printf("%s : %s\n", buffer, regex[r]);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct dm_pool *mem;
+ struct dm_regex *scanner;
+ char **regex;
+ int nregex;
+ int ret = 0;
+ int want_finger_print = 0, i;
+ const char *pattern_file = NULL;
+
+ for (i = 1; i < argc; i++)
+ if (!strcmp(argv[i], "--fingerprint"))
+ want_finger_print = 1;
+
+ else
+ pattern_file = argv[i];
+
+ if (!pattern_file) {
+ fprintf(stderr, "Usage : %s [--fingerprint] <pattern_file>\n", argv[0]);
+ exit(1);
+ }
+
+ dm_log_init_verbose(_LOG_DEBUG);
+
+ if (!(mem = dm_pool_create("match_regex", 10 * 1024))) {
+ fprintf(stderr, "Couldn't create pool\n");
+ ret = 2;
+ goto err;
+ }
+
+ if (!_read_spec(pattern_file, ®ex, &nregex)) {
+ fprintf(stderr, "Couldn't read the lex specification\n");
+ ret = 3;
+ goto err;
+ }
+
+ if (!(scanner = dm_regex_create(mem, (const char **)regex, nregex))) {
+ fprintf(stderr, "Couldn't build the lexer\n");
+ ret = 4;
+ goto err;
+ }
+
+ if (want_finger_print)
+ printf("fingerprint: %x\n", dm_regex_fingerprint(scanner));
+ _scan_input(scanner, regex);
+ _free_regex(regex, nregex);
+
+ err:
+ dm_pool_destroy(mem);
+
+ return ret;
+}
--- /dev/null
+fingerprint: 352b6c4f
+/dev/loop/0 : loop/[0-9]+
+/dev/loop/1 : loop/[0-9]+
+/dev/loop/2 : loop/[0-9]+
+/dev/loop/3 : loop/[0-9]+
+/dev/loop/4 : loop/[0-9]+
+/dev/loop/5 : loop/[0-9]+
+/dev/loop/6 : loop/[0-9]+
+/dev/loop/7 : loop/[0-9]+
+/dev/hda1 : hd[a-d][0-5]+
+/dev/hda2 : hd[a-d][0-5]+
+/dev/hda3 : hd[a-d][0-5]+
+/dev/hda4 : hd[a-d][0-5]+
+/dev/hda5 : hd[a-d][0-5]+
+/dev/hdb1 : hd[a-d][0-5]+
+/dev/hdc1 : hd[a-d][0-5]+
--- /dev/null
+fingerprint: eed8ceb8
--- /dev/null
+foo\80bar : \80
+fooÂb : fooÂb
+\80 : \80
--- /dev/null
+foo.bar
+foo\80bar
+fooÂb
+\80
--- /dev/null
+"foo\80bar"
+"fooÂb"
+"\80"
--- /dev/null
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* hack - using unexported internal function */
+#define DEBUG
+#include "regex/parse_rx.c"
+
+#include <stdio.h>
+#include <ctype.h>
+
+static void _pretty_print(struct rx_node *rx, int depth)
+{
+ int i;
+ for (i = 0; i < depth; i++)
+ printf(" ");
+
+ /* display info about the node */
+ switch (rx->type) {
+ case CAT:
+ printf("Cat");
+ break;
+
+ case OR:
+ printf("Or");
+ break;
+
+ case STAR:
+ printf("Star");
+ break;
+
+ case PLUS:
+ printf("Plus");
+ break;
+
+ case QUEST:
+ printf("Quest");
+ break;
+
+ case CHARSET:
+ printf("Charset : ");
+ for (i = 0; i < 256; i++) {
+ if (dm_bit(rx->charset, i) && isprint(i))
+ printf("%c", (char) i);
+ }
+ break;
+
+ default:
+ printf("Unknown type");
+ }
+ printf("\n");
+
+ if (rx->left)
+ _pretty_print(rx->left, depth + 1);
+
+ if (rx->right)
+ _pretty_print(rx->right, depth + 1);
+}
+
+int main(int argc, char **argv)
+{
+ struct dm_pool *mem;
+ struct rx_node *rx;
+ int regex_print = 0;
+ int show_nodes = 0;
+ int regex_arg = 1;
+
+ if (argc == 3 && !strcmp(argv[1], "-r")) {
+ regex_print++;
+ regex_arg++;
+ argc--;
+ }
+
+ if (argc == 3 && !strcmp(argv[1], "-R")) {
+ regex_print++;
+ show_nodes++;
+ regex_arg++;
+ argc--;
+ }
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage : %s [-r] <regex>\n", argv[0]);
+ exit(0);
+ }
+
+ dm_log_init_verbose(_LOG_DEBUG);
+
+ if (!(mem = dm_pool_create("parse_regex", 1024))) {
+ fprintf(stderr, "Couldn't create pool\n");
+ exit(1);
+ }
+
+ if (!(rx = rx_parse_str(mem, argv[regex_arg]))) {
+ dm_pool_destroy(mem);
+ fprintf(stderr, "Couldn't parse regex\n");
+ exit(1);
+ }
+
+ if (regex_print)
+ _regex_print(rx, 0, show_nodes);
+ else
+ _pretty_print(rx, 0);
+
+ dm_pool_destroy(mem);
+
+ return 0;
+}
--- /dev/null
+"(((a?)(([Ub]*)|z))((([qr]|X)+)([Qn]*)))+"
+"[HZejtuw]*"
+"((B|s)*)|(((([Fv]l)(N+))(([el]|C)(tJ)))?)"
+"((([Ma]?)|(t*))*)|((([cm]E)|(M?))|(([BE][EV])|([Qj][Mh])))"
+"(((([bw]*)|([IO]*))((zK)*))|(((pU)|(i|q))|((z?)|([HL]?))))*"
+"((([Pt]?)|[Tr])?)((Hq)*)"
+"[HOXcfgikosvwxz]"
+"[BCEFGHNPTUWfjlprsy]"
+"((((aD)*)|([Xo]+))+)(([HKn](([Eq]|[JQ])(I*)))*)"
+"([LNWYeghv]|e)*"
+"(((y(L*))*)|((([EP]+)(W+))*))*"
+"U*"
+"((((R+)(W|[Qr]))|([py]+))+)([LM]*)"
+"(([DOjx](D(b?)))|([Ke]*))*"
+"((([ls](c|[FT]))*)([JS]*))*"
+"((l?)|(([Gz]+)|(D*)))*"
+"[ABgjn]"
+"(((q|[dg])?)|([Uk]*))((([Fl]?)|([Ry]+))|(([IR]|c)|(T?)))"
+"((([an]|P)|[Jw])((a*)|(m*)))*"
+"((((R[ht])(h+))?)|(([pz](n?))+))+"
+"(((([Dc]b)([Sp][Ii]))|((k|F)*))|[Uiovz])*"
+"[Res]*"
+"[Zl]|a"
+"^[ANZdf]$"
+"[En]|(((Q+)(U+))([pt]*))"
+"[ADEIMQUWXZhklrsvz]"
+"(((S(y*))*)|(j*))*"
+"n*"
+"[NUau]*"
+"((((Z*)(D|[Nd]))|(([np]|B)+))|(([Xy][Fi])*))+"
+"((([EZ]?)|(d[HR]))*)((([Hg]|q)(P+))*)"
+"q"
+"((m*)|(p|B))|((((x?)|(t+))(([Sb][PX])(O|[HM])))+)"
+"((((A*)(z[RS]))*)|(((z+)(Q*))+))*"
+"(((M*)([Uu]*))+)|[Uk]"
+"[imv]"
+"[GLSchtw](([Yw]((F[Dd])|([Tw]+)))?)"
+"([MOZj]*)(S|[Wknr])"
+"((G|q)*)[BHKN]"
+"((((NW)|([Ao]?))|((l|[UV])+))+)|((i|(z*))*)"
+"((((Z+)|([IR]?))|(L*))|([JKQ]+))+"
+"([Bdin](S*))+"
+"[HLNSTp]*"
+"(((J*)([Bq]|[Yu]))*)|([Kv]*)"
+"(((([BJ]|[Zy])(wI))*)(y*))+"
+"(((hF)+)|(H*))*"
+"((([QU][Pj])([GQ]?))+)|[PWo]"
+"(((([cq][BX])?)|((f[DI])*))*)(([GM]*)[SVYr])"
+"(([Zt]*)|((qx)|(([BV]+)(f?))))*"
+"[ILWYhsx]*"
+"(([Uy]*)|[sv])|([NSc]*)"
+"((c*)|([JUfhy]?))+"
+"(((q*)([So]*))(((g[jq])(j?))+))*"
+"((b+)|(((T+)([fw]T))?))*"
+"((([DS]?)|([Th]|u))(Q*))*"
+"[FKLX]|((([fw](L?))(([gq]*)|(O?)))?)"
+"((([HZ]+)u)*)|[APWijn]"
+"(e*)|(((v?)|((J+)(Hb)))?)"
+"(e|((w+)f))*"
+"[BEHKPQVdelnqy]"
+"((((B|N)(s*))|[Rr])(((g?)|([rv]+))+))+"
+"(((s*)|(K*))([AP]G))*"
+"[CELTp]"
+"(([Fq]?)|([Al]+))*"
+"((((r?)|(y[jx]))|([mp]*))+)|((B(S*))*)"
+"((([Eq]+)|(Y[ds]))|(x|(i|[Ku])))[IJNrvy]"
+"((([NO]*)[Ix])+)([Jenq]+)"
+"(((([HP]*)(j|y))*)[Ylqvy])*"
+"[PTv]+"
+"[AINSZhpx]|([EOYZ]*)"
+"([ABCFQv]*)((([Zx]|h)+)|([ej]*))"
+"((([pr]*)|(([Dq]|p)|(H?)))?)([NRUXmoq]*)"
+"(([er]*)|([mx]*))(((nV)([am]?))+)"
+"[BHPRlpu]"
+"(((([Ah]|[tx])|(e|[uy]))?)((([fl]+)([Vz]|v))*))*"
+"[AGdm]"
+"(((K*)^(O*)$)|(B?))*"
+"((([Ks]|[Ka])*)|([FSTab]?))?"
+"(([kw]+)[ei])(([Hy]*)(([Mc]*)|(G|f)))"
+"((((e*)|(Zf))|(R|[nq]))((([Jz]v)([Rj]+))+))*"
+"(((a?)|(e?))(([Uc]*)(S+)))*"
+"((((E+)([MZ]?))+)|(((s|[Az])|z)*))?"
+"((((i[MO])*)|((LH)*))|(((BA)|([AI]+))|[Ug]))*"
+"[EGHILcho]*"
+"(((Z[vw])?)((z|g)+))(((H|U)([iv]Q))|([qw]?))"
+"(([ehmr]|((L[Uw])*))+)((a+)I)"
+"[EKNSWYagj](((v|[TX])|([Uk]+))*)"
+"(((R[Mo])|(O*))|([Fm]|([qw]*)))((m*)|((S|[Ki])?))"
+"((((kP)|c)?)((([do]+)|([Gi]?))*))*"
+"((^(B|W)$|([Ww]+))([no]*))|((([iv]?)|(M*))|((x|L)?))"
+"[AEGPRSbcfhsy]"
+"[Wbcf]|((([MO]?)|([NT]|m))(([Oo]?)([Wg]*)))"
+"(((YZ)*)[PQVei])*"
+"[GJKYt][AEGWdegmnt]"
+"^[CDEGJKNUVYZagkv]$"
+"([DPWbx]*)|(((q|B)|(P|u))((M[Bq])*))"
+"[FHIJRTVYZdiorsuvz]*"
+"([MWoqvz]*)|^(l*)"
+"(((I|[Rx])*)((X[Mf])([Xa]L)))([Ha]|([HY]*))"
+"(((l|[Sd])*)((([Ix]+)|([XY]?))(Z*)))+"